diff options
Diffstat (limited to '')
333 files changed, 57908 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e01d2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +aclocal.m4 +autom4te.cache +compile +config.* +configure +depcomp +INSTALL +install-sh +libtool +ltmain.sh +m4/l*.m4 +Makefile +Makefile.in +missing +*.pc +.deps +*.o +*.la +*.lo +.libs +tags +ChangeLog +libcmis-*.*.*.tar.gz +test-utils +test-mockup +test-atom +test-gdrive +test-onedrive +test-json +test-sharepoint +test-ws +test-factory +test-api +test-c-build +cmis-client +test-server +*.tar.gz +*.1 +*.swp +run-test.sh +*.gcda +*.gcno +*.info +libcmis-lcov +*.pyc +*.trs +*.log +test-driver +qa/libcmis/libtest.a +doc/cmis-client.xml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2698938 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +language: cpp +sudo: required + +addons: + apt: + update: true + packages: + - docbook-to-man + - libboost-date-time-dev + - libboost-dev + - libboost-program-options-dev + - libcppunit-dev + - libxml2-dev + +compiler: + - gcc + - clang +arch: + - amd64 + - ppc64le + +script: + - ./autogen.sh && make && make check + +notifications: + email: + on_success: never + on_failure: always diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py new file mode 100644 index 0000000..f312747 --- /dev/null +++ b/.ycm_extra_conf.py @@ -0,0 +1,144 @@ + +# +# Here's the license text for this file: +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to <http://unlicense.org/> + +import os +import ycm_core + +# These are the compilation flags that will be used in case there's no +# compilation database set (by default, one is not set). +# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. +flags = [ +'-Werror', +'-Wall', +'-pedantic', +'-Weffc++', +'-Wshadow', +'-Wendif-labels', +'-Wextra', +'-Wsign-promo', +'-Woverloaded-virtual', +'-Wnon-virtual-dtor', +'-Wsign-promo', +'-DDATA_DIR="qa/libcmis/data"', +# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which +# language to use when compiling headers. So it will guess. Badly. So C++ +# headers will be compiled as C headers. You don't want that so ALWAYS specify +# a "-std=<something>". +# For a C project, you would set this to something like 'c99' instead of +# 'c++11'. +'-std=c++98', +# ...and the same thing goes for the magic -x option which specifies the +# language that the files to be compiled are written in. This is mostly +# relevant for c++ headers. +# For a C project, you would set this to 'c' instead of 'c++'. +'-x', +'c++', +'-I', +'src/libcmis', +'-I', +'src/libcmis-c', +'-I', +'qa/libcmis', +'-I', +'qa/libcmis-c', +'-I', +'qa/mockup', +] + +# Set this to the absolute path to the folder (NOT the file!) containing the +# compile_commands.json file to use that instead of 'flags'. See here for +# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html +# +# Most projects will NOT need to set this to anything; you can just change the +# 'flags' list of compilation flags. Notice that YCM itself uses that approach. +compilation_database_folder = '' + +if compilation_database_folder: + database = ycm_core.CompilationDatabase( compilation_database_folder ) +else: + database = None + + +def DirectoryOfThisScript(): + return os.path.dirname( os.path.abspath( __file__ ) ) + + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return list( flags ) + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + + +def FlagsForFile( filename ): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = database.GetCompilationInfoForFile( filename ) + final_flags = MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_ ) + + # NOTE: This is just for YouCompleteMe; it's highly likely that your project + # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR + # ycm_extra_conf IF YOU'RE NOT 100% YOU NEED IT. + try: + final_flags.remove( '-stdlib=libc++' ) + except ValueError: + pass + else: + relative_to = DirectoryOfThisScript() + final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) + + return { + 'flags': final_flags, + 'do_cache': True + } @@ -0,0 +1,4 @@ +Cedric Bosdonnat <cedric.bosdonnat@free.fr> +Mihai Varga <mihai.mv13@gmail.com> +Cao Cuong Ngo <cao.cuong.ngo@gmail.com> +David Tardon <dtardon@redhat.com> diff --git a/COPYING.GPL b/COPYING.GPL new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING.GPL @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/COPYING.LGPL b/COPYING.LGPL new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/COPYING.LGPL @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/COPYING.MPL b/COPYING.MPL new file mode 100644 index 0000000..8e6bb3e --- /dev/null +++ b/COPYING.MPL @@ -0,0 +1,470 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + BBC Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by BBC. No one + other than BBC has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "BBC", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + BBC Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the GPL, LGPL, MIT or the alternative licenses, if + any, specified by the Initial Developer in the file described in + Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3c1de92 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,56 @@ +include $(top_srcdir)/coverage.mk + +SUBDIRS = inc src + +if ENABLE_TESTS +SUBDIRS += qa +endif + +ACLOCAL_AMFLAGS = -I m4 + +pkgconfig_DATA = \ + libcmis-@LIBCMIS_API_VERSION@.pc \ + libcmis-c-@LIBCMIS_API_VERSION@.pc +pkgconfigdir = $(libdir)/pkgconfig + +if ENABLE_MAN +cmis-client.1: doc/cmis-client.xml + $(DOCBOOK2MAN) $< + +man_MANS = cmis-client.1 +endif + +EXTRA_DIST = \ + libcmis.pc.in \ + libcmis-c.pc.in \ + COPYING.MPL \ + COPYING.GPL \ + COPYING.LGPL \ + doc/cmis-client.xml.in \ + cppcheck-suppress \ + qa/libcmis/data + +DISTCLEANFILES = \ + cmis-client.1 \ + libcmis-@LIBCMIS_API_VERSION@.pc \ + libcmis-c-@LIBCMIS_API_VERSION@.pc + +dist-hook: + @if test -d "$(srcdir)/.git"; \ + then \ + echo Creating ChangeLog && \ + ( cd "$(top_srcdir)" && \ + echo '# Generated by Makefile. Do not edit.'; echo; \ + $(top_srcdir)/missing --run git log --pretty=medium ) > ChangeLog.tmp \ + && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \ + || ( rm -f ChangeLog.tmp ; \ + echo Failed to generate ChangeLog >&2 ); \ + else \ + echo A git clone is required to generate a ChangeLog >&2; \ + fi + +cppcheck: + @CPPCHECK@ -q --enable=style,performance,portability \ + -j @CPPCHECK_PARALLELISM@ \ + --suppressions-list=@SRCDIR@/cppcheck-suppress \ + --error-exitcode=1 @SRCDIR@ @@ -0,0 +1,108 @@ +0.1.0 + + * Generic + * Anonymous connection + * AtomPub binding + * Get the content hierarchy using the down relations + * Get the content of a document + * Query nodes by ID + +0.2.0 + + * Generic + * Support for authentication in the API + * Callback for providing interactive authentication + * Added API to create documents and folders + * Added API to delete documents and folders + * AtomPub binding + * Handle all the authentication methods that can be handled by + libcurl like Basic, NTLM... + * Decode base64 encoded content sent by SharePoint + * Set the content of a document + * Query nodes by path + * Get the allowable actions for a node + * Object properties can be updated + * Query object types + * Creation of folders and documents + * Deletion of folders and documents + +0.2.1 + + * Fixed documentation distribution + +0.2.2 + + * Fixed soname for the library + * Allow building against libcurl 7.13.1 + +0.2.3 + + * Fixed SharePoint support + * Conditional build of man page + +0.3.0 + + * Added Document::checkOut(), Document::cancelCheckout() and Document::checkIn() + * Added Object::move( ) -- Grau Data + * Fixes for xcmis and cloudoku.com -- Grau Data + * Added Document::getAllVersions( ) + * WebService binding implementation + * Session factory automatically detects which binding to use + * C wrapper API + * Unit tests are now split between quick ones and the ones needing a CMIS server + +0.4.0 + + * Support for Google Drive protocol as a binding + * Support for Alfresco in the cloud + * Added OAuth2 authentication support + * Added API to configure HTTP proxy + * Handle invalid SSL certificate problems + * Added API for renditions + * Moved the CMIS Atom binding unit tests to use libcurl mockup + * Added repository capabilities support (still missing + capabilityCreatablePropertyTypes and capabilityNewTypeSettableAttributes) + +0.5.0 + + * Completely removed the dependency on InMemory server for unit tests + * Minimized the number of HTTP requests sent by SessionFactory::createSession + * Added Session::getBaseTypes() + +0.5.1 + + * Fixed a crash when server response did not contain cmis:baseTypeId + property (tdf#90351) + * Removed the requirement for non-empty password when using HTTP + authentication credentials + * Fixed build with boost 1.60 and gcc 5 + * Fixed a few problems found by Coverity + * Fixed a busload of memory leaks + +0.5.2 + + * Fixed Google Drive login, broken by Google's new 2-page login sequence + * Added support for Google Drive two-factor authentication + * Fixed access to SharePoint root folder (tdf#101385) + * Limited the maximal number of redirections to 20 (rhbz#1410197) + * Switched library implementation to C++11 (the API remains + C++98-compatible) + * Fixed build with boost >= 1.68.0 (#19) + * Fixed encoding of OAuth2 credentials + * Dropped cppcheck run from "make check". A new "make cppcheck" target + was created for it + * Added proper API symbol exporting + * Speeded up building of tests a bit + * Fixed a few issues found by coverity and cppcheck + +0.6.0 + + * Merged outstanding LibreOffice, etc. modifications + +0.6.1 + + * add a callback that can be used to configure libcurl + +0.6.2 + + * fix up version-info diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e8e4e3 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +[![Build Status](https://travis-ci.org/tdf/libcmis.svg?branch=master)](https://travis-ci.org/tdf/libcmis) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/tdf/libcmis.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tdf/libcmis/alerts/) +[![Coverity Scan](https://scan.coverity.com/projects/17516/badge.svg)](https://scan.coverity.com/projects/tdf-libcmis) + +Objective +--------- + +Libcmis aims at providing a C/C++ client library for the CMIS protocol. +A cmis-client tool is maintained to help testing and showing libcmis features. + +Dependencies +------------ + + * libxml2 + * libcurl + * boost diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..84165ae --- /dev/null +++ b/autogen.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +touch ChangeLog + +if [ ! -e ltmain.sh ]; then + libtoolize +fi + +aclocal -I m4 +automake -a -c --foreign +autoconf +test x$NOCONFIGURE = x && ./configure $@ diff --git a/cmis-test.sh b/cmis-test.sh new file mode 100644 index 0000000..de315ab --- /dev/null +++ b/cmis-test.sh @@ -0,0 +1,139 @@ +#!/bin/sh +# libcmis +# Version: MPL 1.1 / GPLv2+ / LGPLv2+ +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License or as specified alternatively below. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# Major Contributor(s): +# Copyright (C) 2012 SUSE <cbosdonnat@suse.com> +# +# +# All Rights Reserved. +# +# For minor contributions see the git repository. +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPLv2+"), or +# the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), +# in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable +# instead of those above. + +while [[ $# > 1 ]] +do + key="$1" + shift + + case $key in + --url) + BINDING_URL="$1" + shift;; + -u) + USER="$1" + shift;; + -p) + PASS="$1" + shift;; + -r) + REPO="$1" + shift;; + --oauth2-client-id) + OAUTH2_CLIENT_ID="$1" + shift;; + --oauth2-client-secret) + OAUTH2_CLIENT_SECRET="$1" + shift;; + --oauth2-scope) + OAUTH2_SCOPE="$1" + shift;; + --oauth2-auth-url) + OAUTH2_AUTH_URL="$1" + shift;; + --oauth2-token-url) + OAUTH2_TOKEN_URL="$1" + shift;; + --oauth2-redirect-uri) + OAUTH2_REDIRECT_URI="$1" + shift;; + --base-folder) + BASE_FOLDER="$1" + shift;; + *) + ;; + esac +done + +function cmis_client ( ) +{ + repo_opt= + if test "z$REPO" != "z"; then + repo_opt=" -r \"$REPO\"" + fi + + args="--url "$BINDING_URL" -u "$USER" -p "$PASS"$repo_opt" + if test "z$OAUTH2_CLIENT_ID" != "z"; then + args="$args --oauth2-client-id "$OAUTH2_CLIENT_ID" + --oauth2-client-secret "$OAUTH2_CLIENT_SECRET" + --oauth2-scope "$OAUTH2_SCOPE" + --oauth2-auth-url "$OAUTH2_AUTH_URL" + --oauth2-token-url "$OAUTH2_TOKEN_URL" + --oauth2-redirect-uri "$OAUTH2_REDIRECT_URI"" + fi + + args="$args "$@"" + src/cmis-client $args +} + +function get_versionable_type ( ) +{ + versionable_type= + test_type=$1 + + # Is test_type versionable? + versionable=`cmis_client type-by-id $test_type | grep ^Versionable: | cut -d ' ' -f 2` + if test "z$versionable" == "z1"; then + versionable_type=$test_type + else + # Otherwise, loop over its children + children=`cmis_client type-by-id $test_type | sed -n -e '/Children type/,/^[^ ]/ p' | grep -e '^\ ' | tr '()' '\t' | cut -f 2` + for type_id in $children; do + versionable_type=`get_versionable_type $type_id` + if test "z$versionable_type" != "z"; then + break + fi + done + fi + + echo -n $versionable_type +} + +#First get the Root Id +ROOT_ID=`cmis_client show-root | grep '^Id:' | cut -d ' ' -f 2` + +# Create a test folder +test_folder_name=$BASE_FOLDER"/test-$$" +test_folder_id=`cmis_client create-folder $ROOT_ID $test_folder_name | grep '^Id:' | cut -d ' ' -f 2` + +# Get a Versionable document type, not alway cmis:document for all servers +versionable_type=`get_versionable_type "cmis:document"` + +# Create a versionable document +file_path=NEWS +file_mime=`file --mime-type $file_path | cut -d ' ' -f 2` +doc1_id=`cmis_client --object-type $versionable_type --input-file $file_path --input-type $file_mime create-document $test_folder_id doc_1 | grep '^Id:' | cut -d ' ' -f 2` + +# Checkout the document +doc1_pwc=`cmis_client checkout $doc1_id | grep '^Id:' | cut -d ' ' -f 2` + +# TODO Checkin the document +doc1_checkIn=`cmis_client --input-file $file_path --input-type $file_mime --message checkin_message checkin $doc1_id | grep '^Id:' | cut -d ' ' -f 2` + +# Cleanup the test folder to remove all traces of the tests +cmis_client delete $test_folder_id diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..218cf9f --- /dev/null +++ b/configure.ac @@ -0,0 +1,224 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +m4_define([libcmis_major_version], [0]) +m4_define([libcmis_minor_version], [6]) +m4_define([libcmis_micro_version], [2]) +m4_define([libcmis_api_version], [libcmis_major_version.libcmis_minor_version]) +m4_define([libcmis_version],[libcmis_api_version.libcmis_micro_version]) + + +AC_PREREQ([2.63]) +AC_INIT([libcmis], [libcmis_version]) +AM_INIT_AUTOMAKE([1.10 foreign dist-xz]) +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PROG_LIBTOOL +LT_INIT([win32-dll disable-static pic-only]) +AC_CANONICAL_HOST + +AX_CXX_COMPILE_STDCXX_11 + +# Check the options +AC_ARG_ENABLE(client, + AC_HELP_STRING([--disable-client],[Compile only the library, not the client tool]), + enable_client=$enableval, enable_client=yes) +AM_CONDITIONAL(ENABLE_CLIENT, test "x$enable_client" != "xno") + +AC_ARG_ENABLE([tests], + [AS_HELP_STRING([--disable-tests], [Do not build the unit tests])], + [enable_tests="$enableval"], + [enable_tests=yes] +) +AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" != "xno") + +AC_ARG_ENABLE([coverage], + [AS_HELP_STRING([--enable-coverage], [Extract test coverage data])], + [enable_coverage="$enableval"], + [enable_coverage=no] +) +AM_CONDITIONAL(ENABLE_COVERAGE, test "x$enable_coverage" != "xno") + +AS_IF([test "x$enable_coverage" != "xno"], [ + enable_tests=yes + + # Make sure we have gcc + if test "x$GCC" != "xyes"; then + AC_MSG_ERROR([GCC is required for --enable-coverage]) + fi + + AC_CHECK_PROG(LCOV, lcov, lcov) + AC_CHECK_PROG(GENHTML, genhtml, genhtml) + + AS_IF([test "x$ac_cv_prog_LCOV" = "x"], [ + AC_MSG_ERROR([lcov is required for --enable-coverage]) + ]) + AS_IF([test "x$ac_cv_prog_GENHTML" = "x"], [ + AC_MSG_ERROR([genhtml is required for --enable-coverage]) + ]) + + CFLAGS="-g -O0 --coverage" + CXXFLAGS="-g -O0 --coverage" + LDFLAGS+="--coverage" +]) + + +LIBCMIS_API_VERSION=libcmis_api_version +AC_SUBST(LIBCMIS_API_VERSION) + +AC_CONFIG_MACRO_DIR([m4]) + +for top_builddir in . .. ../.. $ac_auxdir $ac_auxdir/..; do + test -f $top_builddir/configure && break +done + +SRCDIR=$srcdir +AC_SUBST(SRCDIR) + +AC_LANG([C++]) + +# ========================== +# Platform check for windows +# ========================== +AC_MSG_CHECKING([for native Win32]) +AS_CASE([$host], [*-*-mingw*], [native_win32=yes], [native_win32=no]) +AC_MSG_RESULT([$native_win32]) +AM_CONDITIONAL(OS_WIN32, [test "x$native_win32" = "xyes"]) + +# ==================== +# Check for visibility +# ==================== +AC_MSG_CHECKING([for -fvisibility=hidden compiler flag]) +saved_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS -fvisibility=hidden" +AC_TRY_COMPILE([], [], [have_visibility=yes], [have_visibility=no]) +AC_MSG_RESULT([$have_visibility]) +CPPFLAGS="$saved_CPPFLAGS" +AX_GCC_FUNC_ATTRIBUTE([visibility]) +AM_CONDITIONAL([ENABLE_VISIBILITY], [ + test "x$have_visibility" = "xyes" && test "x$ax_cv_have_func_attribute_visibility" = "xyes"]) + +# ============= +# Documentation +# ============= +AC_ARG_WITH(man, + [AS_HELP_STRING([--without-man], [Do not build manpage])], + [with_man="$withval"], + [with_man=yes] +) +AS_IF([test "x$with_man" != "xno"], [ + build_man=yes + AS_IF([test -z "$DOCBOOK2MAN"], [ + AC_PATH_PROGS([DOCBOOK2MAN], [docbook2x-man docbook-to-man docbook2man.pl docbook2man]) + AS_IF([test -z "$DOCBOOK2MAN"], [ + AC_MSG_ERROR([docbook-to-man is missing. Install docbook2X package.]) + ]) + ]) +], [build_man=no]) +AC_SUBST(DOCBOOK2MAN) +AM_CONDITIONAL([ENABLE_MAN], [test "x$build_man" != "xno"]) + +# ======== +# Cppcheck +# ======== +AC_PATH_PROG(CPPCHECK,[cppcheck],[]) +AS_IF([test "x$ac_cv_path_CPPCHECK" != "x"], [enable_cppcheck=yes], [enable_cppcheck=no]) +AC_SUBST(CPPCHECK) +AC_PATH_PROG([NPROC], [nproc], []) +AS_IF([test "x$NPROC" != "x"], [CPPCHECK_PARALLELISM=`$NPROC`], [CPPCHECK_PARALLELISM=1]) +AC_SUBST([CPPCHECK_PARALLELISM]) + +AC_ARG_ENABLE([werror], + [AS_HELP_STRING([--disable-werror], [Treat all warnings as errors, usefull for development])], + [enable_werror="$enableval"], + [enable_werror=yes] +) +AS_IF([test x"$enable_werror" != "xno"], [ + CFLAGS="$CFLAGS -Werror" + CXXFLAGS="$CXXFLAGS -Werror" +]) +AS_IF([test x"$GCC" = xyes], [ + # Be tough with warnings and produce less careless code + CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wendif-labels -Wextra" + CXXFLAGS="$CXXFLAGS -Wall -pedantic -Weffc++ -Wshadow -Wendif-labels -Wextra -Wsign-promo -Woverloaded-virtual -Wnon-virtual-dtor -Wsign-promo" +]) + +# Check for curl +PKG_CHECK_MODULES(CURL, [libcurl]) +AC_SUBST(CURL_CFLAGS) +AC_SUBST(CURL_LIBS) + +# Check for lixml2 +PKG_CHECK_MODULES(XML2, [libxml-2.0]) +AC_SUBST(XML2_CFLAGS) +AC_SUBST(XML2_LIBS) + +# Check for cppunit +AS_IF([test "x$enable_tests" != "xno"], [ + PKG_CHECK_MODULES(CPPUNIT, cppunit >= 1.12 ) + AC_SUBST(CPPUNIT_CFLAGS) + AC_SUBST(CPPUNIT_LIBS) +]) + +# Check for boost +m4_pattern_allow([^BOOST_]) + +BOOST_REQUIRE([1.36]) +BOOST_DATE_TIME +BOOST_SMART_PTR +BOOST_STRING_ALGO +BOOST_UUID + +AS_IF([test "x$enable_client" != "xno"], [ + BOOST_PROGRAM_OPTIONS +]) +AC_SUBST(BOOST_CPPFLAGS) + +# Checks for header files. +AC_CHECK_HEADERS([stdlib.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_INLINE +AC_TYPE_MODE_T +AC_TYPE_SIZE_T + +# Checks for library functions. + +AC_CONFIG_FILES([ + Makefile + doc/cmis-client.xml + inc/Makefile + inc/libcmis-c/Makefile + inc/libcmis/Makefile + libcmis-c-$LIBCMIS_API_VERSION.pc:libcmis-c.pc.in + libcmis-$LIBCMIS_API_VERSION.pc:libcmis.pc.in + qa/Makefile + qa/libcmis-c/Makefile + qa/libcmis/Makefile + qa/mockup/Makefile + src/Makefile + src/libcmis-c/Makefile + src/libcmis/Makefile +]) +AC_OUTPUT + +AC_MSG_NOTICE([ + +libcmis $VERSION: + Prefix: ${prefix} + Compiler: ${CXX} + Compiler flags: ${CXXFLAGS} + client: ${enable_client} + werror: ${enable_werror} + tests: ${enable_tests} + test coverage: ${enable_coverage} + mans: ${build_man} +]) diff --git a/coverage.mk b/coverage.mk new file mode 100644 index 0000000..01cbadd --- /dev/null +++ b/coverage.mk @@ -0,0 +1,22 @@ +.PHONY: coverage genlcov coverage-clean + +coverage: + -$(MAKE) $(AM_MAKEFLAGS) -k check + $(MAKE) $(AM_MAKEFLAGS) genlcov + +infos = libcmis-lcov.info libcmis-c-lcov.info +$(infos): %-lcov.info: + $(LCOV) --directory $(top_builddir)/src/$* \ + --base-directory $(top_builddir)/src/$* \ + --capture \ + --output-file $@ \ + --no-external \ + --compat-libtool +genlcov: $(infos) + LANG=C $(GENHTML) --output-directory libcmis-lcov --title "Libcmis Code Coverage" --legend --show-details $^ + @echo "file://$(abs_top_builddir)/libcmis-lcov/index.html" + +coverage-clean: + -$(LCOV) --directory $(top_builddir) -z + -rm -rf $(infos) libcmis-lcov + -find $(top_builddir) -type f -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" -exec rm "{}" \; diff --git a/cppcheck-suppress b/cppcheck-suppress new file mode 100644 index 0000000..9ef8482 --- /dev/null +++ b/cppcheck-suppress @@ -0,0 +1,3 @@ +publicAllocationError:qa/libcmis/test-decoder.cxx +noExplicitConstructor +noExplicitCopyMoveConstructor diff --git a/doc/cmis-client.xml.in b/doc/cmis-client.xml.in new file mode 100644 index 0000000..a1b8c9f --- /dev/null +++ b/doc/cmis-client.xml.in @@ -0,0 +1,570 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"> +<refentry> + <refentryinfo> + <date>2018-12-23</date> + </refentryinfo> + <refmeta> + <refentrytitle>cmis-client</refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo>@PACKAGE_STRING@</refmiscinfo> + </refmeta> + <refnamediv> + <refname> + <application>cmis-client</application> + </refname> + <refpurpose> + command line CMIS client tool. + </refpurpose> + </refnamediv> + <refsynopsisdiv> + <refsynopsisdivinfo> + <date>2012-01-27</date> + </refsynopsisdivinfo> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="plain">help</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">list-repos</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="plain">repo-infos</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="plain">show-root</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="plain">get-content <replaceable>id</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="plain">--input-file <replaceable>path/to/file</replaceable></arg> + <arg choice="plain">--input-type <replaceable>mime/type</replaceable></arg> + <arg choice="opt">--input-name <replaceable>name.ext</replaceable></arg> + <arg choice="plain">set-content <replaceable>id</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="opt">--object-type <replaceable>some:cmistype</replaceable></arg> + <arg choice="opt" rep="repeat">--object-property <replaceable>prop-id=prop-value</replaceable></arg> + <arg choice="plain">create-folder <replaceable>parent-id name</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="plain">--input-file <replaceable>path/to/file</replaceable></arg> + <arg choice="plain">--input-type <replaceable>mime/type</replaceable></arg> + <arg choice="opt">--input-name <replaceable>name.ext</replaceable></arg> + <arg choice="opt">--object-type <replaceable>some:cmistype</replaceable></arg> + <arg choice="opt" rep="repeat">--object-property <replaceable>prop-id=prop-value</replaceable></arg> + <arg choice="plain">create-document <replaceable>parent-id name</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="opt" rep="repeat">--object-property <replaceable>prop-id=prop-value</replaceable></arg> + <arg choice="plain">update-object <replaceable>object-id</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <group choice="plain"> + <arg>type-by-id</arg> + <arg>show-by-id</arg> + <arg>show-by-path</arg> + <arg>delete</arg> + </group> + <arg rep="repeat" choice="plain"> + <replaceable>arg</replaceable> + </arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <group choice="plain"> + <arg>checkout</arg> + <arg>cancel-checkout</arg> + <arg>get-versions</arg> + </group> + <arg choice="plain"> + <replaceable>arg</replaceable> + </arg> + </cmdsynopsis> + <cmdsynopsis> + <command>cmis-client</command> + <arg choice="opt">-v</arg> + <arg choice="opt">-u <replaceable>login</replaceable></arg> + <arg choice="opt">-p <replaceable>secret</replaceable></arg> + <arg choice="plain">--url <replaceable>url://to/binding</replaceable></arg> + <arg choice="plain">-r <replaceable>repo-id</replaceable></arg> + <arg choice="opt">--input-file <replaceable>path/to/file</replaceable></arg> + <arg choice="opt">--input-type <replaceable>mime/type</replaceable></arg> + <arg choice="opt">--input-name <replaceable>name.ext</replaceable></arg> + <arg choice="opt" rep="repeat">--object-property <replaceable>prop-id=prop-value</replaceable></arg> + <arg choice="opt">--major</arg> + <arg choice="opt">--message</arg> + <arg choice="plain">checkin <replaceable>pwc id</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + <refsect1> + <refsect1info> + <date>2012-01-27</date> + </refsect1info> + <title>DESCRIPTION</title> + <para> + The <command>cmis-client</command> tool sends queries over the net to a CMIS-enabled server + to access or modify its content. It is originally demonstrating what libcmis is capable of. + </para> + </refsect1> + <refsect1> + <title>OPTIONS</title> + <refsect2> + <title>GLOBAL OPTIONS</title> + <variablelist> + <varlistentry> + <term>-v, --verbose</term> + <listitem> + <para> + Shows a lot of information to monitor what is happening behind the scene. + This helps a lot to debug libcmis. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--help</term> + <listitem> + <para> + Show the help and exit. This is equivalent to use the help command. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--url <replaceable class="parameter">url://to/binding</replaceable></term> + <listitem> + <para><replaceable class="parameter">url://to/binding</replaceable> needs to point to the service document of + either AtomPub or WebService binding. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>-r,--repository <replaceable class="parameter">repo-id</replaceable></term> + <listitem> + <para> + Operate on the <replaceable class="parameter">repo-id</replaceable> CMIS repository. + If there is only one repository on the server, this parameter is not needed and that + repository will be automatically selected. Use this parameter if there are several + repositories on the server. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>-u,--username <replaceable class="parameter">login</replaceable></term> + <listitem> + <para> + Connect as <replaceable class="parameter">login</replaceable> to the CMIS server. + If not provided connect anonymously. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>-p,--password <replaceable class="parameter">secret</replaceable></term> + <listitem> + <para> + Use <replaceable class="parameter">secret</replaceable> to authenticate on the CMIS server. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--no-ssl-check</term> + <listitem> + <para> + Disables the SSL certificate verifications. Lowers the security, but may be handy + to work around bad certificates like expired or self-signed ones. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--proxy <replaceable class="parameter">url</replaceable></term> + <listitem> + <para> + Use <replaceable class="parameter">url</replaceable> as the HTTP proxy. + Setting this value will override the system proxy settings. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--proxy-username <replaceable class="parameter">login</replaceable></term> + <listitem> + <para> + Use <replaceable class="parameter">login</replaceable> to authenticate on the HTTP proxy. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--proxy-password <replaceable class="parameter">secret</replaceable></term> + <listitem> + <para> + Use <replaceable class="parameter">secret</replaceable> to authenticate on the HTTP proxy. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--noproxy <replaceable class="parameter">list</replaceable></term> + <listitem> + <para> + Proxy settings won't apply to hostnames and domain names listed + in <replaceable class="parameter">list</replaceable>. + This value is a coma separated list. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--oauth2-client-id <replaceable class="parameter">client_id</replaceable></term> + <listitem> + <para> + Application client id to use in the OAuth2 authentication flow. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--oauth2-client-secret <replaceable class="parameter">client_secret</replaceable></term> + <listitem> + <para> + Application client secret to use in the OAuth2 authentication flow. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--oauth2-auth-url <replaceable class="parameter">url</replaceable></term> + <listitem> + <para> + URL to authenticate the user in the OAuth2 authentication flow. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--oauth2-token-url <replaceable class="parameter">url</replaceable></term> + <listitem> + <para> + URL to authenticate the application in the OAuth2 authentication flow. + The access and refresh tokens are provided by this URL. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--oauth2-redirect-uri <replaceable class="parameter">uri</replaceable></term> + <listitem> + <para> + URI where the OAuth2 authentication flow will redirect after a sucessful + authentication. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--oauth2-scope <replaceable class="parameter">scope</replaceable></term> + <listitem> + <para> + Requested scope to access in the OAuth2 authentication flow. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect2> + <refsect2> + <title>MODIFICATION OPERATIONS OPTIONS</title> + <variablelist> + <varlistentry> + <term>--input-file <replaceable class="parameter">path/to/file</replaceable></term> + <listitem> + <para> + Upload <replaceable class="parameter">path/to/file</replaceable> as the new content stream + of the object. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--input-type <replaceable class="parameter">mime/type</replaceable></term> + <listitem> + <para> + Set the mime type of the new content stream of the object to <replaceable class="parameter">mime/type</replaceable>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--input-name <replaceable class="parameter">name.ext</replaceable></term> + <listitem> + <para> + Set the remote content stream filename of the new content stream of the object to <replaceable class="parameter">name.ext</replaceable>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--object-type <replaceable class="parameter">some:cmistype</replaceable></term> + <listitem> + <para> + Set the object type of the CMIS object to be created to <replaceable class="parameter">some:cmistype</replaceable>. + This is the equivalent of --object-property cmis:objectTypeId=some:cmistype. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--object-property <replaceable class="parameter">prop-id=prop-value</replaceable></term> + <listitem> + <para> + Set a property to be updated or added to the CMIS object. <replaceable class="parameter">prop-id</replaceable> is + the property definition id and <replaceable class="parameter">prop-value</replaceable> is the value to set on it. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>--major</term> + <listitem> + <para> + Create a major version when performing a checkin. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>-m, --message <replaceable class="parameter">message</replaceable></term> + <listitem> + <para> + Set the checking message. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect2> + <refsect2> + <title>COMMANDS</title> + <variablelist> + <varlistentry> + <term>help</term> + <listitem> + <para> + Show the help and exit. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>list-repos</term> + <listitem> + <para> + List the repositories available on the server. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>repo-infos</term> + <listitem> + <para> + Displays the informations and capabilities of the selected repository + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>show-root</term> + <listitem> + <para> + Displays the root node infos and children. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>get-content <replaceable class="parameter">id</replaceable></term> + <listitem> + <para> + Download the content of the CMIS object corresponding to + <replaceable class="parameter">id</replaceable> in the current directory. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>set-content <replaceable class="parameter">id</replaceable></term> + <listitem> + <para> + Upload a file as the content stream of the CMIS object corresponding to + <replaceable class="parameter">id</replaceable>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>create-folder <replaceable class="parameter">parent-id name</replaceable></term> + <listitem> + <para> + Create a sub folder in folder <replaceable class="parameter">parent-id</replaceable> + named <replaceable class="parameter">name</replaceable>. The default type of the folder + to create is cmis:folder, but this can be changed using --object-type option. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>create-document <replaceable class="parameter">parent-id name</replaceable></term> + <listitem> + <para> + Create a document in folder <replaceable class="parameter">parent-id</replaceable> + named <replaceable class="parameter">name</replaceable>. The default type of the document + to create is cmis:document, but this can be changed using --object-type option. + </para> + <para> + Note that the --input-file and --input-type may be mandatory, depending on the type of + the document to create and its constraints. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>update-object <replaceable class="parameter">object-id</replaceable></term> + <listitem> + <para> + Replace the writeable properties given with --object-property option on the object + matching id <replaceable class="parameter">object-id</replaceable>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>type-by-id <replaceable class="parameter">arg...</replaceable></term> + <listitem> + <para> + Displays the infos and children (if any) of all the CMIS types corresponding + to the listed ids. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>show-by-id <replaceable class="parameter">arg...</replaceable></term> + <listitem> + <para> + Displays the infos and children (if any) of all the CMIS objects corresponding + to the listed ids. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>show-by-path <replaceable class="parameter">arg...</replaceable></term> + <listitem> + <para> + Displays the infos and children (if any) of all the CMIS objects corresponding + to the listed paths. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>delete <replaceable class="parameter">arg...</replaceable></term> + <listitem> + <para> + Deletes the CMIS objects corresponding to the listed ids. If the node + is a folder, its content will be removed as well. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>checkout <replaceable class="parameter">arg</replaceable></term> + <listitem> + <para> + Checkout the document corresponding to the provided id and display + the infos of the created private working copy. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>cancel-checkout <replaceable class="parameter">arg</replaceable></term> + <listitem> + <para> + Cancel the Private Working Copy corresponding to the node id. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>get-versions <replaceable class="parameter">arg</replaceable></term> + <listitem> + <para> + Display the versions (if any) of all the CMIS object corresponding + to the provided id. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>checkin <replaceable class="parameter">arg</replaceable></term> + <listitem> + <para> + Check in the private working copy corresponding to the provided id and display + the infos of the resulting document. Use the --major and --message options to + define the version to create and the commit to associate to it. + + Note that repositories without the ability to update the private working copies + will need the --input-file, --input-type and --object-property options. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect2> + </refsect1> + <refsect1> + <title>USAGE</title> + <para>Displays the root node of repository A1:</para> + <para><command>cmis-client</command> -r A1 --url http://localhost/atom show-root</para> + <para>Displays the nodes with id 133 and 116 of repository A1:</para> + <para><command>cmis-client</command> -r A1 --url http://localhost/atom show-by id 133 116</para> + </refsect1> + <refsect1> + <title>AUTHOR</title> + <para> + <author> + <firstname>Cédric</firstname> + <surname>Bosdonnat</surname> + <contrib>Original author</contrib> + </author> + </para> + </refsect1> + <refsect1> + <title>REPORTING BUGS</title> + <para>Report bugs to <<ulink url="https://github.com/tdf/libcmis/issues">https://github.com/tdf/libcmis/issues</ulink>>.</para> + </refsect1> +</refentry> diff --git a/inc/Makefile.am b/inc/Makefile.am new file mode 100644 index 0000000..f088d50 --- /dev/null +++ b/inc/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = libcmis libcmis-c diff --git a/inc/libcmis-c/Makefile.am b/inc/libcmis-c/Makefile.am new file mode 100644 index 0000000..eefbde2 --- /dev/null +++ b/inc/libcmis-c/Makefile.am @@ -0,0 +1,20 @@ +libcmis_cdir = $(includedir)/libcmis-c-@LIBCMIS_API_VERSION@/libcmis-c + +dist_libcmis_c_HEADERS = \ + allowable-actions.h \ + document.h \ + error.h \ + folder.h \ + libcmis-c-api.h \ + libcmis-c.h \ + oauth2-data.h \ + object-type.h \ + object.h \ + property-type.h \ + property.h \ + rendition.h \ + repository.h \ + session-factory.h \ + session.h \ + types.h \ + vectors.h diff --git a/inc/libcmis-c/allowable-actions.h b/inc/libcmis-c/allowable-actions.h new file mode 100644 index 0000000..c2764d3 --- /dev/null +++ b/inc/libcmis-c/allowable-actions.h @@ -0,0 +1,50 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_ALLOWABLE_ACTIONS_H_ +#define _LIBCMIS_ALLOWABLE_ACTIONS_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_allowable_actions_free( libcmis_AllowableActionsPtr allowable ); + +LIBCMIS_C_API bool libcmis_allowable_actions_isAllowed( libcmis_AllowableActionsPtr allowable, + libcmis_allowable_actions_Type action ); + +LIBCMIS_C_API bool libcmis_allowable_actions_isDefined( libcmis_AllowableActionsPtr allowable, + libcmis_allowable_actions_Type action ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/document.h b/inc/libcmis-c/document.h new file mode 100644 index 0000000..5436956 --- /dev/null +++ b/inc/libcmis-c/document.h @@ -0,0 +1,96 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_DOCUMENT_H_ +#define _LIBCMIS_DOCUMENT_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_vector_document_free( libcmis_vector_document_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_document_size( libcmis_vector_document_Ptr vector ); +LIBCMIS_C_API libcmis_DocumentPtr libcmis_vector_document_get( libcmis_vector_document_Ptr vector, size_t i ); + +LIBCMIS_C_API bool libcmis_is_document( libcmis_ObjectPtr object ); +LIBCMIS_C_API libcmis_DocumentPtr libcmis_document_cast( libcmis_ObjectPtr object ); + +LIBCMIS_C_API void libcmis_document_free( libcmis_DocumentPtr document ); + +LIBCMIS_C_API libcmis_vector_folder_Ptr libcmis_document_getParents( libcmis_DocumentPtr document, libcmis_ErrorPtr error ); + +LIBCMIS_C_API void libcmis_document_getContentStream( + libcmis_DocumentPtr document, + libcmis_writeFn writeFn, + void* userData, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API void libcmis_document_setContentStream( + libcmis_DocumentPtr document, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* filename, + bool overwrite, + libcmis_ErrorPtr ); + +/** The resulting value needs to be free'd + */ +LIBCMIS_C_API char* libcmis_document_getContentType( libcmis_DocumentPtr document ); + +/** The resulting value needs to be free'd + */ +LIBCMIS_C_API char* libcmis_document_getContentFilename( libcmis_DocumentPtr document ); + +LIBCMIS_C_API long libcmis_document_getContentLength( libcmis_DocumentPtr document ); + +LIBCMIS_C_API libcmis_DocumentPtr libcmis_document_checkOut( libcmis_DocumentPtr document, libcmis_ErrorPtr error ); +LIBCMIS_C_API void libcmis_document_cancelCheckout( libcmis_DocumentPtr document, libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_DocumentPtr libcmis_document_checkIn( + libcmis_DocumentPtr document, + bool isMajor, + const char* comment, + libcmis_vector_property_Ptr properties, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* filename, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_vector_document_Ptr libcmis_document_getAllVersions( + libcmis_DocumentPtr document, + libcmis_ErrorPtr error ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/error.h b/inc/libcmis-c/error.h new file mode 100644 index 0000000..8a63bfa --- /dev/null +++ b/inc/libcmis-c/error.h @@ -0,0 +1,48 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_ERROR_H_ +#define _LIBCMIS_ERROR_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API libcmis_ErrorPtr libcmis_error_create( ); +LIBCMIS_C_API void libcmis_error_free( libcmis_ErrorPtr e ); + +LIBCMIS_C_API const char* libcmis_error_getMessage( libcmis_ErrorPtr e ); +LIBCMIS_C_API const char* libcmis_error_getType( libcmis_ErrorPtr e ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/folder.h b/inc/libcmis-c/folder.h new file mode 100644 index 0000000..e741e71 --- /dev/null +++ b/inc/libcmis-c/folder.h @@ -0,0 +1,81 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_FOLDER_H_ +#define _LIBCMIS_FOLDER_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_vector_folder_free( libcmis_vector_folder_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_folder_size( libcmis_vector_folder_Ptr vector ); +LIBCMIS_C_API libcmis_FolderPtr libcmis_vector_folder_get( libcmis_vector_folder_Ptr vector, size_t i ); + + +LIBCMIS_C_API bool libcmis_is_folder( libcmis_ObjectPtr object ); +LIBCMIS_C_API libcmis_FolderPtr libcmis_folder_cast( libcmis_ObjectPtr object ); + +LIBCMIS_C_API void libcmis_folder_free( libcmis_FolderPtr folder ); + +LIBCMIS_C_API libcmis_FolderPtr libcmis_folder_getParent( libcmis_FolderPtr folder, libcmis_ErrorPtr error ); +LIBCMIS_C_API libcmis_vector_object_Ptr libcmis_folder_getChildren( libcmis_FolderPtr folder, libcmis_ErrorPtr error ); + +/** Get the path of the folder. The returned string needs to be freed. + */ +LIBCMIS_C_API char* libcmis_folder_getPath( libcmis_FolderPtr folder ); + +LIBCMIS_C_API bool libcmis_folder_isRootFolder( libcmis_FolderPtr folder ); + +LIBCMIS_C_API libcmis_FolderPtr libcmis_folder_createFolder( + libcmis_FolderPtr folder, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_DocumentPtr libcmis_folder_createDocument( + libcmis_FolderPtr folder, + libcmis_vector_property_Ptr properties, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* filename, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_vector_string_Ptr libcmis_folder_removeTree( libcmis_FolderPtr folder, + bool allVersion, + libcmis_folder_UnfileObjects unfile, + bool continueOnError, + libcmis_ErrorPtr error ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/libcmis-c-api.h b/inc/libcmis-c/libcmis-c-api.h new file mode 100644 index 0000000..3fe7986 --- /dev/null +++ b/inc/libcmis-c/libcmis-c-api.h @@ -0,0 +1,45 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_C_API_H_ +#define _LIBCMIS_C_API_H_ + +#ifdef DLL_EXPORT +#ifdef LIBCMIS_C_BUILD +#define LIBCMIS_C_API __declspec(dllexport) +#else +#define LIBCMIS_C_API __declspec(dllimport) +#endif +#else /* !DLL_EXPORT */ +#ifdef LIBCMIS_C_VISIBILITY +#define LIBCMIS_C_API __attribute__((visibility("default"))) +#else +#define LIBCMIS_C_API +#endif +#endif + +#endif diff --git a/inc/libcmis-c/libcmis-c.h b/inc/libcmis-c/libcmis-c.h new file mode 100644 index 0000000..d9f6e4e --- /dev/null +++ b/inc/libcmis-c/libcmis-c.h @@ -0,0 +1,50 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_C_H_ +#define _LIBCMIS_C_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#include "libcmis-c/allowable-actions.h" +#include "libcmis-c/document.h" +#include "libcmis-c/error.h" +#include "libcmis-c/folder.h" +#include "libcmis-c/object.h" +#include "libcmis-c/object-type.h" +#include "libcmis-c/property.h" +#include "libcmis-c/property-type.h" +#include "libcmis-c/oauth2-data.h" +#include "libcmis-c/rendition.h" +#include "libcmis-c/repository.h" +#include "libcmis-c/types.h" +#include "libcmis-c/session.h" +#include "libcmis-c/session-factory.h" +#include "libcmis-c/vectors.h" + +#endif diff --git a/inc/libcmis-c/oauth2-data.h b/inc/libcmis-c/oauth2-data.h new file mode 100644 index 0000000..ec4a9d3 --- /dev/null +++ b/inc/libcmis-c/oauth2-data.h @@ -0,0 +1,58 @@ + +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_OAUTH2_DATA_H_ +#define _LIBCMIS_OAUTH2_DATA_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API libcmis_OAuth2DataPtr libcmis_oauth2data_create( + char* authUrl, char* tokenUrl, char* scopes, char* redirectUri, + char* clientId, char* clientSecret ); + +LIBCMIS_C_API void libcmis_oauth2data_free( libcmis_OAuth2DataPtr oauth2 ); + +LIBCMIS_C_API bool libcmis_oauth2data_isComplete( libcmis_OAuth2DataPtr oauth2 ); + +LIBCMIS_C_API const char* libcmis_oauth2data_getAuthUrl( libcmis_OAuth2DataPtr oauth2 ); +LIBCMIS_C_API const char* libcmis_oauth2data_getTokenUrl( libcmis_OAuth2DataPtr oauth2 ); +LIBCMIS_C_API const char* libcmis_oauth2data_getClientId( libcmis_OAuth2DataPtr oauth2 ); +LIBCMIS_C_API const char* libcmis_oauth2data_getClientSecret( libcmis_OAuth2DataPtr oauth2 ); +LIBCMIS_C_API const char* libcmis_oauth2data_getScope( libcmis_OAuth2DataPtr oauth2 ); +LIBCMIS_C_API const char* libcmis_oauth2data_getRedirectUri( libcmis_OAuth2DataPtr oauth2 ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/object-type.h b/inc/libcmis-c/object-type.h new file mode 100644 index 0000000..0e0317c --- /dev/null +++ b/inc/libcmis-c/object-type.h @@ -0,0 +1,114 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_OBJECT_TYPE_H_ +#define _LIBCMIS_OBJECT_TYPE_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +LIBCMIS_C_API void libcmis_vector_object_type_free( libcmis_vector_object_type_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_object_type_size( libcmis_vector_object_type_Ptr vector ); +LIBCMIS_C_API libcmis_ObjectTypePtr libcmis_vector_object_type_get( libcmis_vector_object_type_Ptr vector, size_t i ); + + +LIBCMIS_C_API void libcmis_object_type_free( libcmis_ObjectTypePtr type ); + +/** The resulting value needs to be freed + */ +LIBCMIS_C_API char* libcmis_object_type_getId( libcmis_ObjectTypePtr type ); + +/** The resulting value needs to be freed + */ +LIBCMIS_C_API char* libcmis_object_type_getLocalName( libcmis_ObjectTypePtr type ); + +/** The resulting value needs to be freed + */ +LIBCMIS_C_API char* libcmis_object_type_getLocalNamespace( libcmis_ObjectTypePtr type ); + +/** The resulting value needs to be freed + */ +LIBCMIS_C_API char* libcmis_object_type_getQueryName( libcmis_ObjectTypePtr type ); + +/** The resulting value needs to be freed + */ +LIBCMIS_C_API char* libcmis_object_type_getDisplayName( libcmis_ObjectTypePtr type ); + +/** The resulting value needs to be freed + */ +LIBCMIS_C_API char* libcmis_object_type_getDescription( libcmis_ObjectTypePtr type ); + +LIBCMIS_C_API libcmis_ObjectTypePtr libcmis_object_type_getParentType( + libcmis_ObjectTypePtr type, + libcmis_ErrorPtr error ); +LIBCMIS_C_API libcmis_ObjectTypePtr libcmis_object_type_getBaseType( + libcmis_ObjectTypePtr type, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_vector_object_type_Ptr libcmis_object_type_getChildren( + libcmis_ObjectTypePtr type, + libcmis_ErrorPtr error ); + +/** The resulting value needs to be freed + \since libcmis 0.4 + */ +LIBCMIS_C_API char* libcmis_object_type_getParentTypeId( libcmis_ObjectTypePtr type ); + +/** The resulting value needs to be freed + \since libcmis 0.4 + */ +LIBCMIS_C_API char* libcmis_object_type_getBaseTypeId( libcmis_ObjectTypePtr type ); + +LIBCMIS_C_API bool libcmis_object_type_isCreatable( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API bool libcmis_object_type_isFileable( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API bool libcmis_object_type_isQueryable( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API bool libcmis_object_type_isFulltextIndexed( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API bool libcmis_object_type_isIncludedInSupertypeQuery( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API bool libcmis_object_type_isControllablePolicy( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API bool libcmis_object_type_isControllableACL( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API bool libcmis_object_type_isVersionable( libcmis_ObjectTypePtr type ); + +LIBCMIS_C_API libcmis_object_type_ContentStreamAllowed libcmis_object_type_getContentStreamAllowed( libcmis_ObjectTypePtr type ); + +LIBCMIS_C_API libcmis_vector_property_type_Ptr libcmis_object_type_getPropertiesTypes( libcmis_ObjectTypePtr type ); +LIBCMIS_C_API libcmis_PropertyTypePtr libcmis_object_type_getPropertyType( libcmis_ObjectTypePtr type, const char* id ); + + +/** The resulting value needs to be freed + */ +LIBCMIS_C_API char* libcmis_object_type_toString( libcmis_ObjectTypePtr type ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/object.h b/inc/libcmis-c/object.h new file mode 100644 index 0000000..8739cff --- /dev/null +++ b/inc/libcmis-c/object.h @@ -0,0 +1,134 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_OBJECT_H_ +#define _LIBCMIS_OBJECT_H_ + +#include <time.h> + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_vector_object_free( libcmis_vector_object_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_object_size( libcmis_vector_object_Ptr vector ); +LIBCMIS_C_API libcmis_ObjectPtr libcmis_vector_object_get( libcmis_vector_object_Ptr vector, size_t i ); + + +LIBCMIS_C_API void libcmis_object_free( libcmis_ObjectPtr object ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getId( libcmis_ObjectPtr object ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getName( libcmis_ObjectPtr object ); + +LIBCMIS_C_API libcmis_vector_string_Ptr libcmis_object_getPaths( libcmis_ObjectPtr object ); + + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getBaseType( libcmis_ObjectPtr object ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getType( libcmis_ObjectPtr object ); + + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getCreatedBy( libcmis_ObjectPtr object ); +LIBCMIS_C_API time_t libcmis_object_getCreationDate( libcmis_ObjectPtr object ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getLastModifiedBy( libcmis_ObjectPtr object ); +LIBCMIS_C_API time_t libcmis_object_getLastModificationDate( libcmis_ObjectPtr object ); + + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getChangeToken( libcmis_ObjectPtr object ); +LIBCMIS_C_API bool libcmis_object_isImmutable( libcmis_ObjectPtr object ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API libcmis_vector_string_Ptr libcmis_object_getSecondaryTypes( libcmis_ObjectPtr object ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_getThumbnailUrl( libcmis_ObjectPtr object ); + +LIBCMIS_C_API libcmis_vector_rendition_Ptr libcmis_object_getRenditions( libcmis_ObjectPtr object, libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_ObjectPtr +LIBCMIS_C_API libcmis_object_addSecondaryType( libcmis_ObjectPtr object, + const char* id, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_ObjectPtr +LIBCMIS_C_API libcmis_object_removeSecondaryType( libcmis_ObjectPtr object, + const char* id, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_vector_property_Ptr libcmis_object_getProperties( libcmis_ObjectPtr object ); +LIBCMIS_C_API libcmis_PropertyPtr libcmis_object_getProperty( libcmis_ObjectPtr object, const char* name ); +LIBCMIS_C_API void libcmis_object_setProperty( libcmis_ObjectPtr object, libcmis_PropertyPtr property ); +LIBCMIS_C_API libcmis_ObjectPtr libcmis_object_updateProperties( + libcmis_ObjectPtr object, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_ObjectTypePtr libcmis_object_getTypeDescription( libcmis_ObjectPtr object ); +LIBCMIS_C_API libcmis_AllowableActionsPtr libcmis_object_getAllowableActions( libcmis_ObjectPtr object ); + +LIBCMIS_C_API void libcmis_object_refresh( libcmis_ObjectPtr object, libcmis_ErrorPtr error ); +LIBCMIS_C_API time_t libcmis_object_getRefreshTimestamp( libcmis_ObjectPtr object ); + +LIBCMIS_C_API void libcmis_object_remove( libcmis_ObjectPtr object, bool allVersions, libcmis_ErrorPtr error ); + +LIBCMIS_C_API void libcmis_object_move( libcmis_ObjectPtr object, + libcmis_FolderPtr source, + libcmis_FolderPtr dest, + libcmis_ErrorPtr error ); + + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_object_toString( libcmis_ObjectPtr object ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/property-type.h b/inc/libcmis-c/property-type.h new file mode 100644 index 0000000..f0faa90 --- /dev/null +++ b/inc/libcmis-c/property-type.h @@ -0,0 +1,86 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_PROPERTY_TYPE_H_ +#define _LIBCMIS_PROPERTY_TYPE_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +LIBCMIS_C_API void libcmis_vector_property_type_free( libcmis_vector_property_type_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_property_type_size( libcmis_vector_property_type_Ptr vector ); +LIBCMIS_C_API libcmis_PropertyTypePtr libcmis_vector_property_type_get( libcmis_vector_property_type_Ptr vector, size_t i ); + +LIBCMIS_C_API void libcmis_property_type_free( libcmis_PropertyTypePtr type ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_property_type_getId( libcmis_PropertyTypePtr type ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_property_type_getLocalName( libcmis_PropertyTypePtr type ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_property_type_getLocalNamespace( libcmis_PropertyTypePtr type ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_property_type_getDisplayName( libcmis_PropertyTypePtr type ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_property_type_getQueryName( libcmis_PropertyTypePtr type ); + +LIBCMIS_C_API libcmis_property_type_Type libcmis_property_type_getType( libcmis_PropertyTypePtr type ); + +/** The resulting value needs to be free'd. + */ +LIBCMIS_C_API char* libcmis_property_type_getXmlType( libcmis_PropertyTypePtr type ); + +LIBCMIS_C_API bool libcmis_property_type_isMultiValued( libcmis_PropertyTypePtr type ); +LIBCMIS_C_API bool libcmis_property_type_isUpdatable( libcmis_PropertyTypePtr type ); +LIBCMIS_C_API bool libcmis_property_type_isInherited( libcmis_PropertyTypePtr type ); +LIBCMIS_C_API bool libcmis_property_type_isRequired( libcmis_PropertyTypePtr type ); +LIBCMIS_C_API bool libcmis_property_type_isQueryable( libcmis_PropertyTypePtr type ); +LIBCMIS_C_API bool libcmis_property_type_isOrderable( libcmis_PropertyTypePtr type ); +LIBCMIS_C_API bool libcmis_property_type_isOpenChoice( libcmis_PropertyTypePtr type ); + +LIBCMIS_C_API void libcmis_property_type_update( libcmis_PropertyTypePtr propDef, + libcmis_vector_object_type_Ptr types ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/property.h b/inc/libcmis-c/property.h new file mode 100644 index 0000000..7d06418 --- /dev/null +++ b/inc/libcmis-c/property.h @@ -0,0 +1,66 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_PROPERTY_HXX_ +#define _LIBCMIS_PROPERTY_HXX_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API libcmis_vector_property_Ptr libcmis_vector_property_create( ); +LIBCMIS_C_API void libcmis_vector_property_free( libcmis_vector_property_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_property_size( libcmis_vector_property_Ptr vector ); +LIBCMIS_C_API libcmis_PropertyPtr libcmis_vector_property_get( libcmis_vector_property_Ptr vector, size_t i ); + +/** The item object can be deleted after this call safely. + */ +LIBCMIS_C_API void libcmis_vector_property_append( libcmis_vector_property_Ptr vector, libcmis_PropertyPtr item ); + + +LIBCMIS_C_API libcmis_PropertyPtr libcmis_property_create( libcmis_PropertyTypePtr type, const char** strValues, size_t size ); +LIBCMIS_C_API void libcmis_property_free( libcmis_PropertyPtr property ); + +LIBCMIS_C_API libcmis_PropertyTypePtr libcmis_property_getPropertyType( libcmis_PropertyPtr property ); + +LIBCMIS_C_API libcmis_vector_time_Ptr libcmis_property_getDateTimes( libcmis_PropertyPtr property ); +LIBCMIS_C_API libcmis_vector_bool_Ptr libcmis_property_getBools( libcmis_PropertyPtr property ); +LIBCMIS_C_API libcmis_vector_string_Ptr libcmis_property_getStrings( libcmis_PropertyPtr property ); +LIBCMIS_C_API libcmis_vector_long_Ptr libcmis_property_getLongs( libcmis_PropertyPtr property ); +LIBCMIS_C_API libcmis_vector_double_Ptr libcmis_property_getDoubles( libcmis_PropertyPtr property ); + +LIBCMIS_C_API void libcmis_property_setValues( libcmis_PropertyPtr property, const char** strValues, size_t size ); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/inc/libcmis-c/rendition.h b/inc/libcmis-c/rendition.h new file mode 100644 index 0000000..927fd28 --- /dev/null +++ b/inc/libcmis-c/rendition.h @@ -0,0 +1,59 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _LIBCMIS_RENDITION_H_ +#define _LIBCMIS_RENDITION_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_rendition_free( libcmis_RenditionPtr rendition ); + +LIBCMIS_C_API bool libcmis_rendition_isThumbnail( libcmis_RenditionPtr rendition ); + +LIBCMIS_C_API const char* libcmis_rendition_getStreamId( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API const char* libcmis_rendition_getMimeType( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API const char* libcmis_rendition_getKind( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API const char* libcmis_rendition_getUrl( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API const char* libcmis_rendition_getTitle( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API long libcmis_rendition_getLength( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API long libcmis_rendition_getWidth( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API long libcmis_rendition_getHeight( libcmis_RenditionPtr rendition ); +LIBCMIS_C_API const char* libcmis_rendition_getRenditionDocumentId( libcmis_RenditionPtr rendition ); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/inc/libcmis-c/repository.h b/inc/libcmis-c/repository.h new file mode 100644 index 0000000..651a881 --- /dev/null +++ b/inc/libcmis-c/repository.h @@ -0,0 +1,107 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _REPOSITORY_H_ +#define _REPOSITORY_H_ + +#include <libxml/tree.h> + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_vector_repository_free( libcmis_vector_Repository_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_repository_size( libcmis_vector_Repository_Ptr vector ); +LIBCMIS_C_API libcmis_RepositoryPtr libcmis_vector_repository_get( libcmis_vector_Repository_Ptr vector, size_t i ); + + +LIBCMIS_C_API libcmis_RepositoryPtr libcmis_repository_create( xmlNodePtr node ); + +LIBCMIS_C_API void libcmis_repository_free( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getId( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getName( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getDescription( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getVendorName( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getProductName( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getProductVersion( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getRootId( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getCmisVersionSupported( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getThinClientUri( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getPrincipalAnonymous( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getPrincipalAnyone( libcmis_RepositoryPtr repository ); + +/** The resulting value needs to be freed. + */ +LIBCMIS_C_API char* libcmis_repository_getCapability( + libcmis_RepositoryPtr repository, + libcmis_repository_capability_Type capability ); + +LIBCMIS_C_API bool libcmis_repository_getCapabilityAsBool( + libcmis_RepositoryPtr repository, + libcmis_repository_capability_Type capability ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/session-factory.h b/inc/libcmis-c/session-factory.h new file mode 100644 index 0000000..f10ddd2 --- /dev/null +++ b/inc/libcmis-c/session-factory.h @@ -0,0 +1,79 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SESSION_FACTORY_H_ +#define _SESSION_FACTORY_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_setAuthenticationCallback( libcmis_authenticationCallback callback ); +LIBCMIS_C_API void libcmis_setCertValidationCallback( libcmis_certValidationCallback callback ); +LIBCMIS_C_API void libcmis_setOAuth2AuthCodeProvider( libcmis_oauth2AuthCodeProvider callback ); +LIBCMIS_C_API libcmis_oauth2AuthCodeProvider libcmis_getOAuth2AuthCodeProvider( ); + +LIBCMIS_C_API void libcmis_setProxySettings( + char* proxy, + char* noProxy, + char* proxyUser, + char* proxyPass ); + +LIBCMIS_C_API const char* libcmis_getProxy( ); +LIBCMIS_C_API const char* libcmis_getNoProxy( ); +LIBCMIS_C_API const char* libcmis_getProxyUser( ); +LIBCMIS_C_API const char* libcmis_getProxyPass( ); + +LIBCMIS_C_API libcmis_SessionPtr libcmis_createSession( + char* bindingUrl, + char* repositoryId, + char* username, + char* password, + bool noSslCheck, + libcmis_OAuth2DataPtr oauth2, + bool verbose, + libcmis_ErrorPtr error ); + +/** + \deprecated + use libcmis_createSession and libcmis_session_getRepositories instead + */ +LIBCMIS_C_API libcmis_vector_Repository_Ptr libcmis_getRepositories( + char* bindingUrl, + char* username, + char* password, + bool verbose, + libcmis_ErrorPtr error ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/session.h b/inc/libcmis-c/session.h new file mode 100644 index 0000000..77c0d90 --- /dev/null +++ b/inc/libcmis-c/session.h @@ -0,0 +1,83 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_SESSION_H_ +#define _LIBCMIS_SESSION_H_ + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_session_free( libcmis_SessionPtr session ); + +LIBCMIS_C_API libcmis_RepositoryPtr libcmis_session_getRepository( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_vector_Repository_Ptr libcmis_session_getRepositories( + libcmis_SessionPtr session ); + +LIBCMIS_C_API bool libcmis_session_setRepository( + libcmis_SessionPtr session, + const char* repositoryId ); + +LIBCMIS_C_API libcmis_FolderPtr libcmis_session_getRootFolder( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_ObjectPtr libcmis_session_getObject( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_ObjectPtr libcmis_session_getObjectByPath( + libcmis_SessionPtr session, + const char* path, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_FolderPtr libcmis_session_getFolder( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_ObjectTypePtr libcmis_session_getType( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ); + +LIBCMIS_C_API libcmis_vector_object_type_Ptr libcmis_session_getBaseTypes( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/types.h b/inc/libcmis-c/types.h new file mode 100644 index 0000000..c3e784c --- /dev/null +++ b/inc/libcmis-c/types.h @@ -0,0 +1,228 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_TYPES_H_ +#define _LIBCMIS_TYPES_H_ + +#ifndef __cplusplus +#include <stdbool.h> +#endif +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Vectors of simple types */ + + +typedef struct libcmis_vector_bool* libcmis_vector_bool_Ptr; + +typedef struct libcmis_vector_string* libcmis_vector_string_Ptr; + +typedef struct libcmis_vector_long* libcmis_vector_long_Ptr; + +typedef struct libcmis_vector_double* libcmis_vector_double_Ptr; + +typedef struct libcmis_vector_time* libcmis_vector_time_Ptr; + +typedef struct libcmis_vector_repository* libcmis_vector_Repository_Ptr; + + +/* AllowableActions */ + + +typedef struct libcmis_allowable_actions* libcmis_AllowableActionsPtr; + +typedef enum +{ + libcmis_DeleteObject, + libcmis_UpdateProperties, + libcmis_GetFolderTree, + libcmis_GetProperties, + libcmis_GetObjectRelationships, + libcmis_GetObjectParents, + libcmis_GetFolderParent, + libcmis_GetDescendants, + libcmis_MoveObject, + libcmis_DeleteContentStream, + libcmis_CheckOut, + libcmis_CancelCheckOut, + libcmis_CheckIn, + libcmis_SetContentStream, + libcmis_GetAllVersions, + libcmis_AddObjectToFolder, + libcmis_RemoveObjectFromFolder, + libcmis_GetContentStream, + libcmis_ApplyPolicy, + libcmis_GetAppliedPolicies, + libcmis_RemovePolicy, + libcmis_GetChildren, + libcmis_CreateDocument, + libcmis_CreateFolder, + libcmis_CreateRelationship, + libcmis_DeleteTree, + libcmis_GetRenditions, + libcmis_GetACL, + libcmis_ApplyACL +} libcmis_allowable_actions_Type; + + +/* Document */ + + +typedef struct libcmis_document* libcmis_DocumentPtr; +typedef size_t ( *libcmis_writeFn )( const void*, size_t, size_t, void* ); +typedef size_t ( *libcmis_readFn )( void*, size_t, size_t, void* ); + +typedef struct libcmis_vector_document* libcmis_vector_document_Ptr; + +/* Error */ + + +typedef struct libcmis_error* libcmis_ErrorPtr; + + +/* Folder */ + + +typedef struct libcmis_folder* libcmis_FolderPtr; + + +typedef struct libcmis_vector_folder* libcmis_vector_folder_Ptr; + +typedef enum +{ + libcmis_Unfile, + libcmis_DeleteSingleFiled, + libcmis_Delete +} libcmis_folder_UnfileObjects; + + +/* ObjectType */ + + +typedef struct libcmis_object_type* libcmis_ObjectTypePtr; + +typedef struct libcmis_vector_object_type* libcmis_vector_object_type_Ptr; + +typedef enum +{ + libcmis_NotAllowed, + libcmis_Allowed, + libcmis_Required +} libcmis_object_type_ContentStreamAllowed; + + +/* Object */ + + +typedef struct libcmis_object* libcmis_ObjectPtr; + +typedef struct libcmis_vector_object* libcmis_vector_object_Ptr; + + +/* Property */ + + +typedef struct libcmis_property* libcmis_PropertyPtr; + +typedef struct libcmis_vector_property* libcmis_vector_property_Ptr; + + +/* PropertyType */ + + +typedef struct libcmis_property_type* libcmis_PropertyTypePtr; + +typedef struct libcmis_vector_property_type* libcmis_vector_property_type_Ptr; + +typedef enum +{ + libcmis_String, + libcmis_Integer, + libcmis_Decimal, + libcmis_Bool, + libcmis_DateTime +} libcmis_property_type_Type; + + +/* Repository */ + +typedef enum +{ + libcmis_capability_ACL, + libcmis_capability_AllVersionsSearchable, + libcmis_capability_Changes, + libcmis_capability_ContentStreamUpdatability, + libcmis_capability_GetDescendants, + libcmis_capability_GetFolderTree, + libcmis_capability_OrderBy, + libcmis_capability_Multifiling, + libcmis_capability_PWCSearchable, + libcmis_capability_PWCUpdatable, + libcmis_capability_Query, + libcmis_capability_Renditions, + libcmis_capability_Unfiling, + libcmis_capability_VersionSpecificFiling, + libcmis_capability_Join +} libcmis_repository_capability_Type; + +typedef struct libcmis_repository* libcmis_RepositoryPtr; + + +/* Session */ + + +typedef struct libcmis_session* libcmis_SessionPtr; + +typedef bool ( *libcmis_authenticationCallback )( char* username, char* password ); +typedef bool ( *libcmis_certValidationCallback )( libcmis_vector_string_Ptr certificatesChain ); +typedef char * ( *libcmis_oauth2AuthCodeProvider ) ( const char* authUrl, const char* username, const char* password ); + + +/* OAuth2Data */ + + +typedef struct libcmis_oauth2data* libcmis_OAuth2DataPtr; + +typedef char* ( *libcmis_OAuth2AuthCodeProvider )( const char* authUrl, + const char* username, const char* password ); + + +/* Rendition */ + + +typedef struct libcmis_rendition* libcmis_RenditionPtr; +typedef struct libcmis_vector_rendition* libcmis_vector_rendition_Ptr; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis-c/vectors.h b/inc/libcmis-c/vectors.h new file mode 100644 index 0000000..2c791c7 --- /dev/null +++ b/inc/libcmis-c/vectors.h @@ -0,0 +1,68 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_VECTORS_HXX_ +#define _LIBCMIS_VECTORS_HXX_ + +#include <time.h> + +#include "libcmis-c/libcmis-c-api.h" +#include "libcmis-c/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LIBCMIS_C_API void libcmis_vector_bool_free( libcmis_vector_bool_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_bool_size( libcmis_vector_bool_Ptr vector ); +LIBCMIS_C_API bool libcmis_vector_bool_get( libcmis_vector_bool_Ptr vector, size_t i ); + + +LIBCMIS_C_API void libcmis_vector_string_free( libcmis_vector_string_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_string_size( libcmis_vector_string_Ptr vector ); +LIBCMIS_C_API const char* libcmis_vector_string_get( libcmis_vector_string_Ptr vector, size_t i ); + + +LIBCMIS_C_API void libcmis_vector_long_free( libcmis_vector_long_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_long_size( libcmis_vector_long_Ptr vector ); +LIBCMIS_C_API long libcmis_vector_long_get( libcmis_vector_long_Ptr vector, size_t i ); + + +LIBCMIS_C_API void libcmis_vector_double_free( libcmis_vector_double_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_double_size( libcmis_vector_double_Ptr vector ); +LIBCMIS_C_API double libcmis_vector_double_get( libcmis_vector_double_Ptr vector, size_t i ); + + +LIBCMIS_C_API void libcmis_vector_time_free( libcmis_vector_time_Ptr vector ); +LIBCMIS_C_API size_t libcmis_vector_time_size( libcmis_vector_time_Ptr vector ); +LIBCMIS_C_API time_t libcmis_vector_time_get( libcmis_vector_time_Ptr vector, size_t i ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/libcmis/Makefile.am b/inc/libcmis/Makefile.am new file mode 100644 index 0000000..93b139b --- /dev/null +++ b/inc/libcmis/Makefile.am @@ -0,0 +1,20 @@ +libcmisdir = $(includedir)/libcmis-@LIBCMIS_API_VERSION@/libcmis + +dist_libcmis_HEADERS = \ + allowable-actions.hxx \ + document.hxx \ + exception.hxx \ + folder.hxx \ + libcmis-api.h \ + libcmis.hxx \ + oauth2-data.hxx \ + object-type.hxx \ + object.hxx \ + property-type.hxx \ + property.hxx \ + rendition.hxx \ + repository.hxx \ + session-factory.hxx \ + session.hxx \ + xml-utils.hxx \ + xmlserializable.hxx diff --git a/inc/libcmis/allowable-actions.hxx b/inc/libcmis/allowable-actions.hxx new file mode 100644 index 0000000..50e9402 --- /dev/null +++ b/inc/libcmis/allowable-actions.hxx @@ -0,0 +1,131 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ALLOWABLE_ACTIONS_HXX_ +#define _ALLOWABLE_ACTIONS_HXX_ + +#include <map> +#include <string> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include "libcmis/exception.hxx" +#include "libcmis/libcmis-api.h" + +namespace libcmis +{ + class Object; + + class LIBCMIS_API ObjectAction + { + public: + enum Type + { + DeleteObject, + UpdateProperties, + GetFolderTree, + GetProperties, + GetObjectRelationships, + GetObjectParents, + GetFolderParent, + GetDescendants, + MoveObject, + DeleteContentStream, + CheckOut, + CancelCheckOut, + CheckIn, + SetContentStream, + GetAllVersions, + AddObjectToFolder, + RemoveObjectFromFolder, + GetContentStream, + ApplyPolicy, + GetAppliedPolicies, + RemovePolicy, + GetChildren, + CreateDocument, + CreateFolder, + CreateRelationship, + DeleteTree, + GetRenditions, + GetACL, + ApplyACL + }; + + private: + Type m_type; + bool m_enabled; + bool m_valid; + + public: + ObjectAction( xmlNodePtr node ); + virtual ~ObjectAction( ){ } + + Type getType( ) { return m_type; } + bool isEnabled( ) { return m_enabled; } + bool isValid( ) { return m_valid; } + + /** Parses the permission name into one of the enum values or throws + an exception for invalid input strings. + */ + static Type parseType( std::string type ); + + }; + + /** Class providing access to the allowed actions on an object. + */ + class LIBCMIS_API AllowableActions + { + protected: + std::map< ObjectAction::Type, bool > m_states; + + public: + /** Default constructor for testing purpose + */ + AllowableActions( ); + AllowableActions( xmlNodePtr node ); + AllowableActions( const AllowableActions& copy ); + virtual ~AllowableActions( ); + + AllowableActions& operator=( const AllowableActions& copy ); + + /** Returns the permissions for the corresponding actions. + */ + bool isAllowed( ObjectAction::Type action ); + + /** Returns true if the action was defined, false if the default + value is used. + */ + bool isDefined( ObjectAction::Type action ); + + std::string toString( ); + }; + typedef boost::shared_ptr< AllowableActions > AllowableActionsPtr; +} + +#endif diff --git a/inc/libcmis/document.hxx b/inc/libcmis/document.hxx new file mode 100644 index 0000000..8f44313 --- /dev/null +++ b/inc/libcmis/document.hxx @@ -0,0 +1,147 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _DOCUMENT_HXX_ +#define _DOCUMENT_HXX_ + +#include <iostream> +#include <string> +#include <vector> + +#include <boost/shared_ptr.hpp> + +#include "libcmis/exception.hxx" +#include "libcmis/libcmis-api.h" +#include "libcmis/object.hxx" + +namespace libcmis +{ + class Folder; + class Session; + + /** Interface for a CMIS Document object. + */ + class LIBCMIS_API Document : public virtual Object + { + public: + Document( Session* session ) : Object( session ) { } + virtual ~Document( ) { } + + /** Get the folder parents for the document. + + Note that an unfiled document will have no parent folder. + + @return the parents folder if any. + */ + virtual std::vector< boost::shared_ptr< Folder > > getParents( ) = 0; + + /** Get the content stream without using a temporary file. + + <p>The stream may not contain anything if there is + no content or if something wrong happened during the + download.</p> + + @param streamId of the rendition + @return + An input stream to read the data from. + + @throws Exception + if anything wrong happened during the file transfer. + In such a case, the content of the stream can't be + guaranteed. + */ + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ) + = 0; + + /** Set or replace the content stream of the document. + + @param is the output stream containing the new data for the content stream + @param contentType the mime-type of the new content stream + @param filename the filename to set for the file + @param overwrite if set to false, don't overwrite the content stream if one is already set. + + @throw Exception if anything happens during the upload like a wrong authentication, + no rights to set the stream, server doesn't have the ContentStreamUpdatability + capability. + */ + virtual void setContentStream( boost::shared_ptr< std::ostream > os, std::string contentType, + std::string filename, bool overwrite = true ) = 0; + + /** Get the content mime type. + */ + virtual std::string getContentType( ); + + /** Get the content stream filename. + */ + virtual std::string getContentFilename( ); + + /** Get the content length in bytes. + */ + virtual long getContentLength( ); + + /** Checks out the document and returns the object corresponding to the + created Private Working Copy. + + \return the Private Working Copy document + */ + virtual boost::shared_ptr< Document > checkOut( ) = 0; + + /** Cancels the checkout if the document is a private working copy, or + throws an exception. + */ + virtual void cancelCheckout( ) = 0; + + /** Check in the private working copy and create a new version or throw + an exception. + + The current object will be updated to reflect the changes performed + on the server side. + + \param isMajor defines it the version to create is a major or minor one + \param comment contains the checkin comment + \param properties the properties to set the new version + \param stream the content stream to set for the new version + \param contentType the mime type of the stream to set + + \return the document with the new version + */ + virtual boost::shared_ptr< Document > checkIn( bool isMajor, std::string comment, + const std::map< std::string, PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string fileName ) = 0; + + virtual std::vector< boost::shared_ptr< Document > > getAllVersions( ) = 0; + + // virtual methods form Object + virtual std::vector< std::string > getPaths( ); + + virtual std::string toString( ); + }; + typedef boost::shared_ptr< Document > DocumentPtr; +} + +#endif diff --git a/inc/libcmis/exception.hxx b/inc/libcmis/exception.hxx new file mode 100644 index 0000000..aa42ae7 --- /dev/null +++ b/inc/libcmis/exception.hxx @@ -0,0 +1,62 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _EXCEPTION_HXX_ +#define _EXCEPTION_HXX_ + +#include <exception> +#include <string> + +#include "libcmis/libcmis-api.h" + +namespace libcmis +{ + class LIBCMIS_API Exception : public std::exception + { + private: + std::string m_message; + std::string m_type; + + public: + Exception( std::string message, std::string type = "runtime" ) : + exception( ), + m_message( message ), + m_type( type ) + { + } + + ~Exception( ) noexcept { } + virtual const char* what() const noexcept + { + return m_message.c_str( ); + } + + std::string getType( ) const { return m_type; } + }; +} + +#endif diff --git a/inc/libcmis/folder.hxx b/inc/libcmis/folder.hxx new file mode 100644 index 0000000..0010dbd --- /dev/null +++ b/inc/libcmis/folder.hxx @@ -0,0 +1,84 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _FOLDER_HXX_ +#define _FOLDER_HXX_ + +#include <map> +#include <string> +#include <vector> + +#include "libcmis/exception.hxx" +#include "libcmis/libcmis-api.h" +#include "libcmis/object.hxx" + +namespace libcmis +{ + class Document; + class Session; + + struct LIBCMIS_API UnfileObjects { + enum Type + { + Unfile, + DeleteSingleFiled, + Delete + }; + }; + + /** Class representing a CMIS folder. + */ + class LIBCMIS_API Folder : public virtual Object + { + public: + Folder( Session* session ) : Object( session ) { } + virtual ~Folder() { } + + virtual std::vector< std::string > getPaths( ); + + virtual boost::shared_ptr< Folder > getFolderParent( ); + virtual std::vector< ObjectPtr > getChildren( ) = 0; + virtual std::string getParentId( ); + virtual std::string getPath( ); + + virtual bool isRootFolder( ); + + virtual boost::shared_ptr< Folder > createFolder( const std::map< std::string, PropertyPtr >& properties ) + = 0; + virtual boost::shared_ptr< Document > createDocument( const std::map< std::string, PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType, std::string fileName ) = 0; + + virtual std::vector< std::string > removeTree( bool allVersion = true, UnfileObjects::Type unfile = UnfileObjects::Delete, + bool continueOnError = false ) = 0; + + virtual std::string toString( ); + }; + typedef boost::shared_ptr< Folder > FolderPtr; + +} + +#endif diff --git a/inc/libcmis/libcmis-api.h b/inc/libcmis/libcmis-api.h new file mode 100644 index 0000000..983ff4f --- /dev/null +++ b/inc/libcmis/libcmis-api.h @@ -0,0 +1,45 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_API_H_ +#define _LIBCMIS_API_H_ + +#ifdef DLL_EXPORT +#ifdef LIBCMIS_BUILD +#define LIBCMIS_API __declspec(dllexport) +#else +#define LIBCMIS_API __declspec(dllimport) +#endif +#else // !DLL_EXPORT +#ifdef LIBCMIS_VISIBILITY +#define LIBCMIS_API __attribute__((visibility("default"))) +#else +#define LIBCMIS_API +#endif +#endif + +#endif diff --git a/inc/libcmis/libcmis.hxx b/inc/libcmis/libcmis.hxx new file mode 100644 index 0000000..411850d --- /dev/null +++ b/inc/libcmis/libcmis.hxx @@ -0,0 +1,49 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_HXX_ +#define _LIBCMIS_HXX_ + +#include "libcmis/libcmis-api.h" + +#include "libcmis/allowable-actions.hxx" +#include "libcmis/document.hxx" +#include "libcmis/exception.hxx" +#include "libcmis/folder.hxx" +#include "libcmis/oauth2-data.hxx" +#include "libcmis/object-type.hxx" +#include "libcmis/object.hxx" +#include "libcmis/property-type.hxx" +#include "libcmis/property.hxx" +#include "libcmis/rendition.hxx" +#include "libcmis/repository.hxx" +#include "libcmis/session-factory.hxx" +#include "libcmis/session.hxx" +#include "libcmis/xml-utils.hxx" +#include "libcmis/xmlserializable.hxx" + +#endif diff --git a/inc/libcmis/oauth2-data.hxx b/inc/libcmis/oauth2-data.hxx new file mode 100644 index 0000000..5321652 --- /dev/null +++ b/inc/libcmis/oauth2-data.hxx @@ -0,0 +1,78 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_OAUTH2_DATA_HXX_ +#define _LIBCMIS_OAUTH2_DATA_HXX_ + +#include <string> +#include <boost/shared_ptr.hpp> + +#include "libcmis/libcmis-api.h" + +namespace libcmis +{ + /** Class storing the data needed for OAuth2 authentication. + */ + class LIBCMIS_API OAuth2Data + { + private: + + std::string m_authUrl; + std::string m_tokenUrl; + std::string m_clientId; + std::string m_clientSecret; + std::string m_scope; + std::string m_redirectUri; + public: + + OAuth2Data( ); + OAuth2Data( const std::string& authUrl, + const std::string& tokenUrl, + const std::string& scope, + const std::string& redirectUri, + const std::string& clientId, + const std::string& clientSecret ); + + OAuth2Data( const OAuth2Data& copy ); + ~OAuth2Data( ); + + OAuth2Data& operator=( const OAuth2Data& copy ); + + bool isComplete(); + + const std::string& getAuthUrl() { return m_authUrl; } + const std::string& getTokenUrl() { return m_tokenUrl; } + const std::string& getClientId() { return m_clientId; } + const std::string& getClientSecret() { return m_clientSecret; } + const std::string& getScope() { return m_scope; } + const std::string& getRedirectUri() { return m_redirectUri; } + }; + typedef boost::shared_ptr< OAuth2Data > OAuth2DataPtr; +} + +#endif //_LIBCMIS_OAUTH2_DATA_HXX_ + diff --git a/inc/libcmis/object-type.hxx b/inc/libcmis/object-type.hxx new file mode 100644 index 0000000..7fbf3e9 --- /dev/null +++ b/inc/libcmis/object-type.hxx @@ -0,0 +1,144 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _OBJECT_TYPE_HXX_ +#define _OBJECT_TYPE_HXX_ + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include <string> +#include <vector> + +#include "libcmis/exception.hxx" +#include "libcmis/libcmis-api.h" +#include "libcmis/property-type.hxx" + +namespace libcmis +{ + /** Class representing a CMIS object type definition. + */ + class LIBCMIS_API ObjectType + { + public: + + enum ContentStreamAllowed + { + NotAllowed, + Allowed, + Required + }; + + protected: + time_t m_refreshTimestamp; + + std::string m_id; + std::string m_localName; + std::string m_localNamespace; + std::string m_displayName; + std::string m_queryName; + std::string m_description; + + std::string m_parentTypeId; + std::string m_baseTypeId; + + bool m_creatable; + bool m_fileable; + bool m_queryable; + bool m_fulltextIndexed; + bool m_includedInSupertypeQuery; + bool m_controllablePolicy; + bool m_controllableAcl; + bool m_versionable; + libcmis::ObjectType::ContentStreamAllowed m_contentStreamAllowed; + + std::map< std::string, libcmis::PropertyTypePtr > m_propertiesTypes; + + ObjectType( ); + void initializeFromNode( xmlNodePtr node ); + + public: + + ObjectType( xmlNodePtr node ); + ObjectType( const ObjectType& copy ); + virtual ~ObjectType() { } + + ObjectType& operator=( const ObjectType& copy ); + + /** Reload the data from the server. + + \attention + This method needs to be implemented in subclasses or it will + do nothing + */ + virtual void refresh( ); + virtual time_t getRefreshTimestamp( ) const; + + std::string getId( ) const; + std::string getLocalName( ) const; + std::string getLocalNamespace( ) const; + std::string getDisplayName( ) const; + std::string getQueryName( ) const; + std::string getDescription( ) const; + + virtual boost::shared_ptr< ObjectType > getParentType( ); + virtual boost::shared_ptr< ObjectType > getBaseType( ); + virtual std::vector< boost::shared_ptr< ObjectType > > getChildren( ); + + /** Get the parent type id without extracting the complete parent type from + the repository. This is mainly provided for performance reasons. + + \since libcmis 0.4 + */ + std::string getParentTypeId( ) const; + + /** Get the base type id without extracting the complete base type from + the repository. This is mainly provided for performance reasons. + + \since libcmis 0.4 + */ + std::string getBaseTypeId( ) const; + + bool isCreatable( ) const; + bool isFileable( ) const; + bool isQueryable( ) const; + bool isFulltextIndexed( ) const; + bool isIncludedInSupertypeQuery( ) const; + bool isControllablePolicy( ) const; + bool isControllableACL( ) const; + bool isVersionable( ) const; + ContentStreamAllowed getContentStreamAllowed( ) const; + + std::map< std::string, PropertyTypePtr >& getPropertiesTypes( ); + + virtual std::string toString( ); + }; + + typedef boost::shared_ptr< ObjectType > ObjectTypePtr; +} + +#endif diff --git a/inc/libcmis/object.hxx b/inc/libcmis/object.hxx new file mode 100644 index 0000000..5e5b3b5 --- /dev/null +++ b/inc/libcmis/object.hxx @@ -0,0 +1,218 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _OBJECT_HXX_ +#define _OBJECT_HXX_ + +#include <ctime> +#include <map> +#include <string> +#include <vector> + +#ifndef __cplusplus +#include <stdbool.h> +#endif + +#include <boost/date_time.hpp> +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include "libcmis/allowable-actions.hxx" +#include "libcmis/exception.hxx" +#include "libcmis/libcmis-api.h" +#include "libcmis/object-type.hxx" +#include "libcmis/property.hxx" +#include "libcmis/xmlserializable.hxx" +#include "libcmis/rendition.hxx" + +namespace libcmis +{ + class Folder; + class Session; + + /** Class representing any CMIS object. + */ + class LIBCMIS_API Object : public XmlSerializable + { + protected: + Session* m_session; + + ObjectTypePtr m_typeDescription; + time_t m_refreshTimestamp; + + /** Type id used as cache before we get it as a property + */ + std::string m_typeId; + + std::map< std::string, PropertyPtr > m_properties; + boost::shared_ptr< AllowableActions > m_allowableActions; + std::vector< RenditionPtr > m_renditions; + void initializeFromNode( xmlNodePtr node ); + + public: + + Object( Session* session ); + Object( Session* session, xmlNodePtr node ); + Object( const Object& copy ); + virtual ~Object( ) { } + + Object& operator=( const Object& copy ); + + virtual std::string getId( ); + virtual std::string getName( ); + virtual std::string getStringProperty( const std::string& propertyName ); + + /** Computes the paths for the objects. + + Note that folders will have only path, documents may have + several ones and there may be cases where there is no path + at all (unfilled objects); + */ + virtual std::vector< std::string > getPaths( ); + + virtual std::string getBaseType( ); + virtual std::string getType( ); + + virtual std::string getCreatedBy( ); + virtual boost::posix_time::ptime getCreationDate( ); + virtual std::string getLastModifiedBy( ); + virtual boost::posix_time::ptime getLastModificationDate( ); + + virtual std::string getChangeToken( ); + virtual bool isImmutable( ); + + virtual std::vector< std::string > getSecondaryTypes(); + + /** Convenience function adding a secondary type to the object. + + Behind the scene this function is basically computing the + properties and sets them for you to avoid reading the CMIS + 1.1 specification, section 2.1.9. + + \param id + the identifier of the secondary type to add + \param properties + the properties coming with the secondary type + + \return + the updated object. Note that it may represent the same + object on the server but it still is a different object + instance (see updateProperties method). + + \throw Exception + if anything wrong happens. Note that the server is likely + to throw a constraint exception if it doesn't allow the + operation. + */ + virtual boost::shared_ptr< Object > addSecondaryType( + std::string id, + PropertyPtrMap properties ); + + /** Convenience function removing a secondary type from the object. + + Behind the scene this function is basically computing the + correct property and sets it for you to avoid reading the + CMIS 1.1 specification, section 2.1.9. + + The server should remove the related properties, there is + normally no need to worry about them. + + \param id + the identifier of the secondary type to remove + + \return + the updated object. Note that it may represent the same + object on the server but it still is a different object + instance (see updateProperties method). + + \throw Exception + if anything wrong happens. Note that the server is likely + to throw a constraint exception if it doesn't allow the + operation. + */ + virtual boost::shared_ptr< Object > removeSecondaryType( std::string id ); + + /** Gives access to the properties of the object. + + \attention + API users should consider this method as read-only as the + changed properties won't be updated to the server. Updating + the returned map may lead to changes loss when calling + updateProperties. + + \sa updateProperties to change properties on the server + */ + virtual libcmis::PropertyPtrMap& getProperties( ); + + + /** Get the renditions of the object. + + \param filter is defined by the CMIS spec section 2.2.1.2.4.1. + By default, this value is just ignored, but some bindings and servers + may use it. + + \attention + The streamId of the rendition is used in getContentStream( ) + */ + virtual std::vector< RenditionPtr> getRenditions( std::string filter = std::string( ) ); + virtual AllowableActionsPtr getAllowableActions( ) { return m_allowableActions; } + + /** Update the object properties and return the updated object. + + \attention + even if the returned object may have the same Id than 'this' + and thus representing the same object on the server, those + are still two different instances to ease memory handling. + */ + virtual boost::shared_ptr< Object > updateProperties( + const PropertyPtrMap& properties ) = 0; + + virtual ObjectTypePtr getTypeDescription( ); + + /** Reload the data from the server. + */ + virtual void refresh( ) = 0; + virtual time_t getRefreshTimestamp( ) { return m_refreshTimestamp; } + + virtual void remove( bool allVersions = true ) = 0; + + virtual void move( boost::shared_ptr< Folder > source, boost::shared_ptr< Folder > destination ) = 0; + + + virtual std::string getThumbnailUrl( ); + + /** Dump the object as a string for debugging or display purpose. + */ + virtual std::string toString( ); + + void toXml( xmlTextWriterPtr writer ); + }; + + typedef boost::shared_ptr< Object > ObjectPtr; +} + +#endif diff --git a/inc/libcmis/property-type.hxx b/inc/libcmis/property-type.hxx new file mode 100644 index 0000000..350a7b2 --- /dev/null +++ b/inc/libcmis/property-type.hxx @@ -0,0 +1,127 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _PROPERTY_TYPE_HXX_ +#define _PROPERTY_TYPE_HXX_ + +#include <boost/date_time.hpp> +#include <libxml/tree.h> + +#include <string> + +#include "libcmis/libcmis-api.h" + +namespace libcmis +{ + class ObjectType; + typedef boost::shared_ptr< ObjectType > ObjectTypePtr; + + class LIBCMIS_API PropertyType + { + public: + + enum Type + { + String, + Integer, + Decimal, + Bool, + DateTime + }; + + private: + + std::string m_id; + std::string m_localName; + std::string m_localNamespace; + std::string m_displayName; + std::string m_queryName; + Type m_type; + std::string m_xmlType; + bool m_multiValued; + bool m_updatable; + bool m_inherited; + bool m_required; + bool m_queryable; + bool m_orderable; + bool m_openChoice; + bool m_temporary; + + public: + + /// Default constructor, mostly present for testing. + PropertyType( ); + PropertyType( xmlNodePtr node ); + PropertyType( const PropertyType& copy ); + /// constructor for temporary type definitions + PropertyType( std::string type, + std::string id, + std::string localName, + std::string displayName, + std::string queryName ); + virtual ~PropertyType( ) { }; + + PropertyType& operator=( const PropertyType& copy ); + + std::string getId( ) { return m_id; } + std::string getLocalName( ) { return m_localName; } + std::string getLocalNamespace( ) { return m_localNamespace; } + std::string getDisplayName( ) { return m_displayName; } + std::string getQueryName( ) { return m_queryName; } + Type getType( ) { return m_type; } + std::string getXmlType( ) { return m_xmlType; } + bool isMultiValued( ) { return m_multiValued; } + bool isUpdatable( ) { return m_updatable; } + bool isInherited( ) { return m_inherited; } + bool isRequired( ) { return m_required; } + bool isQueryable( ) { return m_queryable; } + bool isOrderable( ) { return m_orderable; } + bool isOpenChoice( ) { return m_openChoice; } + + void setId( std::string id ) { m_id = id; } + void setLocalName( std::string localName ) { m_localName = localName; } + void setLocalNamespace( std::string localNamespace ) { m_localNamespace = localNamespace; } + void setDisplayName( std::string displayName ) { m_displayName = displayName; } + void setQueryName( std::string queryName ) { m_queryName = queryName; } + void setType( Type type ) { m_type = type; } + void setMultiValued( bool multivalued ) { m_multiValued = multivalued; } + void setUpdatable( bool updatable ) { m_updatable = updatable; } + void setInherited( bool inherited ) { m_inherited = inherited; } + void setRequired( bool required ) { m_required = required; } + void setQueryable( bool queryable ) { m_queryable = queryable; } + void setOrderable( bool orderable ) { m_orderable = orderable; } + void setOpenChoice( bool openChoice ) { m_openChoice = openChoice; } + + void setTypeFromXml( std::string typeStr ); + void setTypeFromJsonType( std::string jsonType ); + + void update( std::vector< ObjectTypePtr > typesDefs ); + }; + typedef boost::shared_ptr< PropertyType > PropertyTypePtr; +} + +#endif diff --git a/inc/libcmis/property.hxx b/inc/libcmis/property.hxx new file mode 100644 index 0000000..9f67b55 --- /dev/null +++ b/inc/libcmis/property.hxx @@ -0,0 +1,89 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _PROPERTY_HXX_ +#define _PROPERTY_HXX_ + +#include <libxml/tree.h> +#include <libxml/xmlwriter.h> + +#include <boost/date_time.hpp> +#include <boost/shared_ptr.hpp> + +#include <string> +#include <vector> + +#include "libcmis/libcmis-api.h" +#include "libcmis/property-type.hxx" +#include "libcmis/xmlserializable.hxx" + +namespace libcmis +{ + class ObjectType; + + class LIBCMIS_API Property : public XmlSerializable + { + private: + PropertyTypePtr m_propertyType; + std::vector< std::string > m_strValues; + std::vector< bool > m_boolValues; + std::vector< long > m_longValues; + std::vector< double > m_doubleValues; + std::vector< boost::posix_time::ptime > m_dateTimeValues; + + protected: + Property( ); + + public: + /** Property constructor allowing to use different values for the id and names. + */ + Property( PropertyTypePtr propertyType, std::vector< std::string > strValues ); + + ~Property( ){ } + + PropertyTypePtr getPropertyType( ) { return m_propertyType; } + + std::vector< boost::posix_time::ptime > getDateTimes( ) { return m_dateTimeValues; } + std::vector< bool > getBools( ) { return m_boolValues; } + std::vector< std::string > getStrings( ) { return m_strValues; } + std::vector< long > getLongs( ) { return m_longValues; } + std::vector< double > getDoubles( ) { return m_doubleValues; } + + void setPropertyType( PropertyTypePtr propertyType); + void setValues( std::vector< std::string > strValues ); + + void toXml( xmlTextWriterPtr writer ); + + std::string toString( ); + }; + typedef boost::shared_ptr< Property > PropertyPtr; + typedef std::map< std::string, libcmis::PropertyPtr > PropertyPtrMap; + + PropertyPtr parseProperty( xmlNodePtr node, boost::shared_ptr< ObjectType > objectType ); +} + +#endif diff --git a/inc/libcmis/rendition.hxx b/inc/libcmis/rendition.hxx new file mode 100644 index 0000000..62dc59c --- /dev/null +++ b/inc/libcmis/rendition.hxx @@ -0,0 +1,90 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _RENDITION_HXX_ +#define _RENDITION_HXX_ + +#include <string> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include "libcmis/libcmis-api.h" + +namespace libcmis +{ + class LIBCMIS_API Rendition + { + private: + Rendition( ); + + std::string m_streamId; + std::string m_mimeType; + std::string m_kind; + std::string m_href; + std::string m_title; + long m_length; + long m_width; + long m_height; + std::string m_renditionDocumentId; + + public: + Rendition( std::string streamId, std::string mimeType, + std::string kind, std::string href, + std::string title = std::string( ), + long length = -1, long width = -1, long height = -1, + std::string renditionDocumentId = std::string( ) ); + + /** Parse an XML node of type cmisRenditionType + */ + Rendition( xmlNodePtr node ); + ~Rendition( ); + + bool isThumbnail( ); + + const std::string& getStreamId( ) const; + const std::string& getMimeType( ) const; + const std::string& getKind( ) const; + const std::string& getUrl( ) const; + const std::string& getTitle( ) const; + + /** Provides the stream length in bytes or a negative value if missing. + */ + long getLength( ) const; + long getWidth( ) const; + long getHeight( ) const; + const std::string& getRenditionDocumentId( ); + + std::string toString( ); + }; + + typedef boost::shared_ptr< Rendition > RenditionPtr; +} + +#endif + diff --git a/inc/libcmis/repository.hxx b/inc/libcmis/repository.hxx new file mode 100644 index 0000000..3c0f67c --- /dev/null +++ b/inc/libcmis/repository.hxx @@ -0,0 +1,119 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Cédric Bosdonnat <cbosdo@users.sourceforge.net> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _REPOSITORY_HXX_ +#define _REPOSITORY_HXX_ + +#include <map> +#include <string> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include "libcmis/libcmis-api.h" + +namespace libcmis +{ + /** Class representing a repository and its infos. + + \sa 2.2.2.2 section of the CMIS specifications + */ + class LIBCMIS_API Repository + { + public: + + enum Capability + { + ACL, + AllVersionsSearchable, + Changes, + ContentStreamUpdatability, + GetDescendants, + GetFolderTree, + OrderBy, + Multifiling, + PWCSearchable, + PWCUpdatable, + Query, + Renditions, + Unfiling, + VersionSpecificFiling, + Join + }; + + protected: + std::string m_id; + std::string m_name; + std::string m_description; + std::string m_vendorName; + std::string m_productName; + std::string m_productVersion; + std::string m_rootId; + std::string m_cmisVersionSupported; + boost::shared_ptr< std::string > m_thinClientUri; + boost::shared_ptr< std::string > m_principalAnonymous; + boost::shared_ptr< std::string > m_principalAnyone; + + std::map< Capability, std::string > m_capabilities ; + + Repository( ); + void initializeFromNode( xmlNodePtr node ); + + public: + Repository( xmlNodePtr node ); + virtual ~Repository( ) { }; + + std::string getId( ) const; + std::string getName( ) const; + std::string getDescription( ) const; + std::string getVendorName( ) const; + std::string getProductName( ) const; + std::string getProductVersion( ) const; + std::string getRootId( ) const; + std::string getCmisVersionSupported( ) const; + boost::shared_ptr< std::string > getThinClientUri( ) const; + boost::shared_ptr< std::string > getPrincipalAnonymous( ) const; + boost::shared_ptr< std::string > getPrincipalAnyone( ) const; + + std::string getCapability( Capability capability ) const; + + /** Wrapper function providing the capability as a boolean value. + If the capability value is not a boolean, returns false. + */ + bool getCapabilityAsBool( Capability capability ) const; + + std::string toString( ) const; + + private: + + static std::map< Capability, std::string > parseCapabilities( xmlNodePtr node ); + }; + + typedef boost::shared_ptr< Repository > RepositoryPtr; +} + +#endif diff --git a/inc/libcmis/session-factory.hxx b/inc/libcmis/session-factory.hxx new file mode 100644 index 0000000..227ac4d --- /dev/null +++ b/inc/libcmis/session-factory.hxx @@ -0,0 +1,157 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SESSION_FACTORY_HXX_ +#define _SESSION_FACTORY_HXX_ + +#include <vector> +#include <map> +#include <string> + +#include "libcmis/exception.hxx" +#include "libcmis/libcmis-api.h" +#include "libcmis/oauth2-data.hxx" +#include "libcmis/repository.hxx" +#include "libcmis/session.hxx" + +// needed for a callback type +typedef void CURL; + +namespace libcmis +{ + /** This callback provides the OAuth2 code or NULL. + + The returned string must be free()d by the caller. + */ + typedef char* ( *OAuth2AuthCodeProvider )( const char* authUrl, + const char* username, const char* password ); + + class LIBCMIS_API AuthProvider + { + public: + virtual ~AuthProvider() { }; + + /** The function implementing it needs to fill the username and password parameters + and return true. Returning false means that the user cancelled the authentication + and will fail the query. + */ + virtual bool authenticationQuery( std::string& username, std::string& password ) = 0; + }; + typedef boost::shared_ptr< AuthProvider > AuthProviderPtr; + + /** Handler class used to request user input when an invalid SSL certificate is encountered. + */ + class LIBCMIS_API CertValidationHandler + { + public: + virtual ~CertValidationHandler( ){ }; + + /** This function is provided a vector of X509 certificates encoded in base64, with + the first certificate being the one to validate, and the others are the issuers + chain. + + The result will be stored in the session object to avoid asking several times + to validate the same certificate. + + \result true if the certificate should be ignored, false to fail the request. + */ + virtual bool validateCertificate( std::vector< std::string > certificatesChain ) = 0; + }; + typedef boost::shared_ptr< CertValidationHandler > CertValidationHandlerPtr; + + typedef void(*CurlInitProtocolsFunction)(CURL *); + + class LIBCMIS_API SessionFactory + { + private: + + static AuthProviderPtr s_authProvider; + + static std::string s_proxy; + static std::string s_noProxy; + static std::string s_proxyUser; + static std::string s_proxyPass; + + static OAuth2AuthCodeProvider s_oauth2AuthCodeProvider; + + static CertValidationHandlerPtr s_certValidationHandler; + + public: + + static void setAuthenticationProvider( AuthProviderPtr provider ) { s_authProvider = provider; } + static AuthProviderPtr getAuthenticationProvider( ) { return s_authProvider; } + + static void setOAuth2AuthCodeProvider( OAuth2AuthCodeProvider provider ) { s_oauth2AuthCodeProvider = provider; } + static OAuth2AuthCodeProvider getOAuth2AuthCodeProvider( ) { return s_oauth2AuthCodeProvider; } + + /** Set the handler to ask the user what to do with invalid SSL certificates. If not set, + every invalid certificate will raise an exception. + */ + static void setCertificateValidationHandler( CertValidationHandlerPtr handler ) { s_certValidationHandler = handler; } + static CertValidationHandlerPtr getCertificateValidationHandler( ) { return s_certValidationHandler; } + + static void setCurlInitProtocolsFunction(CurlInitProtocolsFunction); + + static void setProxySettings( std::string proxy, + std::string noProxy, + std::string proxyUser, + std::string proxyPass ); + + static const std::string& getProxy() { return s_proxy; } + static const std::string& getNoProxy() { return s_noProxy; } + static const std::string& getProxyUser() { return s_proxyUser; } + static const std::string& getProxyPass() { return s_proxyPass; } + + /** Create a session from the given parameters. The binding type is automatically + detected based on the provided URL. + + The resulting pointer should be deleted by the caller. + */ + static Session* createSession( std::string bindingUrl, + std::string username = std::string( ), + std::string password = std::string( ), + std::string repositoryId = std::string( ), + bool noSslCheck = false, + OAuth2DataPtr oauth2 = OAuth2DataPtr(), bool verbose = false ); + + /** + Gets the informations of the repositories on the server. + + \deprecated + Since libcmis 0.4.0, this helper function simply creates a session + using the createSession function with no repository and then calls + getRepositories on the resulting session. + Kept only for backward API compatibility. + */ + static std::vector< RepositoryPtr > getRepositories( std::string bindingUrl, + std::string username = std::string( ), + std::string password = std::string( ), + bool verbose = false ); + }; +} + +#endif diff --git a/inc/libcmis/session.hxx b/inc/libcmis/session.hxx new file mode 100644 index 0000000..ec95ab4 --- /dev/null +++ b/inc/libcmis/session.hxx @@ -0,0 +1,103 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SESSION_HXX_ +#define _SESSION_HXX_ + +#include <vector> +#include <string> +#include <boost/shared_ptr.hpp> + +#include "libcmis/libcmis-api.h" +#include "libcmis/object-type.hxx" +#include "libcmis/object.hxx" +#include "libcmis/folder.hxx" +#include "libcmis/repository.hxx" + +namespace libcmis +{ + class LIBCMIS_API Session + { + public: + + virtual ~Session() { }; + + /** Get the current repository. + */ + virtual RepositoryPtr getRepository( ) = 0; + + virtual std::vector< RepositoryPtr > getRepositories( ) = 0; + + /** Change the current repository. + + \return + false if no repository with the provided id can be found on the server, + true otherwise + */ + virtual bool setRepository( std::string repositoryId ) = 0; + + /** Get the Root folder of the repository + */ + virtual FolderPtr getRootFolder()= 0; + + /** Get a CMIS object from its ID. + */ + virtual ObjectPtr getObject( std::string id ) = 0; + + /** Get a CMIS object from one of its path. + */ + virtual ObjectPtr getObjectByPath( std::string path ) = 0; + + /** Get a CMIS folder from its ID. + */ + virtual libcmis::FolderPtr getFolder( std::string id ) = 0; + + /** Get a CMIS object type from its ID. + */ + virtual ObjectTypePtr getType( std::string id ) = 0; + + /** Get all the CMIS base object types known by the server. + */ + virtual std::vector< ObjectTypePtr > getBaseTypes( ) = 0; + + /** Enable or disable the SSL certificate verification. + + By default, SSL certificates are verified and errors are thrown in case of + one is invalid. The user may decide to ignore the checks for this CMIS session + to workaround self-signed certificates or other similar problems. + + As each session only handles the connection to one CMIS server, it should + concern only one SSL certificate and should provide the same feature as the + certificate exception feature available on common web browser. + */ + virtual void setNoSSLCertificateCheck( bool noCheck ) = 0; + + virtual std::string getRefreshToken() { return ""; }; + }; +} + +#endif diff --git a/inc/libcmis/xml-utils.hxx b/inc/libcmis/xml-utils.hxx new file mode 100644 index 0000000..929385e --- /dev/null +++ b/inc/libcmis/xml-utils.hxx @@ -0,0 +1,166 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _XML_UTILS_HXX_ +#define _XML_UTILS_HXX_ + +#include <map> +#include <ostream> +#include <sstream> +#include <string> + +#include <boost/date_time.hpp> +#include <libxml/tree.h> +#include <libxml/xpathInternals.h> +#include <libxml/xmlwriter.h> + +#include "libcmis/exception.hxx" +#include "libcmis/libcmis-api.h" + +#define NS_CMIS_PREFIX "cmis" +#define NS_CMISRA_PREFIX "cmisra" +#define NS_SOAP_ENV_PREFIX "soap-env" +#define NS_CMIS_URL "http://docs.oasis-open.org/ns/cmis/core/200908/" +#define NS_CMISRA_URL "http://docs.oasis-open.org/ns/cmis/restatom/200908/" +#define NS_CMISM_URL "http://docs.oasis-open.org/ns/cmis/messaging/200908/" +#define NS_CMISW_URL "http://docs.oasis-open.org/ns/cmis/ws/200908/" +#define NS_APP_URL "http://www.w3.org/2007/app" +#define NS_ATOM_URL "http://www.w3.org/2005/Atom" +#define NS_SOAP_URL "http://schemas.xmlsoap.org/wsdl/soap/" +#define NS_SOAP_ENV_URL "http://schemas.xmlsoap.org/soap/envelope/" + +#define LIBCURL_VERSION_VALUE ( \ + ( LIBCURL_VERSION_MAJOR << 16 ) | ( LIBCURL_VERSION_MINOR << 8 ) | ( LIBCURL_VERSION_PATCH ) \ +) + +namespace libcmis +{ + /** Class used to decode a stream. + + An instance of this class can hold remaining un-decoded data to use + for a future decode call. + */ + class LIBCMIS_API EncodedData + { + private: + xmlTextWriterPtr m_writer; + FILE* m_stream; + std::ostream* m_outStream; + + std::string m_encoding; + bool m_decode; + unsigned long m_pendingValue; + int m_pendingRank; + size_t m_missingBytes; + + public: + EncodedData( FILE* stream ); + EncodedData( std::ostream* stream ); + EncodedData( const EncodedData& rCopy ); + EncodedData( xmlTextWriterPtr writer ); + + EncodedData& operator=( const EncodedData& rCopy ); + + void setEncoding( std::string encoding ) { m_encoding = encoding; } + void decode( void* buf, size_t size, size_t nmemb ); + void encode( void* buf, size_t size, size_t nmemb ); + void finish( ); + + private: + void write( void* buf, size_t size, size_t nmemb ); + void decodeBase64( const char* buf, size_t len ); + void encodeBase64( const char* buf, size_t len ); + }; + + class LIBCMIS_API HttpResponse + { + private: + std::map< std::string, std::string > m_headers; + boost::shared_ptr< std::stringstream > m_stream; + boost::shared_ptr< EncodedData > m_data; + + public: + HttpResponse( ); + ~HttpResponse( ) { }; + + std::map< std::string, std::string >& getHeaders( ) { return m_headers; } + boost::shared_ptr< EncodedData > getData( ) { return m_data; } + boost::shared_ptr< std::stringstream > getStream( ) { return m_stream; } + }; + typedef boost::shared_ptr< HttpResponse > HttpResponsePtr; + + LIBCMIS_API void registerNamespaces( xmlXPathContextPtr xpathCtx ); + + /** Register the CMIS and WSDL / SOAP namespaces + */ + LIBCMIS_API void registerCmisWSNamespaces( xmlXPathContextPtr xpathCtx ); + + /** Register only the WSD / SOAP namespaces. + */ + LIBCMIS_API void registerSoapNamespaces( xmlXPathContextPtr xpathCtx ); + + LIBCMIS_API std::string getXPathValue( xmlXPathContextPtr xpathCtx, std::string req ); + + LIBCMIS_API xmlDocPtr wrapInDoc( xmlNodePtr entryNode ); + + /** Utility extracting an attribute value from an Xml Node, + based on the attribute name. If the defaultValue is NULL and + the attribute can't be found then throw an exception. + */ + LIBCMIS_API std::string getXmlNodeAttributeValue( xmlNodePtr node, + const char* attributeName, + const char* defaultValue = NULL ); + + /** Parse a xsd:dateTime string and return the corresponding UTC posix time. + */ + LIBCMIS_API boost::posix_time::ptime parseDateTime( std::string dateTimeStr ); + + /// Write a UTC time object to an xsd:dateTime string + LIBCMIS_API std::string writeDateTime( boost::posix_time::ptime time ); + + LIBCMIS_API bool parseBool( std::string str ); + + LIBCMIS_API long parseInteger( std::string str ); + + LIBCMIS_API double parseDouble( std::string str ); + + /** Trim spaces on the left and right of a string. + */ + LIBCMIS_API std::string trim( const std::string& str ); + + LIBCMIS_API std::string base64encode( const std::string& str ); + + LIBCMIS_API std::string sha1( const std::string& str ); + + LIBCMIS_API int stringstream_write_callback(void * context, const char * s, int len); + + LIBCMIS_API std::string escape( std::string str ); + + LIBCMIS_API std::string unescape( std::string str ); +} + +#endif diff --git a/inc/libcmis/xmlserializable.hxx b/inc/libcmis/xmlserializable.hxx new file mode 100644 index 0000000..bd7c7c2 --- /dev/null +++ b/inc/libcmis/xmlserializable.hxx @@ -0,0 +1,48 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _XMLSERIALIZABLE_HXX_ +#define _XMLSERIALIZABLE_HXX_ + +#include <libxml/xmlwriter.h> + +#include "libcmis/libcmis-api.h" + +namespace libcmis +{ + + /// Interface for objects dumpable as XML + class LIBCMIS_API XmlSerializable + { + public: + virtual ~XmlSerializable( ) { } + + virtual void toXml( xmlTextWriterPtr writer ) = 0; + }; +} + +#endif diff --git a/lgtm.yml b/lgtm.yml new file mode 100644 index 0000000..998c2dd --- /dev/null +++ b/lgtm.yml @@ -0,0 +1,9 @@ +path_classifiers: + library: + - src/inc + - src/libcmis + - src/libcmis-c + test: + - qa + tool: + - src/cmis-client.cxx diff --git a/libcmis-c.pc.in b/libcmis-c.pc.in new file mode 100644 index 0000000..9590d12 --- /dev/null +++ b/libcmis-c.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libcmis +Description: CMIS protocol C client library +Version: @VERSION@ +Requires: libcmis-@LIBCMIS_API_VERSION@ libxml-2.0 +Requires.private: libcurl +Libs: -L${libdir} -lcmis-c-@LIBCMIS_API_VERSION@ +Cflags: -I${includedir}/libcmis-c-@LIBCMIS_API_VERSION@ + diff --git a/libcmis.pc.in b/libcmis.pc.in new file mode 100644 index 0000000..c9307f1 --- /dev/null +++ b/libcmis.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libcmis +Description: CMIS protocol client library +Version: @VERSION@ +Requires: libcurl libxml-2.0 +Libs: -L${libdir} -lcmis-@LIBCMIS_API_VERSION@ +Cflags: -I${includedir}/libcmis-@LIBCMIS_API_VERSION@ + diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 0000000..acc0db2 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,982 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com> +# Copyright (c) 2012 Zack Weinberg <zackw@panix.com> +# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu> +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com> +# Copyright (c) 2015 Paul Norman <penorman@mac.com> +# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu> +# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AX_REQUIRE_DEFINED([AC_MSG_WARN]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) + m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L && !(defined _MSC_VER) + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template <typename T> + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check<void> single_type; + typedef check<check<void>> double_type; + typedef check<check<check<void>>> triple_type; + typedef check<check<check<check<void>>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same<T, T> + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same<int, decltype(0)>::value == true, ""); + static_assert(is_same<int, decltype(c)>::value == false, ""); + static_assert(is_same<int, decltype(v)>::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same<int, decltype(ac)>::value == true, ""); + static_assert(is_same<int, decltype(av)>::value == true, ""); + static_assert(is_same<int, decltype(sumi)>::value == true, ""); + static_assert(is_same<int, decltype(sumf)>::value == false, ""); + static_assert(is_same<int, decltype(add(c, v))>::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template <int...> + struct sum; + + template <int N0, int... N1toN> + struct sum<N0, N1toN...> + { + static constexpr auto value = N0 + sum<N1toN...>::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template<typename T> + using member = typename T::member_type; + + template<typename T> + void func(...) {} + + template<typename T> + void func(member<T>*) {} + + void test(); + + void test() { func<foo>(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same<T, T> + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same<int, decltype(f(x))>::value, ""); + static_assert(is_same<int&, decltype(g(x))>::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include <initializer_list> +#include <utility> +#include <type_traits> + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template<typename... Args> + int multiply(Args... args) + { + return (args * ... * 1); + } + + template<typename... Args> + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value); + static_assert(std::is_same<int, decltype(bar)>::value); + } + + namespace test_typename_in_template_template_parameter + { + + template<template<typename> typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template <bool cond> + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template <typename T1, typename T2> + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template <auto n> + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair<int, int> pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair<int, int>& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template<typename T> + Bad + f(T*, T*); + + template<typename T1, typename T2> + Good + f(T1*, T2*); + + static_assert (std::is_same_v<Good, decltype(f(g1, g2))>); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template<class T> void f(T) + {} + + template<class T> inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 0000000..1733fd8 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,39 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++11. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com> +# Copyright (c) 2012 Zack Weinberg <zackw@panix.com> +# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu> +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com> +# Copyright (c) 2015 Paul Norman <penorman@mac.com> +# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 18 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) diff --git a/m4/ax_gcc_func_attribute.m4 b/m4/ax_gcc_func_attribute.m4 new file mode 100644 index 0000000..098c9aa --- /dev/null +++ b/m4/ax_gcc_func_attribute.m4 @@ -0,0 +1,238 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_FUNC_ATTRIBUTE(ATTRIBUTE) +# +# DESCRIPTION +# +# This macro checks if the compiler supports one of GCC's function +# attributes; many other compilers also provide function attributes with +# the same syntax. Compiler warnings are used to detect supported +# attributes as unsupported ones are ignored by default so quieting +# warnings when using this macro will yield false positives. +# +# The ATTRIBUTE parameter holds the name of the attribute to be checked. +# +# If ATTRIBUTE is supported define HAVE_FUNC_ATTRIBUTE_<ATTRIBUTE>. +# +# The macro caches its result in the ax_cv_have_func_attribute_<attribute> +# variable. +# +# The macro currently supports the following function attributes: +# +# alias +# aligned +# alloc_size +# always_inline +# artificial +# cold +# const +# constructor +# constructor_priority for constructor attribute with priority +# deprecated +# destructor +# dllexport +# dllimport +# error +# externally_visible +# fallthrough +# flatten +# format +# format_arg +# gnu_inline +# hot +# ifunc +# leaf +# malloc +# noclone +# noinline +# nonnull +# noreturn +# nothrow +# optimize +# pure +# sentinel +# sentinel_position +# unused +# used +# visibility +# warning +# warn_unused_result +# weak +# weakref +# +# Unsupported function attributes will be tested with a prototype +# returning an int and not accepting any arguments and the result of the +# check might be wrong or meaningless so use with care. +# +# LICENSE +# +# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 9 + +AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ + AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1]) + + AC_CACHE_CHECK([for __attribute__(($1))], [ac_var], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + m4_case([$1], + [alias], [ + int foo( void ) { return 0; } + int bar( void ) __attribute__(($1("foo"))); + ], + [aligned], [ + int foo( void ) __attribute__(($1(32))); + ], + [alloc_size], [ + void *foo(int a) __attribute__(($1(1))); + ], + [always_inline], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [artificial], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [cold], [ + int foo( void ) __attribute__(($1)); + ], + [const], [ + int foo( void ) __attribute__(($1)); + ], + [constructor_priority], [ + int foo( void ) __attribute__((__constructor__(65535/2))); + ], + [constructor], [ + int foo( void ) __attribute__(($1)); + ], + [deprecated], [ + int foo( void ) __attribute__(($1(""))); + ], + [destructor], [ + int foo( void ) __attribute__(($1)); + ], + [dllexport], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [dllimport], [ + int foo( void ) __attribute__(($1)); + ], + [error], [ + int foo( void ) __attribute__(($1(""))); + ], + [externally_visible], [ + int foo( void ) __attribute__(($1)); + ], + [fallthrough], [ + int foo( void ) {switch (0) { case 1: __attribute__(($1)); case 2: break ; }}; + ], + [flatten], [ + int foo( void ) __attribute__(($1)); + ], + [format], [ + int foo(const char *p, ...) __attribute__(($1(printf, 1, 2))); + ], + [format_arg], [ + char *foo(const char *p) __attribute__(($1(1))); + ], + [gnu_inline], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [hot], [ + int foo( void ) __attribute__(($1)); + ], + [ifunc], [ + int my_foo( void ) { return 0; } + static int (*resolve_foo(void))(void) { return my_foo; } + int foo( void ) __attribute__(($1("resolve_foo"))); + ], + [leaf], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [malloc], [ + void *foo( void ) __attribute__(($1)); + ], + [noclone], [ + int foo( void ) __attribute__(($1)); + ], + [noinline], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [nonnull], [ + int foo(char *p) __attribute__(($1(1))); + ], + [noreturn], [ + void foo( void ) __attribute__(($1)); + ], + [nothrow], [ + int foo( void ) __attribute__(($1)); + ], + [optimize], [ + __attribute__(($1(3))) int foo( void ) { return 0; } + ], + [pure], [ + int foo( void ) __attribute__(($1)); + ], + [sentinel], [ + int foo(void *p, ...) __attribute__(($1)); + ], + [sentinel_position], [ + int foo(void *p, ...) __attribute__(($1(1))); + ], + [returns_nonnull], [ + void *foo( void ) __attribute__(($1)); + ], + [unused], [ + int foo( void ) __attribute__(($1)); + ], + [used], [ + int foo( void ) __attribute__(($1)); + ], + [visibility], [ + int foo_def( void ) __attribute__(($1("default"))); + int foo_hid( void ) __attribute__(($1("hidden"))); + int foo_int( void ) __attribute__(($1("internal"))); + int foo_pro( void ) __attribute__(($1("protected"))); + ], + [warning], [ + int foo( void ) __attribute__(($1(""))); + ], + [warn_unused_result], [ + int foo( void ) __attribute__(($1)); + ], + [weak], [ + int foo( void ) __attribute__(($1)); + ], + [weakref], [ + static int foo( void ) { return 0; } + static int bar( void ) __attribute__(($1("foo"))); + ], + [ + m4_warn([syntax], [Unsupported attribute $1, the test may fail]) + int foo( void ) __attribute__(($1)); + ] + )], []) + ], + dnl GCC doesn't exit with an error if an unknown attribute is + dnl provided but only outputs a warning, so accept the attribute + dnl only if no warning were issued. + [AS_IF([test -s conftest.err], + [AS_VAR_SET([ac_var], [no])], + [AS_VAR_SET([ac_var], [yes])])], + [AS_VAR_SET([ac_var], [no])]) + ]) + + AS_IF([test yes = AS_VAR_GET([ac_var])], + [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_FUNC_ATTRIBUTE_$1), 1, + [Define to 1 if the system has the `$1' function attribute])], []) + + AS_VAR_POPDEF([ac_var]) +]) diff --git a/m4/boost.m4 b/m4/boost.m4 new file mode 100644 index 0000000..2c1df68 --- /dev/null +++ b/m4/boost.m4 @@ -0,0 +1,1568 @@ +# boost.m4: Locate Boost headers and libraries for autoconf-based projects. +# Copyright (C) 2007-2011, 2014 Benoit Sigoure <tsuna@lrde.epita.fr> +# +# 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. +# +# Additional permission under section 7 of the GNU General Public +# License, version 3 ("GPLv3"): +# +# If you convey this file as part of a work that contains a +# configuration script generated by Autoconf, you may do so under +# terms of your choice. +# +# 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 <http://www.gnu.org/licenses/>. + +m4_define([_BOOST_SERIAL], [m4_translit([ +# serial 26 +], [# +], [])]) + +# Original sources can be found at http://github.com/tsuna/boost.m4 +# You can fetch the latest version of the script by doing: +# wget http://github.com/tsuna/boost.m4/raw/master/build-aux/boost.m4 + +# ------ # +# README # +# ------ # + +# This file provides several macros to use the various Boost libraries. +# The first macro is BOOST_REQUIRE. It will simply check if it's possible to +# find the Boost headers of a given (optional) minimum version and it will +# define BOOST_CPPFLAGS accordingly. It will add an option --with-boost to +# your configure so that users can specify non standard locations. +# If the user's environment contains BOOST_ROOT and --with-boost was not +# specified, --with-boost=$BOOST_ROOT is implicitly used. +# For more README and documentation, go to http://github.com/tsuna/boost.m4 +# Note: THESE MACROS ASSUME THAT YOU USE LIBTOOL. If you don't, don't worry, +# simply read the README, it will show you what to do step by step. + +m4_pattern_forbid([^_?(BOOST|Boost)_]) + + +# _BOOST_SED_CPP(SED-PROGRAM, PROGRAM, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# -------------------------------------------------------- +# Same as AC_EGREP_CPP, but leave the result in conftest.i. +# +# SED-PROGRAM is *not* overquoted, as in AC_EGREP_CPP. It is expanded +# in double-quotes, so escape your double quotes. +# +# It could be useful to turn this into a macro which extracts the +# value of any macro. +m4_define([_BOOST_SED_CPP], +[AC_LANG_PUSH([C++])dnl +AC_LANG_PREPROC_REQUIRE()dnl +AC_REQUIRE([AC_PROG_SED])dnl +AC_LANG_CONFTEST([AC_LANG_SOURCE([[$2]])]) +AS_IF([dnl eval is necessary to expand ac_cpp. +dnl Ultrix and Pyramid sh refuse to redirect output of eval, so use subshell. +dnl Beware of Windows end-of-lines, for instance if we are running +dnl some Windows programs under Wine. In that case, boost/version.hpp +dnl is certainly using "\r\n", but the regular Unix shell will only +dnl strip `\n' with backquotes, not the `\r'. This results in +dnl boost_cv_lib_version='1_37\r' for instance, which breaks +dnl everything else. +dnl Cannot use 'dnl' after [$4] because a trailing dnl may break AC_CACHE_CHECK +dnl +dnl Beware that GCC 5, when expanding macros, may embed # line directives +dnl a within single line: +dnl +dnl # 1 "conftest.cc" +dnl # 1 "<built-in>" +dnl # 1 "<command-line>" +dnl # 1 "conftest.cc" +dnl # 1 "/opt/local/include/boost/version.hpp" 1 3 +dnl # 2 "conftest.cc" 2 +dnl boost-lib-version = +dnl # 2 "conftest.cc" 3 +dnl "1_56" +dnl +dnl So get rid of the # and empty lines, and glue the remaining ones together. +(eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | + grep -v '#' | + grep -v '^[[[:space:]]]*$' | + tr -d '\r' | + tr -s '\n' ' ' | + $SED -n -e "$1" >conftest.i 2>&1], + [$3], + [$4]) +rm -rf conftest* +AC_LANG_POP([C++])dnl +])# _BOOST_SED_CPP + + + +# BOOST_REQUIRE([VERSION], [ACTION-IF-NOT-FOUND]) +# ----------------------------------------------- +# Look for Boost. If version is given, it must either be a literal of the form +# "X.Y.Z" where X, Y and Z are integers (the ".Z" part being optional) or a +# variable "$var". +# Defines the value BOOST_CPPFLAGS. This macro only checks for headers with +# the required version, it does not check for any of the Boost libraries. +# On # success, defines HAVE_BOOST. On failure, calls the optional +# ACTION-IF-NOT-FOUND action if one was supplied. +# Otherwise aborts with an error message. +AC_DEFUN_ONCE([BOOST_REQUIRE], +[AC_REQUIRE([AC_PROG_CXX])dnl +AC_REQUIRE([AC_PROG_GREP])dnl +echo "$as_me: this is boost.m4[]_BOOST_SERIAL" >&AS_MESSAGE_LOG_FD +boost_save_IFS=$IFS +boost_version_req=$1 +IFS=. +set x $boost_version_req 0 0 0 +IFS=$boost_save_IFS +shift +boost_version_req=`expr "$[1]" '*' 100000 + "$[2]" '*' 100 + "$[3]"` +boost_version_req_string=$[1].$[2].$[3] +AC_ARG_WITH([boost], + [AS_HELP_STRING([--with-boost=DIR], + [prefix of Boost $1 @<:@guess@:>@])])dnl +AC_ARG_VAR([BOOST_ROOT],[Location of Boost installation])dnl +# If BOOST_ROOT is set and the user has not provided a value to +# --with-boost, then treat BOOST_ROOT as if it the user supplied it. +if test x"$BOOST_ROOT" != x; then + if test x"$with_boost" = x; then + AC_MSG_NOTICE([Detected BOOST_ROOT; continuing with --with-boost=$BOOST_ROOT]) + with_boost=$BOOST_ROOT + else + AC_MSG_NOTICE([Detected BOOST_ROOT=$BOOST_ROOT, but overridden by --with-boost=$with_boost]) + fi +fi +AC_SUBST([DISTCHECK_CONFIGURE_FLAGS], + ["$DISTCHECK_CONFIGURE_FLAGS '--with-boost=$with_boost'"])dnl +boost_save_CPPFLAGS=$CPPFLAGS + AC_CACHE_CHECK([for Boost headers version >= $boost_version_req_string], + [boost_cv_inc_path], + [boost_cv_inc_path=no +AC_LANG_PUSH([C++])dnl +m4_pattern_allow([^BOOST_VERSION$])dnl + AC_LANG_CONFTEST([AC_LANG_PROGRAM([[#include <boost/version.hpp> +#if !defined BOOST_VERSION +# error BOOST_VERSION is not defined +#elif BOOST_VERSION < $boost_version_req +# error Boost headers version < $boost_version_req +#endif +]])]) + # If the user provided a value to --with-boost, use it and only it. + case $with_boost in #( + ''|yes) set x '' /opt/local/include /usr/local/include /opt/include \ + /usr/include C:/Boost/include;; #( + *) set x "$with_boost/include" "$with_boost";; + esac + shift + for boost_dir + do + # Without --layout=system, Boost (or at least some versions) installs + # itself in <prefix>/include/boost-<version>. This inner loop helps to + # find headers in such directories. + # + # Any ${boost_dir}/boost-x_xx directories are searched in reverse version + # order followed by ${boost_dir}. The final '.' is a sentinel for + # searching $boost_dir" itself. Entries are whitespace separated. + # + # I didn't indent this loop on purpose (to avoid over-indented code) + boost_layout_system_search_list=`cd "$boost_dir" 2>/dev/null \ + && ls -1 | "${GREP}" '^boost-' | sort -rn -t- -k2 \ + && echo .` + for boost_inc in $boost_layout_system_search_list + do + if test x"$boost_inc" != x.; then + boost_inc="$boost_dir/$boost_inc" + else + boost_inc="$boost_dir" # Uses sentinel in boost_layout_system_search_list + fi + if test x"$boost_inc" != x; then + # We are going to check whether the version of Boost installed + # in $boost_inc is usable by running a compilation that + # #includes it. But if we pass a -I/some/path in which Boost + # is not installed, the compiler will just skip this -I and + # use other locations (either from CPPFLAGS, or from its list + # of system include directories). As a result we would use + # header installed on the machine instead of the /some/path + # specified by the user. So in that precise case (trying + # $boost_inc), make sure the version.hpp exists. + # + # Use test -e as there can be symlinks. + test -e "$boost_inc/boost/version.hpp" || continue + CPPFLAGS="$CPPFLAGS -I$boost_inc" + fi + AC_COMPILE_IFELSE([], [boost_cv_inc_path=yes], [boost_cv_version=no]) + if test x"$boost_cv_inc_path" = xyes; then + if test x"$boost_inc" != x; then + boost_cv_inc_path=$boost_inc + fi + break 2 + fi + done + done +AC_LANG_POP([C++])dnl + ]) + case $boost_cv_inc_path in #( + no) + boost_errmsg="cannot find Boost headers version >= $boost_version_req_string" + m4_if([$2], [], [AC_MSG_ERROR([$boost_errmsg])], + [AC_MSG_NOTICE([$boost_errmsg])]) + $2 + ;;#( + yes) + BOOST_CPPFLAGS= + ;;#( + *) + AC_SUBST([BOOST_CPPFLAGS], ["-I$boost_cv_inc_path"])dnl + ;; + esac + if test x"$boost_cv_inc_path" != xno; then + AC_DEFINE([HAVE_BOOST], [1], + [Defined if the requested minimum BOOST version is satisfied]) + AC_CACHE_CHECK([for Boost's header version], + [boost_cv_lib_version], + [m4_pattern_allow([^BOOST_LIB_VERSION$])dnl + _BOOST_SED_CPP([[/^boost-lib-version = /{s///;s/[\" ]//g;p;q;}]], + [#include <boost/version.hpp> +boost-lib-version = BOOST_LIB_VERSION], + [boost_cv_lib_version=`cat conftest.i`])]) + # e.g. "134" for 1_34_1 or "135" for 1_35 + boost_major_version=`echo "$boost_cv_lib_version" | sed 's/_//;s/_.*//'` + case $boost_major_version in #( + '' | *[[!0-9]]*) + AC_MSG_ERROR([invalid value: boost_major_version='$boost_major_version']) + ;; + esac +fi +CPPFLAGS=$boost_save_CPPFLAGS +])# BOOST_REQUIRE + + +# BOOST_STATIC() +# -------------- +# Add the "--enable-static-boost" configure argument. If this argument is given +# on the command line, static versions of the libraries will be looked up. +AC_DEFUN([BOOST_STATIC], + [AC_ARG_ENABLE([static-boost], + [AS_HELP_STRING([--enable-static-boost], + [Prefer the static boost libraries over the shared ones [no]])], + [enable_static_boost=yes], + [enable_static_boost=no])])# BOOST_STATIC + + +# BOOST_FIND_HEADER([HEADER-NAME], [ACTION-IF-NOT-FOUND], [ACTION-IF-FOUND]) +# -------------------------------------------------------------------------- +# Wrapper around AC_CHECK_HEADER for Boost headers. Useful to check for +# some parts of the Boost library which are only made of headers and don't +# require linking (such as Boost.Foreach). +# +# Default ACTION-IF-NOT-FOUND: Fail with a fatal error unless Boost couldn't be +# found in the first place, in which case by default a notice is issued to the +# user. Presumably if we haven't died already it's because it's OK to not have +# Boost, which is why only a notice is issued instead of a hard error. +# +# Default ACTION-IF-FOUND: define the preprocessor symbol HAVE_<HEADER-NAME> in +# case of success # (where HEADER-NAME is written LIKE_THIS, e.g., +# HAVE_BOOST_FOREACH_HPP). +AC_DEFUN([BOOST_FIND_HEADER], +[AC_REQUIRE([BOOST_REQUIRE])dnl +if test x"$boost_cv_inc_path" = xno; then + m4_default([$2], [AC_MSG_NOTICE([Boost not available, not searching for $1])]) +else +AC_LANG_PUSH([C++])dnl +boost_save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +AC_CHECK_HEADER([$1], + [m4_default([$3], [AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have <$1>])])], + [m4_default([$2], [AC_MSG_ERROR([cannot find $1])])]) +CPPFLAGS=$boost_save_CPPFLAGS +AC_LANG_POP([C++])dnl +fi +])# BOOST_FIND_HEADER + + +# BOOST_FIND_LIBS([COMPONENT-NAME], [CANDIDATE-LIB-NAMES], +# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], +# [CXX-PROLOGUE]) +# -------------------------------------------------------------- +# Look for the Boost library COMPONENT-NAME (e.g., `thread', for +# libboost_thread) under the possible CANDIDATE-LIB-NAMES (e.g., +# "thread_win32 thread"). Check that HEADER-NAME works and check that +# libboost_LIB-NAME can link with the code CXX-TEST. The optional +# argument CXX-PROLOGUE can be used to include some C++ code before +# the `main' function. +# +# Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above). +# +# Boost libraries typically come compiled with several flavors (with different +# runtime options) so PREFERRED-RT-OPT is the preferred suffix. A suffix is one +# or more of the following letters: sgdpn (in that order). s = static +# runtime, d = debug build, g = debug/diagnostic runtime, p = STLPort build, +# n = (unsure) STLPort build without iostreams from STLPort (it looks like `n' +# must always be used along with `p'). Additionally, PREFERRED-RT-OPT can +# start with `mt-' to indicate that there is a preference for multi-thread +# builds. Some sample values for PREFERRED-RT-OPT: (nothing), mt, d, mt-d, gdp +# ... If you want to make sure you have a specific version of Boost +# (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro. +AC_DEFUN([BOOST_FIND_LIBS], +[AC_REQUIRE([BOOST_REQUIRE])dnl +AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])dnl +AC_REQUIRE([BOOST_STATIC])dnl +AC_REQUIRE([_BOOST_GUESS_WHETHER_TO_USE_MT])dnl +if test x"$boost_cv_inc_path" = xno; then + AC_MSG_NOTICE([Boost not available, not searching for the Boost $1 library]) +else +dnl The else branch is huge and wasn't intended on purpose. +AC_LANG_PUSH([C++])dnl +AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])dnl +AS_VAR_PUSHDEF([Boost_lib_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl +AS_VAR_PUSHDEF([Boost_lib_LDPATH], [boost_cv_lib_$1_LDPATH])dnl +AS_VAR_PUSHDEF([Boost_lib_LIBS], [boost_cv_lib_$1_LIBS])dnl +BOOST_FIND_HEADER([$4]) +boost_save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib], + [_BOOST_FIND_LIBS($@)]) +case $Boost_lib in #( + (no) _AC_MSG_LOG_CONFTEST + AC_MSG_ERROR([cannot find the flags to link with Boost $1]) + ;; +esac +AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])dnl +AC_SUBST(AS_TR_CPP([BOOST_$1_LDPATH]), [$Boost_lib_LDPATH])dnl +AC_SUBST([BOOST_LDPATH], [$Boost_lib_LDPATH])dnl +AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])dnl +CPPFLAGS=$boost_save_CPPFLAGS +AS_VAR_POPDEF([Boost_lib])dnl +AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl +AS_VAR_POPDEF([Boost_lib_LDPATH])dnl +AS_VAR_POPDEF([Boost_lib_LIBS])dnl +AC_LANG_POP([C++])dnl +fi +]) + + +# BOOST_FIND_LIB([LIB-NAME], +# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], +# [CXX-PROLOGUE]) +# -------------------------------------------------------------- +# Backward compatibility wrapper for BOOST_FIND_LIBS. +AC_DEFUN([BOOST_FIND_LIB], +[BOOST_FIND_LIBS([$1], $@)]) + + +# _BOOST_FIND_LIBS([LIB-NAME], [CANDIDATE-LIB-NAMES], +# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], +# [CXX-PROLOGUE]) +# -------------------------------------------------------------- +# Real implementation of BOOST_FIND_LIBS: rely on these local macros: +# Boost_lib, Boost_lib_LDFLAGS, Boost_lib_LDPATH, Boost_lib_LIBS +# +# The algorithm is as follows: first look for a given library name +# according to the user's PREFERRED-RT-OPT. For each library name, we +# prefer to use the ones that carry the tag (toolset name). Each +# library is searched through the various standard paths were Boost is +# usually installed. If we can't find the standard variants, we try +# to enforce -mt (for instance on MacOSX, libboost_thread.dylib +# doesn't exist but there's -obviously- libboost_thread-mt.dylib). +AC_DEFUN([_BOOST_FIND_LIBS], +[Boost_lib=no + case "$3" in #( + (mt | mt-) boost_mt=-mt; boost_rtopt=;; #( + (mt* | mt-*) boost_mt=-mt; boost_rtopt=`expr "X$3" : 'Xmt-*\(.*\)'`;; #( + (*) boost_mt=; boost_rtopt=$3;; + esac + if test $enable_static_boost = yes; then + boost_rtopt="s$boost_rtopt" + fi + # Find the proper debug variant depending on what we've been asked to find. + case $boost_rtopt in #( + (*d*) boost_rt_d=$boost_rtopt;; #( + (*[[sgpn]]*) # Insert the `d' at the right place (in between `sg' and `pn') + boost_rt_d=`echo "$boost_rtopt" | sed 's/\(s*g*\)\(p*n*\)/\1\2/'`;; #( + (*) boost_rt_d='-d';; + esac + # If the PREFERRED-RT-OPT are not empty, prepend a `-'. + test -n "$boost_rtopt" && boost_rtopt="-$boost_rtopt" + $boost_guess_use_mt && boost_mt=-mt + # Look for the abs path the static archive. + # $libext is computed by Libtool but let's make sure it's non empty. + test -z "$libext" && + AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?]) + boost_save_ac_objext=$ac_objext + # Generate the test file. + AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <$4> +$6], [$5])]) +dnl Optimization hacks: compiling C++ is slow, especially with Boost. What +dnl we're trying to do here is guess the right combination of link flags +dnl (LIBS / LDFLAGS) to use a given library. This can take several +dnl iterations before it succeeds and is thus *very* slow. So what we do +dnl instead is that we compile the code first (and thus get an object file, +dnl typically conftest.o). Then we try various combinations of link flags +dnl until we succeed to link conftest.o in an executable. The problem is +dnl that the various TRY_LINK / COMPILE_IFELSE macros of Autoconf always +dnl remove all the temporary files including conftest.o. So the trick here +dnl is to temporarily change the value of ac_objext so that conftest.o is +dnl preserved accross tests. This is obviously fragile and I will burn in +dnl hell for not respecting Autoconf's documented interfaces, but in the +dnl mean time, it optimizes the macro by a factor of 5 to 30. +dnl Another small optimization: the first argument of AC_COMPILE_IFELSE left +dnl empty because the test file is generated only once above (before we +dnl start the for loops). + AC_COMPILE_IFELSE([], + [ac_objext=do_not_rm_me_plz], + [AC_MSG_ERROR([cannot compile a test that uses Boost $1])]) + ac_objext=$boost_save_ac_objext + boost_failed_libs= +# Don't bother to ident the following nested for loops, only the 2 +# innermost ones matter. +for boost_lib_ in $2; do +for boost_tag_ in -$boost_cv_lib_tag ''; do +for boost_ver_ in -$boost_cv_lib_version ''; do +for boost_mt_ in $boost_mt -mt ''; do +for boost_rtopt_ in $boost_rtopt '' -d; do + for boost_lib in \ + boost_$boost_lib_$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \ + boost_$boost_lib_$boost_tag_$boost_rtopt_$boost_ver_ \ + boost_$boost_lib_$boost_tag_$boost_mt_$boost_ver_ \ + boost_$boost_lib_$boost_tag_$boost_ver_ + do + # Avoid testing twice the same lib + case $boost_failed_libs in #( + (*@$boost_lib@*) continue;; + esac + # If with_boost is empty, we'll search in /lib first, which is not quite + # right so instead we'll try to a location based on where the headers are. + boost_tmp_lib=$with_boost + test x"$with_boost" = x && boost_tmp_lib=${boost_cv_inc_path%/include} + for boost_ldpath in "$boost_tmp_lib/lib" '' \ + /opt/local/lib* /usr/local/lib* /opt/lib* /usr/lib* \ + "$with_boost" C:/Boost/lib /lib* + do + # Don't waste time with directories that don't exist. + if test x"$boost_ldpath" != x && test ! -e "$boost_ldpath"; then + continue + fi + boost_save_LDFLAGS=$LDFLAGS + # Are we looking for a static library? + case $boost_ldpath:$boost_rtopt_ in #( + (*?*:*s*) # Yes (Non empty boost_ldpath + s in rt opt) + Boost_lib_LIBS="$boost_ldpath/lib$boost_lib.$libext" + test -e "$Boost_lib_LIBS" || continue;; #( + (*) # No: use -lboost_foo to find the shared library. + Boost_lib_LIBS="-l$boost_lib";; + esac + boost_save_LIBS=$LIBS + LIBS="$Boost_lib_LIBS $LIBS" + test x"$boost_ldpath" != x && LDFLAGS="$LDFLAGS -L$boost_ldpath" +dnl First argument of AC_LINK_IFELSE left empty because the test file is +dnl generated only once above (before we start the for loops). + _BOOST_AC_LINK_IFELSE([], + [Boost_lib=yes], [Boost_lib=no]) + ac_objext=$boost_save_ac_objext + LDFLAGS=$boost_save_LDFLAGS + LIBS=$boost_save_LIBS + if test x"$Boost_lib" = xyes; then + # Check or used cached result of whether or not using -R or + # -rpath makes sense. Some implementations of ld, such as for + # Mac OSX, require -rpath but -R is the flag known to work on + # other systems. https://github.com/tsuna/boost.m4/issues/19 + AC_CACHE_VAL([boost_cv_rpath_link_ldflag], + [case $boost_ldpath in + '') # Nothing to do. + boost_cv_rpath_link_ldflag= + boost_rpath_link_ldflag_found=yes;; + *) + for boost_cv_rpath_link_ldflag in -Wl,-R, -Wl,-rpath,; do + LDFLAGS="$boost_save_LDFLAGS -L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" + LIBS="$boost_save_LIBS $Boost_lib_LIBS" + _BOOST_AC_LINK_IFELSE([], + [boost_rpath_link_ldflag_found=yes + break], + [boost_rpath_link_ldflag_found=no]) + done + ;; + esac + AS_IF([test "x$boost_rpath_link_ldflag_found" != "xyes"], + [AC_MSG_ERROR([Unable to determine whether to use -R or -rpath])]) + LDFLAGS=$boost_save_LDFLAGS + LIBS=$boost_save_LIBS + ]) + test x"$boost_ldpath" != x && + Boost_lib_LDFLAGS="-L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" + Boost_lib_LDPATH="$boost_ldpath" + break 7 + else + boost_failed_libs="$boost_failed_libs@$boost_lib@" + fi + done + done +done +done +done +done +done # boost_lib_ +rm -f conftest.$ac_objext +]) + + + +# --------------------------------------- # +# Checks for the various Boost libraries. # +# --------------------------------------- # + +# List of boost libraries: http://www.boost.org/libs/libraries.htm +# The page http://beta.boost.org/doc/libs is useful: it gives the first release +# version of each library (among other things). + +# BOOST_DEFUN(LIBRARY, CODE) +# -------------------------- +# Define BOOST_<LIBRARY-UPPERCASE> as a macro that runs CODE. +# +# Use indir to avoid the warning on underquoted macro name given to AC_DEFUN. +m4_define([BOOST_DEFUN], +[m4_indir([AC_DEFUN], + m4_toupper([BOOST_$1]), +[m4_pushdef([BOOST_Library], [$1])dnl +$2 +m4_popdef([BOOST_Library])dnl +]) +]) + +# BOOST_ARRAY() +# ------------- +# Look for Boost.Array +BOOST_DEFUN([Array], +[BOOST_FIND_HEADER([boost/array.hpp])]) + + +# BOOST_ASIO() +# ------------ +# Look for Boost.Asio (new in Boost 1.35). +BOOST_DEFUN([Asio], +[AC_REQUIRE([BOOST_SYSTEM])dnl +BOOST_FIND_HEADER([boost/asio.hpp])]) + + +# BOOST_ASSIGN() +# ------------- +# Look for Boost.Assign +BOOST_DEFUN([Assign], +[BOOST_FIND_HEADER([boost/assign.hpp])]) + + +# BOOST_BIND() +# ------------ +# Look for Boost.Bind. +BOOST_DEFUN([Bind], +[BOOST_FIND_HEADER([boost/bind.hpp])]) + + +# BOOST_CHRONO() +# -------------- +# Look for Boost.Chrono. +BOOST_DEFUN([Chrono], +[# Do we have to check for Boost.System? This link-time dependency was +# added as of 1.35.0. If we have a version <1.35, we must not attempt to +# find Boost.System as it didn't exist by then. +if test $boost_major_version -ge 135; then + BOOST_SYSTEM([$1]) +fi # end of the Boost.System check. +boost_filesystem_save_LIBS=$LIBS +boost_filesystem_save_LDFLAGS=$LDFLAGS +m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_SYSTEM_LIBS" +LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" +BOOST_FIND_LIB([chrono], [$1], + [boost/chrono.hpp], + [boost::chrono::thread_clock d;]) +if test $enable_static_boost = yes && test $boost_major_version -ge 135; then + BOOST_CHRONO_LIBS="$BOOST_CHRONO_LIBS $BOOST_SYSTEM_LIBS" +fi +LIBS=$boost_filesystem_save_LIBS +LDFLAGS=$boost_filesystem_save_LDFLAGS +])# BOOST_CHRONO + + +# BOOST_CONTEXT([PREFERRED-RT-OPT]) +# ----------------------------------- +# Look for Boost.Context. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +# +# * This library was introduced in Boost 1.51.0 +# * The signatures of make_fcontext() and jump_fcontext were changed in 1.56.0 +# * A dependency on boost_thread appears in 1.57.0 +BOOST_DEFUN([Context], +[boost_context_save_LIBS=$LIBS + boost_context_save_LDFLAGS=$LDFLAGS +if test $boost_major_version -ge 157; then + BOOST_THREAD([$1]) + m4_pattern_allow([^BOOST_THREAD_(LIBS|LDFLAGS)$])dnl + LIBS="$LIBS $BOOST_THREAD_LIBS" + LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS" +fi +BOOST_FIND_LIB([context], [$1], + [boost/context/all.hpp],[[ + +// creates a stack +void * stack_pointer = new void*[4096]; +std::size_t const size = sizeof(void*[4096]); + +#if BOOST_VERSION <= 105100 +ctx::make_fcontext(&fc, f); +return ctx::jump_fcontext(&fcm, &fc, 3) == 6; + +#else + +fc = ctx::make_fcontext(stack_pointer, size, f); +return ctx::jump_fcontext(&fcm, fc, 3) == 6; + +#endif + + +]],[dnl + +#include <boost/version.hpp> +#if BOOST_VERSION <= 105100 + +namespace ctx = boost::ctx; + +static ctx::fcontext_t fcm, fc; + +static void f(intptr_t i) { + ctx::jump_fcontext(&fc, &fcm, i * 2); +} + +#elif BOOST_VERSION <= 105500 + +namespace ctx = boost::context; + +// context +static ctx::fcontext_t fcm, *fc; + +// context-function +static void f(intptr_t i) { + ctx::jump_fcontext(fc, &fcm, i * 2); +} + +#else + +namespace ctx = boost::context; + +// context +static ctx::fcontext_t fcm, fc; + +// context-function +static void f(intptr_t i) { + ctx::jump_fcontext(&fc, fcm, i * 2); +} +#endif +]) +LIBS=$boost_context_save_LIBS +LDFLAGS=$boost_context_save_LDFLAGS +])# BOOST_CONTEXT + + +# BOOST_CONVERSION() +# ------------------ +# Look for Boost.Conversion (cast / lexical_cast) +BOOST_DEFUN([Conversion], +[BOOST_FIND_HEADER([boost/cast.hpp]) +BOOST_FIND_HEADER([boost/lexical_cast.hpp]) +])# BOOST_CONVERSION + + +# BOOST_COROUTINE([PREFERRED-RT-OPT]) +# ----------------------------------- +# Look for Boost.Coroutine. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. This library was introduced in Boost +# 1.53.0 +BOOST_DEFUN([Coroutine], +[ +boost_coroutine_save_LIBS=$LIBS +boost_coroutine_save_LDFLAGS=$LDFLAGS +# Link-time dependency from coroutine to context +BOOST_CONTEXT([$1]) +# Starting from Boost 1.55 a dependency on Boost.System is added +if test $boost_major_version -ge 155; then + BOOST_SYSTEM([$1]) +fi +m4_pattern_allow([^BOOST_(CONTEXT|SYSTEM)_(LIBS|LDFLAGS)]) +LIBS="$LIBS $BOOST_CONTEXT_LIBS $BOOST_SYSTEM_LIBS" +LDFLAGS="$LDFLAGS $BOOST_CONTEXT_LDFLAGS" + +# in 1.53 coroutine was a header only library +if test $boost_major_version -eq 153; then + BOOST_FIND_HEADER([boost/coroutine/coroutine.hpp]) +else + BOOST_FIND_LIB([coroutine], [$1], + [boost/coroutine/coroutine.hpp], + [ + #include <boost/version.hpp> + #if BOOST_VERSION <= 105500 + boost::coroutines::coroutine<int(int)> coro; coro.get(); + #else + boost::coroutines::asymmetric_coroutine<int>::pull_type coro; coro.get(); + #endif + ]) +fi +# Link-time dependency from coroutine to context, existed only in 1.53, in 1.54 +# coroutine doesn't use context from its headers but from its library. +if test $boost_major_version -eq 153 || test $enable_static_boost = yes && test $boost_major_version -ge 154; then + BOOST_COROUTINE_LIBS="$BOOST_COROUTINE_LIBS $BOOST_CONTEXT_LIBS" + BOOST_COROUTINE_LDFLAGS="$BOOST_COROUTINE_LDFLAGS $BOOST_CONTEXT_LDFLAGS" +fi +if test $enable_static_boost = yes && test $boost_major_version -ge 155; then + BOOST_COROUTINE_LIBS="$BOOST_COROUTINE_LIBS $BOOST_SYSTEM_LIBS" + BOOST_COROUTINE_LDFLAGS="$BOOST_COROUTINE_LDFLAGS $BOOST_SYSTEM_LDFLAGS" +fi +LIBS=$boost_coroutine_save_LIBS +LDFLAGS=$boost_coroutine_save_LDFLAGS +])# BOOST_COROUTINE + + +# BOOST_CRC() +# ----------- +# Look for Boost.CRC +BOOST_DEFUN([CRC], +[BOOST_FIND_HEADER([boost/crc.hpp]) +])# BOOST_CRC + + +# BOOST_DATE_TIME([PREFERRED-RT-OPT]) +# ----------------------------------- +# Look for Boost.Date_Time. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Date_Time], +[BOOST_FIND_LIB([date_time], [$1], + [boost/date_time/posix_time/posix_time.hpp], + [boost::posix_time::ptime t;]) +])# BOOST_DATE_TIME + + +# BOOST_FILESYSTEM([PREFERRED-RT-OPT]) +# ------------------------------------ +# Look for Boost.Filesystem. For the documentation of PREFERRED-RT-OPT, see +# the documentation of BOOST_FIND_LIB above. +# Do not check for boost/filesystem.hpp because this file was introduced in +# 1.34. +BOOST_DEFUN([Filesystem], +[# Do we have to check for Boost.System? This link-time dependency was +# added as of 1.35.0. If we have a version <1.35, we must not attempt to +# find Boost.System as it didn't exist by then. +if test $boost_major_version -ge 135; then + BOOST_SYSTEM([$1]) +fi # end of the Boost.System check. +boost_filesystem_save_LIBS=$LIBS +boost_filesystem_save_LDFLAGS=$LDFLAGS +m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_SYSTEM_LIBS" +LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" +BOOST_FIND_LIB([filesystem], [$1], + [boost/filesystem/path.hpp], [boost::filesystem::path p;]) +if test $enable_static_boost = yes && test $boost_major_version -ge 135; then + BOOST_FILESYSTEM_LIBS="$BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" +fi +LIBS=$boost_filesystem_save_LIBS +LDFLAGS=$boost_filesystem_save_LDFLAGS +])# BOOST_FILESYSTEM + + +# BOOST_FLYWEIGHT() +# ----------------- +# Look for Boost.Flyweight. +BOOST_DEFUN([Flyweight], +[dnl There's a hidden dependency on pthreads. +AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl +BOOST_FIND_HEADER([boost/flyweight.hpp]) +AC_SUBST([BOOST_FLYWEIGHT_LIBS], [$boost_cv_pthread_flag]) +]) + + +# BOOST_FOREACH() +# --------------- +# Look for Boost.Foreach. +BOOST_DEFUN([Foreach], +[BOOST_FIND_HEADER([boost/foreach.hpp])]) + + +# BOOST_FORMAT() +# -------------- +# Look for Boost.Format. +# Note: we can't check for boost/format/format_fwd.hpp because the header isn't +# standalone. It can't be compiled because it triggers the following error: +# boost/format/detail/config_macros.hpp:88: error: 'locale' in namespace 'std' +# does not name a type +BOOST_DEFUN([Format], +[BOOST_FIND_HEADER([boost/format.hpp])]) + + +# BOOST_FUNCTION() +# ---------------- +# Look for Boost.Function +BOOST_DEFUN([Function], +[BOOST_FIND_HEADER([boost/function.hpp])]) + + +# BOOST_GEOMETRY() +# ---------------- +# Look for Boost.Geometry (new since 1.47.0). +BOOST_DEFUN([Geometry], +[BOOST_FIND_HEADER([boost/geometry.hpp]) +])# BOOST_GEOMETRY + + +# BOOST_GRAPH([PREFERRED-RT-OPT]) +# ------------------------------- +# Look for Boost.Graphs. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Graph], +[boost_graph_save_LIBS=$LIBS +boost_graph_save_LDFLAGS=$LDFLAGS +# Link-time dependency from graph to regex was added as of 1.40.0. +if test $boost_major_version -ge 140; then + BOOST_REGEX([$1]) + m4_pattern_allow([^BOOST_REGEX_(LIBS|LDFLAGS)$])dnl + LIBS="$LIBS $BOOST_REGEX_LIBS" + LDFLAGS="$LDFLAGS $BOOST_REGEX_LDFLAGS" +fi +BOOST_FIND_LIB([graph], [$1], + [boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;]) +LIBS=$boost_graph_save_LIBS +LDFLAGS=$boost_graph_save_LDFLAGS +])# BOOST_GRAPH + + +# BOOST_IOSTREAMS([PREFERRED-RT-OPT]) +# ----------------------------------- +# Look for Boost.IOStreams. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([IOStreams], +[BOOST_FIND_LIB([iostreams], [$1], + [boost/iostreams/device/file_descriptor.hpp], + [boost::iostreams::file_descriptor fd; fd.close();]) +])# BOOST_IOSTREAMS + + +# BOOST_HASH() +# ------------ +# Look for Boost.Functional/Hash +BOOST_DEFUN([Hash], +[BOOST_FIND_HEADER([boost/functional/hash.hpp])]) + + +# BOOST_LAMBDA() +# -------------- +# Look for Boost.Lambda +BOOST_DEFUN([Lambda], +[BOOST_FIND_HEADER([boost/lambda/lambda.hpp])]) + + +# BOOST_LOCALE() +# -------------- +# Look for Boost.Locale +BOOST_DEFUN([Locale], +[ +boost_locale_save_LIBS=$LIBS +boost_locale_save_LDFLAGS=$LDFLAGS +# require SYSTEM for boost-1.50.0 and up +if test $boost_major_version -ge 150; then + BOOST_SYSTEM([$1]) + m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl + LIBS="$LIBS $BOOST_SYSTEM_LIBS" + LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" +fi # end of the Boost.System check. +BOOST_FIND_LIB([locale], [$1], + [boost/locale.hpp], + [[boost::locale::generator gen; std::locale::global(gen(""));]]) +LIBS=$boost_locale_save_LIBS +LDFLAGS=$boost_locale_save_LDFLAGS +])# BOOST_LOCALE + +# BOOST_LOG([PREFERRED-RT-OPT]) +# ----------------------------- +# Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Log], +[boost_log_save_LIBS=$LIBS +boost_log_save_LDFLAGS=$LDFLAGS +BOOST_SYSTEM([$1]) +BOOST_FILESYSTEM([$1]) +BOOST_DATE_TIME([$1]) +m4_pattern_allow([^BOOST_(SYSTEM|FILESYSTEM|DATE_TIME)_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_DATE_TIME_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" +LDFLAGS="$LDFLAGS $BOOST_DATE_TIME_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS $BOOST_SYSTEM_LDFLAGS" +BOOST_FIND_LIB([log], [$1], + [boost/log/core/core.hpp], + [boost::log::attribute a; a.get_value();]) +LIBS=$boost_log_save_LIBS +LDFLAGS=$boost_log_save_LDFLAGS +])# BOOST_LOG + + +# BOOST_LOG_SETUP([PREFERRED-RT-OPT]) +# ----------------------------------- +# Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Log_Setup], +[boost_log_setup_save_LIBS=$LIBS +boost_log_setup_save_LDFLAGS=$LDFLAGS +BOOST_LOG([$1]) +m4_pattern_allow([^BOOST_LOG_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_LOG_LIBS" +LDFLAGS="$LDFLAGS $BOOST_LOG_LDFLAGS" +BOOST_FIND_LIB([log_setup], [$1], + [boost/log/utility/setup/from_settings.hpp], + [boost::log::basic_settings<char> bs; bs.empty();]) +LIBS=$boost_log_setup_save_LIBS +LDFLAGS=$boost_log_setup_save_LDFLAGS +])# BOOST_LOG_SETUP + + +# BOOST_MATH() +# ------------ +# Look for Boost.Math +# TODO: This library isn't header-only but it comes in multiple different +# flavors that don't play well with BOOST_FIND_LIB (e.g, libboost_math_c99, +# libboost_math_c99f, libboost_math_c99l, libboost_math_tr1, +# libboost_math_tr1f, libboost_math_tr1l). This macro must be fixed to do the +# right thing anyway. +BOOST_DEFUN([Math], +[BOOST_FIND_HEADER([boost/math/special_functions.hpp])]) + + +# BOOST_MPI([PREFERRED-RT-OPT]) +# ------------------------------- +# Look for Boost MPI. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. Uses MPICXX variable if it is +# set, otherwise tries CXX +# +BOOST_DEFUN([MPI], +[boost_save_CXX=${CXX} +boost_save_CXXCPP=${CXXCPP} +if test x"${MPICXX}" != x; then + CXX=${MPICXX} + CXXCPP="${MPICXX} -E" +fi +BOOST_FIND_LIB([mpi], [$1], + [boost/mpi.hpp], + [int argc = 0; + char **argv = 0; + boost::mpi::environment env(argc,argv);]) +CXX=${boost_save_CXX} +CXXCPP=${boost_save_CXXCPP} +])# BOOST_MPI + + +# BOOST_MULTIARRAY() +# ------------------ +# Look for Boost.MultiArray +BOOST_DEFUN([MultiArray], +[BOOST_FIND_HEADER([boost/multi_array.hpp])]) + + +# BOOST_NUMERIC_UBLAS() +# -------------------------- +# Look for Boost.NumericUblas (Basic Linear Algebra) +BOOST_DEFUN([Numeric_Ublas], +[BOOST_FIND_HEADER([boost/numeric/ublas/vector.hpp]) +])# BOOST_NUMERIC_UBLAS + + +# BOOST_NUMERIC_CONVERSION() +# -------------------------- +# Look for Boost.NumericConversion (policy-based numeric conversion) +BOOST_DEFUN([Numeric_Conversion], +[BOOST_FIND_HEADER([boost/numeric/conversion/converter.hpp]) +])# BOOST_NUMERIC_CONVERSION + + +# BOOST_OPTIONAL() +# ---------------- +# Look for Boost.Optional +BOOST_DEFUN([Optional], +[BOOST_FIND_HEADER([boost/optional.hpp])]) + + +# BOOST_PREPROCESSOR() +# -------------------- +# Look for Boost.Preprocessor +BOOST_DEFUN([Preprocessor], +[BOOST_FIND_HEADER([boost/preprocessor/repeat.hpp])]) + + +# BOOST_RANGE() +# -------------------- +# Look for Boost.Range +BOOST_DEFUN([Range], +[BOOST_FIND_HEADER([boost/range/adaptors.hpp])]) + +# BOOST_UNORDERED() +# ----------------- +# Look for Boost.Unordered +BOOST_DEFUN([Unordered], +[BOOST_FIND_HEADER([boost/unordered_map.hpp])]) + + +# BOOST_UUID() +# ------------ +# Look for Boost.Uuid +BOOST_DEFUN([Uuid], +[BOOST_FIND_HEADER([boost/uuid/uuid.hpp])]) + + +# BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT]) +# ----------------------------------------- +# Look for Boost.Program_options. For the documentation of PREFERRED-RT-OPT, +# see the documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Program_Options], +[BOOST_FIND_LIB([program_options], [$1], + [boost/program_options.hpp], + [boost::program_options::options_description d("test");]) +])# BOOST_PROGRAM_OPTIONS + + + +# _BOOST_PYTHON_CONFIG(VARIABLE, FLAG) +# ------------------------------------ +# Save VARIABLE, and define it via `python-config --FLAG`. +# Substitute BOOST_PYTHON_VARIABLE. +m4_define([_BOOST_PYTHON_CONFIG], +[AC_SUBST([BOOST_PYTHON_$1], + [`python-config --$2 2>/dev/null`])dnl +boost_python_save_$1=$$1 +$1="$$1 $BOOST_PYTHON_$1"]) + + +# BOOST_PYTHON([PREFERRED-RT-OPT]) +# -------------------------------- +# Look for Boost.Python. For the documentation of PREFERRED-RT-OPT, +# see the documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Python], +[_BOOST_PYTHON_CONFIG([CPPFLAGS], [includes]) +_BOOST_PYTHON_CONFIG([LDFLAGS], [ldflags]) +_BOOST_PYTHON_CONFIG([LIBS], [libs]) +m4_pattern_allow([^BOOST_PYTHON_MODULE$])dnl +BOOST_FIND_LIBS([python], [python python3], [$1], + [boost/python.hpp], + [], [BOOST_PYTHON_MODULE(empty) {}]) +CPPFLAGS=$boost_python_save_CPPFLAGS +LDFLAGS=$boost_python_save_LDFLAGS +LIBS=$boost_python_save_LIBS +])# BOOST_PYTHON + + +# BOOST_REF() +# ----------- +# Look for Boost.Ref +BOOST_DEFUN([Ref], +[BOOST_FIND_HEADER([boost/ref.hpp])]) + + +# BOOST_REGEX([PREFERRED-RT-OPT]) +# ------------------------------- +# Look for Boost.Regex. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Regex], +[BOOST_FIND_LIB([regex], [$1], + [boost/regex.hpp], + [boost::regex exp("*"); boost::regex_match("foo", exp);]) +])# BOOST_REGEX + + +# BOOST_SERIALIZATION([PREFERRED-RT-OPT]) +# --------------------------------------- +# Look for Boost.Serialization. For the documentation of PREFERRED-RT-OPT, see +# the documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Serialization], +[BOOST_FIND_LIB([serialization], [$1], + [boost/archive/text_oarchive.hpp], + [std::ostream* o = 0; // Cheap way to get an ostream... + boost::archive::text_oarchive t(*o);]) +])# BOOST_SERIALIZATION + + +# BOOST_SIGNALS([PREFERRED-RT-OPT]) +# --------------------------------- +# Look for Boost.Signals. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Signals], +[BOOST_FIND_LIB([signals], [$1], + [boost/signal.hpp], + [boost::signal<void ()> s;]) +])# BOOST_SIGNALS + + +# BOOST_SIGNALS2() +# ---------------- +# Look for Boost.Signals2 (new since 1.39.0). +BOOST_DEFUN([Signals2], +[BOOST_FIND_HEADER([boost/signals2.hpp]) +])# BOOST_SIGNALS2 + + +# BOOST_SMART_PTR() +# ----------------- +# Look for Boost.SmartPtr +BOOST_DEFUN([Smart_Ptr], +[BOOST_FIND_HEADER([boost/scoped_ptr.hpp]) +BOOST_FIND_HEADER([boost/shared_ptr.hpp]) +]) + + +# BOOST_STATICASSERT() +# -------------------- +# Look for Boost.StaticAssert +BOOST_DEFUN([StaticAssert], +[BOOST_FIND_HEADER([boost/static_assert.hpp])]) + + +# BOOST_STRING_ALGO() +# ------------------- +# Look for Boost.StringAlgo +BOOST_DEFUN([String_Algo], +[BOOST_FIND_HEADER([boost/algorithm/string.hpp]) +]) + + +# BOOST_SYSTEM([PREFERRED-RT-OPT]) +# -------------------------------- +# Look for Boost.System. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. This library was introduced in Boost +# 1.35.0. +BOOST_DEFUN([System], +[BOOST_FIND_LIB([system], [$1], + [boost/system/error_code.hpp], + [boost::system::error_code e; e.clear();]) +])# BOOST_SYSTEM + + +# BOOST_TEST([PREFERRED-RT-OPT]) +# ------------------------------ +# Look for Boost.Test. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Test], +[m4_pattern_allow([^BOOST_CHECK$])dnl +BOOST_FIND_LIB([unit_test_framework], [$1], + [boost/test/unit_test.hpp], [BOOST_CHECK(2 == 2);], + [using boost::unit_test::test_suite; + test_suite* init_unit_test_suite(int argc, char ** argv) + { return NULL; }]) +])# BOOST_TEST + + +# BOOST_THREAD([PREFERRED-RT-OPT]) +# --------------------------------- +# Look for Boost.Thread. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Thread], +[dnl Having the pthread flag is required at least on GCC3 where +dnl boost/thread.hpp would complain if we try to compile without +dnl -pthread on GNU/Linux. +AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl +boost_thread_save_LIBS=$LIBS +boost_thread_save_LDFLAGS=$LDFLAGS +boost_thread_save_CPPFLAGS=$CPPFLAGS +# Link-time dependency from thread to system was added as of 1.49.0. +if test $boost_major_version -ge 149; then +BOOST_SYSTEM([$1]) +fi # end of the Boost.System check. +m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag" +LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" +CPPFLAGS="$CPPFLAGS $boost_cv_pthread_flag" + +# When compiling for the Windows platform, the threads library is named +# differently. This suffix doesn't exist in new versions of Boost, or +# possibly new versions of GCC on mingw I am assuming it's Boost's change for +# now and I am setting version to 1.48, for lack of knowledge as to when this +# change occurred. +if test $boost_major_version -lt 148; then + case $host_os in + (*mingw*) boost_thread_lib_ext=_win32;; + esac +fi +BOOST_FIND_LIBS([thread], [thread$boost_thread_lib_ext], + [$1], + [boost/thread.hpp], [boost::thread t; boost::mutex m;]) + +case $host_os in + (*mingw*) boost_thread_w32_socket_link=-lws2_32;; +esac + +BOOST_THREAD_LIBS="$BOOST_THREAD_LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag $boost_thread_w32_socket_link" +BOOST_THREAD_LDFLAGS="$BOOST_SYSTEM_LDFLAGS" +BOOST_CPPFLAGS="$BOOST_CPPFLAGS $boost_cv_pthread_flag" +LIBS=$boost_thread_save_LIBS +LDFLAGS=$boost_thread_save_LDFLAGS +CPPFLAGS=$boost_thread_save_CPPFLAGS +])# BOOST_THREAD + +AU_ALIAS([BOOST_THREADS], [BOOST_THREAD]) + + +# BOOST_TOKENIZER() +# ----------------- +# Look for Boost.Tokenizer +BOOST_DEFUN([Tokenizer], +[BOOST_FIND_HEADER([boost/tokenizer.hpp])]) + + +# BOOST_TRIBOOL() +# --------------- +# Look for Boost.Tribool +BOOST_DEFUN([Tribool], +[BOOST_FIND_HEADER([boost/logic/tribool_fwd.hpp]) +BOOST_FIND_HEADER([boost/logic/tribool.hpp]) +]) + + +# BOOST_TUPLE() +# ------------- +# Look for Boost.Tuple +BOOST_DEFUN([Tuple], +[BOOST_FIND_HEADER([boost/tuple/tuple.hpp])]) + + +# BOOST_TYPETRAITS() +# -------------------- +# Look for Boost.TypeTraits +BOOST_DEFUN([TypeTraits], +[BOOST_FIND_HEADER([boost/type_traits.hpp])]) + + +# BOOST_UTILITY() +# --------------- +# Look for Boost.Utility (noncopyable, result_of, base-from-member idiom, +# etc.) +BOOST_DEFUN([Utility], +[BOOST_FIND_HEADER([boost/utility.hpp])]) + + +# BOOST_VARIANT() +# --------------- +# Look for Boost.Variant. +BOOST_DEFUN([Variant], +[BOOST_FIND_HEADER([boost/variant/variant_fwd.hpp]) +BOOST_FIND_HEADER([boost/variant.hpp])]) + + +# BOOST_POINTER_CONTAINER() +# ------------------------ +# Look for Boost.PointerContainer +BOOST_DEFUN([Pointer_Container], +[BOOST_FIND_HEADER([boost/ptr_container/ptr_deque.hpp]) +BOOST_FIND_HEADER([boost/ptr_container/ptr_list.hpp]) +BOOST_FIND_HEADER([boost/ptr_container/ptr_vector.hpp]) +BOOST_FIND_HEADER([boost/ptr_container/ptr_array.hpp]) +BOOST_FIND_HEADER([boost/ptr_container/ptr_set.hpp]) +BOOST_FIND_HEADER([boost/ptr_container/ptr_map.hpp]) +])# BOOST_POINTER_CONTAINER + + +# BOOST_WAVE([PREFERRED-RT-OPT]) +# ------------------------------ +# NOTE: If you intend to use Wave/Spirit with thread support, make sure you +# call BOOST_THREAD first. +# Look for Boost.Wave. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Wave], +[AC_REQUIRE([BOOST_FILESYSTEM])dnl +AC_REQUIRE([BOOST_DATE_TIME])dnl +boost_wave_save_LIBS=$LIBS +boost_wave_save_LDFLAGS=$LDFLAGS +m4_pattern_allow([^BOOST_((FILE)?SYSTEM|DATE_TIME|THREAD)_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_SYSTEM_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_DATE_TIME_LIBS \ +$BOOST_THREAD_LIBS" +LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS \ +$BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS" +BOOST_FIND_LIB([wave], [$1], + [boost/wave.hpp], + [boost::wave::token_id id; get_token_name(id);]) +LIBS=$boost_wave_save_LIBS +LDFLAGS=$boost_wave_save_LDFLAGS +])# BOOST_WAVE + + +# BOOST_XPRESSIVE() +# ----------------- +# Look for Boost.Xpressive (new since 1.36.0). +BOOST_DEFUN([Xpressive], +[BOOST_FIND_HEADER([boost/xpressive/xpressive.hpp])]) + + +# ----------------- # +# Internal helpers. # +# ----------------- # + + +# _BOOST_PTHREAD_FLAG() +# --------------------- +# Internal helper for BOOST_THREAD. Computes boost_cv_pthread_flag +# which must be used in CPPFLAGS and LIBS. +# +# Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3, +# boost/thread.hpp will trigger a #error if -pthread isn't used: +# boost/config/requires_threads.hpp:47:5: #error "Compiler threading support +# is not turned on. Please set the correct command line options for +# threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)" +# +# Based on ACX_PTHREAD: http://autoconf-archive.cryp.to/acx_pthread.html +AC_DEFUN([_BOOST_PTHREAD_FLAG], +[AC_REQUIRE([AC_PROG_CXX])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_LANG_PUSH([C++])dnl +AC_CACHE_CHECK([for the flags needed to use pthreads], [boost_cv_pthread_flag], +[ boost_cv_pthread_flag= + # The ordering *is* (sometimes) important. Some notes on the + # individual items follow: + # (none): in case threads are in libc; should be tried before -Kthread and + # other compiler flags to prevent continual compiler warnings + # -lpthreads: AIX (must check this before -lpthread) + # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + # -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads) + # -pthreads: Solaris/GCC + # -mthreads: MinGW32/GCC, Lynx/GCC + # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it + # doesn't hurt to check since this sometimes defines pthreads too; + # also defines -D_REENTRANT) + # ... -mt is also the pthreads flag for HP/aCC + # -lpthread: GNU Linux, etc. + # --thread-safe: KAI C++ + case $host_os in #( + *solaris*) + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + boost_pthread_flags="-pthreads -lpthread -mt -pthread";; #( + *) + boost_pthread_flags="-lpthreads -Kthread -kthread -llthread -pthread \ + -pthreads -mthreads -lpthread --thread-safe -mt";; + esac + # Generate the test file. + AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <pthread.h>], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0);])]) + for boost_pthread_flag in '' $boost_pthread_flags; do + boost_pthread_ok=false +dnl Re-use the test file already generated. + boost_pthreads__save_LIBS=$LIBS + LIBS="$LIBS $boost_pthread_flag" + AC_LINK_IFELSE([], + [if grep ".*$boost_pthread_flag" conftest.err; then + echo "This flag seems to have triggered warnings" >&AS_MESSAGE_LOG_FD + else + boost_pthread_ok=:; boost_cv_pthread_flag=$boost_pthread_flag + fi]) + LIBS=$boost_pthreads__save_LIBS + $boost_pthread_ok && break + done +]) +AC_LANG_POP([C++])dnl +])# _BOOST_PTHREAD_FLAG + + +# _BOOST_gcc_test(MAJOR, MINOR) +# ----------------------------- +# Internal helper for _BOOST_FIND_COMPILER_TAG. +m4_define([_BOOST_gcc_test], +["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC @ gcc$1$2"])dnl + +# _BOOST_mingw_test(MAJOR, MINOR) +# ----------------------------- +# Internal helper for _BOOST_FIND_COMPILER_TAG. +m4_define([_BOOST_mingw_test], +["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC && \ + (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \ + || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw$1$2"])dnl + + +# _BOOST_FIND_COMPILER_TAG() +# -------------------------- +# Internal. When Boost is installed without --layout=system, each library +# filename will hold a suffix that encodes the compiler used during the +# build. The Boost build system seems to call this a `tag'. +AC_DEFUN([_BOOST_FIND_COMPILER_TAG], +[AC_REQUIRE([AC_PROG_CXX])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_CACHE_CHECK([for the toolset name used by Boost for $CXX], + [boost_cv_lib_tag], +[boost_cv_lib_tag=unknown +if test x$boost_cv_inc_path != xno; then + AC_LANG_PUSH([C++])dnl + # The following tests are mostly inspired by boost/config/auto_link.hpp + # The list is sorted to most recent/common to oldest compiler (in order + # to increase the likelihood of finding the right compiler with the + # least number of compilation attempt). + # Beware that some tests are sensible to the order (for instance, we must + # look for MinGW before looking for GCC3). + # I used one compilation test per compiler with a #error to recognize + # each compiler so that it works even when cross-compiling (let me know + # if you know a better approach). + # Known missing tags (known from Boost's tools/build/v2/tools/common.jam): + # como, edg, kcc, bck, mp, sw, tru, xlc + # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines + # the same defines as GCC's). + for i in \ + _BOOST_mingw_test(5, 3) \ + _BOOST_gcc_test(5, 3) \ + _BOOST_mingw_test(5, 2) \ + _BOOST_gcc_test(5, 2) \ + _BOOST_mingw_test(5, 1) \ + _BOOST_gcc_test(5, 1) \ + _BOOST_mingw_test(5, 0) \ + _BOOST_gcc_test(5, 0) \ + _BOOST_mingw_test(4, 10) \ + _BOOST_gcc_test(4, 10) \ + _BOOST_mingw_test(4, 9) \ + _BOOST_gcc_test(4, 9) \ + _BOOST_mingw_test(4, 8) \ + _BOOST_gcc_test(4, 8) \ + _BOOST_mingw_test(4, 7) \ + _BOOST_gcc_test(4, 7) \ + _BOOST_mingw_test(4, 6) \ + _BOOST_gcc_test(4, 6) \ + _BOOST_mingw_test(4, 5) \ + _BOOST_gcc_test(4, 5) \ + _BOOST_mingw_test(4, 4) \ + _BOOST_gcc_test(4, 4) \ + _BOOST_mingw_test(4, 3) \ + _BOOST_gcc_test(4, 3) \ + _BOOST_mingw_test(4, 2) \ + _BOOST_gcc_test(4, 2) \ + _BOOST_mingw_test(4, 1) \ + _BOOST_gcc_test(4, 1) \ + _BOOST_mingw_test(4, 0) \ + _BOOST_gcc_test(4, 0) \ + "defined __GNUC__ && __GNUC__ == 3 && !defined __ICC \ + && (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \ + || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw" \ + _BOOST_gcc_test(3, 4) \ + _BOOST_gcc_test(3, 3) \ + "defined _MSC_VER && _MSC_VER >= 1500 @ vc90" \ + "defined _MSC_VER && _MSC_VER == 1400 @ vc80" \ + _BOOST_gcc_test(3, 2) \ + "defined _MSC_VER && _MSC_VER == 1310 @ vc71" \ + _BOOST_gcc_test(3, 1) \ + _BOOST_gcc_test(3, 0) \ + "defined __BORLANDC__ @ bcb" \ + "defined __ICC && (defined __unix || defined __unix__) @ il" \ + "defined __ICL @ iw" \ + "defined _MSC_VER && _MSC_VER == 1300 @ vc7" \ + _BOOST_gcc_test(2, 95) \ + "defined __MWERKS__ && __MWERKS__ <= 0x32FF @ cw9" \ + "defined _MSC_VER && _MSC_VER < 1300 && !defined UNDER_CE @ vc6" \ + "defined _MSC_VER && _MSC_VER < 1300 && defined UNDER_CE @ evc4" \ + "defined __MWERKS__ && __MWERKS__ <= 0x31FF @ cw8" + do + boost_tag_test=`expr "X$i" : 'X\([[^@]]*\) @ '` + boost_tag=`expr "X$i" : 'X[[^@]]* @ \(.*\)'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if $boost_tag_test +/* OK */ +#else +# error $boost_tag_test +#endif +]])], [boost_cv_lib_tag=$boost_tag; break], []) + done +AC_LANG_POP([C++])dnl + case $boost_cv_lib_tag in #( + # Some newer (>= 1.35?) versions of Boost seem to only use "gcc" as opposed + # to "gcc41" for instance. + *-gcc | *'-gcc ') :;; #( Don't re-add -gcc: it's already in there. + gcc*) + boost_tag_x= + case $host_os in #( + darwin*) + if test $boost_major_version -ge 136; then + # The `x' added in r46793 of Boost. + boost_tag_x=x + fi;; + esac + # We can specify multiple tags in this variable because it's used by + # BOOST_FIND_LIB that does a `for tag in -$boost_cv_lib_tag' ... + boost_cv_lib_tag="$boost_tag_x$boost_cv_lib_tag -${boost_tag_x}gcc" + ;; #( + unknown) + AC_MSG_WARN([[could not figure out which toolset name to use for $CXX]]) + boost_cv_lib_tag= + ;; + esac +fi])dnl end of AC_CACHE_CHECK +])# _BOOST_FIND_COMPILER_TAG + + +# _BOOST_GUESS_WHETHER_TO_USE_MT() +# -------------------------------- +# Compile a small test to try to guess whether we should favor MT (Multi +# Thread) flavors of Boost. Sets boost_guess_use_mt accordingly. +AC_DEFUN([_BOOST_GUESS_WHETHER_TO_USE_MT], +[# Check whether we do better use `mt' even though we weren't ask to. +AC_LANG_PUSH([C++])dnl +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if defined _REENTRANT || defined _MT || defined __MT__ +/* use -mt */ +#else +# error MT not needed +#endif +]])], [boost_guess_use_mt=:], [boost_guess_use_mt=false]) +AC_LANG_POP([C++])dnl +]) + +# _BOOST_AC_LINK_IFELSE(PROGRAM, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# ------------------------------------------------------------------- +# Fork of _AC_LINK_IFELSE that preserves conftest.o across calls. Fragile, +# will break when Autoconf changes its internals. Requires that you manually +# rm -f conftest.$ac_objext in between to really different tests, otherwise +# you will try to link a conftest.o left behind by a previous test. +# Used to aggressively optimize BOOST_FIND_LIB (see the big comment in this +# macro). +# +# Don't use "break" in the actions, as it would short-circuit some code +# this macro runs after the actions. +m4_define([_BOOST_AC_LINK_IFELSE], +[m4_ifvaln([$1], [AC_LANG_CONFTEST([$1])])dnl +rm -f conftest$ac_exeext +boost_save_ac_ext=$ac_ext +boost_use_source=: +# If we already have a .o, re-use it. We change $ac_ext so that $ac_link +# tries to link the existing object file instead of compiling from source. +test -f conftest.$ac_objext && ac_ext=$ac_objext && boost_use_source=false && + _AS_ECHO_LOG([re-using the existing conftest.$ac_objext]) +AS_IF([_AC_DO_STDERR($ac_link) && { + test -z "$ac_[]_AC_LANG_ABBREV[]_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_executable_p conftest$ac_exeext +dnl FIXME: use AS_TEST_X instead when 2.61 is widespread enough. + }], + [$2], + [if $boost_use_source; then + _AC_MSG_LOG_CONFTEST + fi + $3]) +ac_objext=$boost_save_ac_objext +ac_ext=$boost_save_ac_ext +dnl Delete also the IPA/IPO (Inter Procedural Analysis/Optimization) +dnl information created by the PGI compiler (conftest_ipa8_conftest.oo), +dnl as it would interfere with the next link command. +rm -f core conftest.err conftest_ipa8_conftest.oo \ + conftest$ac_exeext m4_ifval([$1], [conftest.$ac_ext])[]dnl +])# _BOOST_AC_LINK_IFELSE + +# Local Variables: +# mode: autoconf +# End: diff --git a/qa/Makefile.am b/qa/Makefile.am new file mode 100644 index 0000000..1752412 --- /dev/null +++ b/qa/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = mockup libcmis libcmis-c diff --git a/qa/libcmis-c/Makefile.am b/qa/libcmis-c/Makefile.am new file mode 100644 index 0000000..c0de5ad --- /dev/null +++ b/qa/libcmis-c/Makefile.am @@ -0,0 +1,48 @@ +check_PROGRAMS = \ + test-api \ + test-c-build + +test_api_SOURCES = \ + test-allowable-actions.cxx \ + test-api.cxx \ + test-document.cxx \ + test-dummies.cxx \ + test-dummies.hxx \ + test-folder.cxx \ + test-object-type.cxx \ + test-object.cxx \ + test-property-type.cxx \ + test-property.cxx \ + test-repository.cxx \ + test-session.cxx + +test_api_CXXFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis-c/ \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) + +test_api_LDADD = \ + $(top_builddir)/src/libcmis-c/libcmis-c-@LIBCMIS_API_VERSION@.la \ + $(top_builddir)/src/libcmis/libcmis-@LIBCMIS_API_VERSION@.la \ + $(XML2_LIBS) \ + $(CURL_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +test_c_build_SOURCES = \ + test-build.c + +test_c_build_CFLAGS = \ + -I$(top_srcdir)/inc \ + $(XML2_CFLAGS) + +test_c_build_LDADD = \ + $(top_builddir)/src/libcmis-c/libcmis-c-@LIBCMIS_API_VERSION@.la \ + $(top_builddir)/src/libcmis/libcmis-@LIBCMIS_API_VERSION@.la \ + $(XML2_LIBS) \ + $(CURL_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +TESTS = test-api diff --git a/qa/libcmis-c/test-allowable-actions.cxx b/qa/libcmis-c/test-allowable-actions.cxx new file mode 100644 index 0000000..19332d4 --- /dev/null +++ b/qa/libcmis-c/test-allowable-actions.cxx @@ -0,0 +1,82 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/allowable-actions.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class AllowableActionsTest : public CppUnit::TestFixture +{ + private: + libcmis_AllowableActionsPtr getTested( ); + + public: + void isDefinedTest( ); + void isAllowedTest( ); + + CPPUNIT_TEST_SUITE( AllowableActionsTest ); + CPPUNIT_TEST( isDefinedTest ); + CPPUNIT_TEST( isAllowedTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( AllowableActionsTest ); + +libcmis_AllowableActionsPtr AllowableActionsTest::getTested( ) +{ + libcmis_AllowableActionsPtr result = new libcmis_allowable_actions( ); + libcmis::AllowableActionsPtr handle( new dummies::AllowableActions( ) ); + result->handle = handle; + + return result; +} + +void AllowableActionsTest::isDefinedTest( ) +{ + libcmis_AllowableActionsPtr allowableActions = getTested( ); + CPPUNIT_ASSERT( !libcmis_allowable_actions_isDefined( allowableActions, libcmis_DeleteObject ) ); + CPPUNIT_ASSERT( libcmis_allowable_actions_isDefined( allowableActions, libcmis_GetFolderParent ) ); + + libcmis_allowable_actions_free( allowableActions ); +} + +void AllowableActionsTest::isAllowedTest( ) +{ + libcmis_AllowableActionsPtr allowableActions = getTested( ); + CPPUNIT_ASSERT( libcmis_allowable_actions_isAllowed( allowableActions, libcmis_GetProperties ) ); + CPPUNIT_ASSERT( !libcmis_allowable_actions_isAllowed( allowableActions, libcmis_GetFolderParent ) ); + + libcmis_allowable_actions_free( allowableActions ); +} diff --git a/qa/libcmis-c/test-api.cxx b/qa/libcmis-c/test-api.cxx new file mode 100644 index 0000000..45b2daf --- /dev/null +++ b/qa/libcmis-c/test-api.cxx @@ -0,0 +1,39 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/ui/text/TestRunner.h> + +int main( int, char** ) +{ + CppUnit::TextUi::TestRunner runner; + CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + runner.addTest( registry.makeTest() ); + bool wasSuccess = runner.run( "", false ); + return !wasSuccess; +} diff --git a/qa/libcmis-c/test-build.c b/qa/libcmis-c/test-build.c new file mode 100644 index 0000000..52ac44d --- /dev/null +++ b/qa/libcmis-c/test-build.c @@ -0,0 +1,36 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/libcmis-c.h> + +int main ( int argc, char ** argv ) +{ + ( void )argc; + ( void )argv; + return 0; +} diff --git a/qa/libcmis-c/test-document.cxx b/qa/libcmis-c/test-document.cxx new file mode 100644 index 0000000..525cbaa --- /dev/null +++ b/qa/libcmis-c/test-document.cxx @@ -0,0 +1,621 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/error.h> +#include <libcmis-c/folder.h> +#include <libcmis-c/document.h> +#include <libcmis-c/object.h> +#include <libcmis-c/object-type.h> +#include <libcmis-c/property.h> +#include <libcmis-c/property-type.h> +#include <libcmis-c/vectors.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +extern bool isOutOfMemory; + +namespace +{ + string lcl_readFile( FILE* file ) + { + // Get the size + fseek( file, 0, SEEK_END ); + long size = ftell( file ); + rewind( file ); + + char* buf = new char[size + 1]; + size_t readbytes = fread( buf, 1, size, file ); + buf[ readbytes ] = '\0'; + + string result( buf ); + delete[] buf; + + return result; + } +} + +class DocumentTest : public CppUnit::TestFixture +{ + private: + libcmis_DocumentPtr getTested( bool isFiled, bool triggersFaults ); + dummies::Document* getTestedImplementation( libcmis_DocumentPtr document ); + + public: + void objectCastTest( ); + void objectCastFailureTest( ); + void objectFunctionsTest( ); + void getParentsTest( ); + void getParentsUnfiledTest( ); + void getParentsErrorTest( ); + void getContentStreamTest( ); + void getContentStreamErrorTest( ); + void getContentStreamBadAllocTest( ); + void setContentStreamTest( ); + void setContentStreamErrorTest( ); + void getContentTypeTest( ); + void getContentFilenameTest( ); + void getContentLengthTest( ); + void checkOutTest( ); + void checkOutErrorTest( ); + void cancelCheckoutTest( ); + void cancelCheckoutErrorTest( ); + void checkInTest( ); + void checkInErrorTest( ); + void getAllVersionsTest( ); + void getAllVersionsErrorTest( ); + + CPPUNIT_TEST_SUITE( DocumentTest ); + CPPUNIT_TEST( objectCastTest ); + CPPUNIT_TEST( objectCastFailureTest ); + CPPUNIT_TEST( objectFunctionsTest ); + CPPUNIT_TEST( getParentsTest ); + CPPUNIT_TEST( getParentsUnfiledTest ); + CPPUNIT_TEST( getParentsErrorTest ); + CPPUNIT_TEST( getContentStreamTest ); + CPPUNIT_TEST( getContentStreamErrorTest ); + CPPUNIT_TEST( getContentStreamBadAllocTest ); + CPPUNIT_TEST( setContentStreamTest ); + CPPUNIT_TEST( setContentStreamErrorTest ); + CPPUNIT_TEST( getContentTypeTest ); + CPPUNIT_TEST( getContentFilenameTest ); + CPPUNIT_TEST( getContentLengthTest ); + CPPUNIT_TEST( checkOutTest ); + CPPUNIT_TEST( checkOutErrorTest ); + CPPUNIT_TEST( cancelCheckoutTest ); + CPPUNIT_TEST( cancelCheckoutErrorTest ); + CPPUNIT_TEST( checkInTest ); + CPPUNIT_TEST( checkInErrorTest ); + CPPUNIT_TEST( getAllVersionsTest ); + CPPUNIT_TEST( getAllVersionsErrorTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( DocumentTest ); + +libcmis_DocumentPtr DocumentTest::getTested( bool isFiled, bool triggersFaults ) +{ + // Create the document + libcmis_DocumentPtr result = new libcmis_document( ); + libcmis::DocumentPtr handle( new dummies::Document( isFiled, triggersFaults ) ); + result->handle = handle; + + return result; +} + +dummies::Document* DocumentTest::getTestedImplementation( libcmis_DocumentPtr document ) +{ + dummies::Document* impl = dynamic_cast< dummies::Document* >( document->handle.get( ) ); + return impl; +} + +void DocumentTest::objectCastTest( ) +{ + // Create the test object to cast + libcmis_ObjectPtr tested = new libcmis_object( ); + libcmis::DocumentPtr handle( new dummies::Document( true, false ) ); + tested->handle = handle; + + // Test libcmis_is_document + CPPUNIT_ASSERT( libcmis_is_document( tested ) ); + + // Actually cast to a document + libcmis_DocumentPtr actual = libcmis_document_cast( tested ); + + // Check the result + CPPUNIT_ASSERT( NULL != actual ); + + // Check that the libcmis_object-* functions are working with the cast result + char* actualId = libcmis_object_getId( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Document::Id" ), string( actualId ) ); + free( actualId ); + + // Free it all + libcmis_document_free( actual ); + libcmis_object_free( tested ); +} + +void DocumentTest::objectCastFailureTest( ) +{ + // Create the test object to cast + libcmis_ObjectPtr tested = new libcmis_object( ); + libcmis::FolderPtr handle( new dummies::Folder( false, false ) ); + tested->handle = handle; + + // Test libcmis_is_document + CPPUNIT_ASSERT( !libcmis_is_document( tested ) ); + + // Actually cast to a document + libcmis_DocumentPtr actual = libcmis_document_cast( tested ); + + // Check the result + CPPUNIT_ASSERT( NULL == actual ); + + libcmis_object_free( tested ); +} + +void DocumentTest::objectFunctionsTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + char* actual = libcmis_object_getId( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Document::Id" ), string( actual ) ); + free( actual ); + libcmis_document_free( tested ); +} + +void DocumentTest::getParentsTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get the parent folders (tested method) + libcmis_vector_folder_Ptr actual = libcmis_document_getParents( tested, error ); + + // Check + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), libcmis_vector_folder_size( actual ) ); + + // Free it all + libcmis_vector_folder_free( actual ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getParentsUnfiledTest( ) +{ + libcmis_DocumentPtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get the parent folders (tested method) + libcmis_vector_folder_Ptr actual = libcmis_document_getParents( tested, error ); + + // Check + CPPUNIT_ASSERT_EQUAL( size_t( 0 ), libcmis_vector_folder_size( actual ) ); + + // Free it all + libcmis_vector_folder_free( actual ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getParentsErrorTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get the parent folders (tested method) + libcmis_vector_folder_Ptr actual = libcmis_document_getParents( tested, error ); + + // Check + CPPUNIT_ASSERT( NULL == actual ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + + // Free it all + libcmis_vector_folder_free( actual ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getContentStreamTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get the content into a temporary file (tested method) + FILE* tmp = tmpfile( ); + libcmis_document_getContentStream( tested, + ( libcmis_writeFn )fwrite, tmp, error ); + + // Check + string expected = getTestedImplementation( tested )->getContentString( ); + + string actual = lcl_readFile( tmp ); + fclose( tmp ); + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT_EQUAL( expected, actual ); + + // Free it all + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getContentStreamErrorTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get the content into a temporary file (tested method) + FILE* tmp = tmpfile( ); + libcmis_document_getContentStream( tested, + ( libcmis_writeFn )fwrite, tmp, error ); + + // Check + string actual = lcl_readFile( tmp ); + fclose( tmp ); + CPPUNIT_ASSERT_EQUAL( string( ), actual ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + + // Free it all + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getContentStreamBadAllocTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get the content into a temporary file (tested method) + FILE* tmp = tmpfile( ); + + isOutOfMemory= true; + libcmis_document_getContentStream( tested, + ( libcmis_writeFn )fwrite, tmp, error ); + isOutOfMemory = false; + + // Check + string actual = lcl_readFile( tmp ); + fclose( tmp ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty() ); + CPPUNIT_ASSERT_EQUAL( string( ), actual ); + + // Free it all + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::setContentStreamTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Prepare the content to set + FILE* tmp = tmpfile( ); + string expected( "New Content Stream" ); + fwrite( expected.c_str( ), 1, expected.size( ), tmp ); + rewind( tmp ); + + // get the content into a temporary file (tested method) + const char* contentType = "content/type"; + const char* filename = "name.txt"; + libcmis_document_setContentStream( tested, + ( libcmis_readFn )fread, tmp, contentType, filename, true, error ); + fclose( tmp ); + + // Check + string actual = getTestedImplementation( tested )->getContentString( ); + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT_EQUAL( expected, actual ); + + // Free it all + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::setContentStreamErrorTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + string expected = getTestedImplementation( tested )->getContentString( ); + + // Prepare the content to set + FILE* tmp = tmpfile( ); + string newContent( "New Content Stream" ); + fwrite( newContent.c_str( ), 1, newContent.size( ), tmp ); + rewind( tmp ); + + // get the content into a temporary file (tested method) + const char* contentType = "content/type"; + const char* filename = "name.txt"; + libcmis_document_setContentStream( tested, + ( libcmis_readFn )fread, tmp, contentType, filename, true, error ); + fclose( tmp ); + + // Check + string actual = getTestedImplementation( tested )->getContentString( ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + CPPUNIT_ASSERT_EQUAL( expected, actual ); + + // Free it all + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getContentTypeTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + char* actual = libcmis_document_getContentType( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Document::ContentType" ), string( actual ) ); + free( actual ); + libcmis_document_free( tested ); +} + +void DocumentTest::getContentFilenameTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + char* actual = libcmis_document_getContentFilename( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Document::ContentFilename" ), string( actual ) ); + free( actual ); + libcmis_document_free( tested ); +} + +void DocumentTest::getContentLengthTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + long actual = libcmis_document_getContentLength( tested ); + CPPUNIT_ASSERT( 0 != actual ); + libcmis_document_free( tested ); +} + +void DocumentTest::checkOutTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // checkout a private working copy (tested method) + libcmis_DocumentPtr actual = libcmis_document_checkOut( tested, error ); + + // Check + CPPUNIT_ASSERT( NULL != actual ); + + // Free it all + libcmis_document_free( actual ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::checkOutErrorTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // checkout a private working copy (tested method) + libcmis_DocumentPtr actual = libcmis_document_checkOut( tested, error ); + + // Check + CPPUNIT_ASSERT( NULL == actual ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + + // Free it all + libcmis_document_free( actual ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::cancelCheckoutTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // checkout a private working copy (tested method) + libcmis_document_cancelCheckout( tested, error ); + + // Check + CPPUNIT_ASSERT( 0 != libcmis_object_getRefreshTimestamp( tested ) ); + + // Free it all + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::cancelCheckoutErrorTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // checkout a private working copy (tested method) + libcmis_document_cancelCheckout( tested, error ); + + // Check + CPPUNIT_ASSERT( 0 == libcmis_object_getRefreshTimestamp( tested ) ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + + // Free it all + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::checkInTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Prepare the content to set + FILE* tmp = tmpfile( ); + string expected( "New Content Stream" ); + fwrite( expected.c_str( ), 1, expected.size( ), tmp ); + rewind( tmp ); + + // Create the properties for the new version + libcmis_vector_property_Ptr properties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + const char* id = "Property1"; + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, id ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr property = libcmis_property_create( propertyType, values, size ); + delete[] values; + libcmis_vector_property_append( properties, property ); + + // get the content into a temporary file (tested method) + const char* contentType = "content/type"; + const char* comment = "Version comment"; + const char* filename = "filename.txt"; + libcmis_DocumentPtr newVersion = libcmis_document_checkIn( + tested, true, comment, properties, + ( libcmis_readFn )fread, tmp, contentType, filename, error ); + fclose( tmp ); + + // Check + CPPUNIT_ASSERT( NULL != newVersion ); + + string actual = getTestedImplementation( tested )->getContentString( ); + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT_EQUAL( expected, actual ); + + libcmis_PropertyPtr checkedProperty = libcmis_object_getProperty( tested, "Property1" ); + libcmis_vector_string_Ptr newValues = libcmis_property_getStrings( checkedProperty ); + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), libcmis_vector_string_size( newValues ) ); + + // Free it all + libcmis_vector_string_free( newValues ); + libcmis_property_free( checkedProperty ); + libcmis_document_free( newVersion ); + libcmis_property_free( property ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( properties ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::checkInErrorTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + string expected = getTestedImplementation( tested )->getContentString( ); + + // Prepare the content to set + FILE* tmp = tmpfile( ); + string newContent( "New Content Stream" ); + fwrite( newContent.c_str( ), 1, newContent.size( ), tmp ); + rewind( tmp ); + + // Create the properties for the new version + libcmis_vector_property_Ptr properties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + const char* id = "Property1"; + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, id ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr property = libcmis_property_create( propertyType, values, size ); + delete[] values; + libcmis_vector_property_append( properties, property ); + + // get the content into a temporary file (tested method) + const char* contentType = "content/type"; + const char* comment = "Version comment"; + const char* filename = "filename.txt"; + libcmis_DocumentPtr newVersion = libcmis_document_checkIn( + tested, true, comment, properties, + ( libcmis_readFn )fread, tmp, contentType, filename, error ); + fclose( tmp ); + + // Check + CPPUNIT_ASSERT( NULL == newVersion ); + + string actual = getTestedImplementation( tested )->getContentString( ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + CPPUNIT_ASSERT_EQUAL( expected, actual ); + + libcmis_PropertyPtr checkedProperty = libcmis_object_getProperty( tested, "Property1" ); + libcmis_vector_string_Ptr newValues = libcmis_property_getStrings( checkedProperty ); + CPPUNIT_ASSERT_EQUAL( size_t( 1 ), libcmis_vector_string_size( newValues ) ); + + // Free it all + libcmis_vector_string_free( newValues ); + libcmis_property_free( checkedProperty ); + libcmis_document_free( newVersion ); + libcmis_property_free( property ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( properties ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getAllVersionsTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get all versions (tested method) + libcmis_vector_document_Ptr versions = libcmis_document_getAllVersions( tested, error ); + + // Check + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), libcmis_vector_document_size( versions ) ); + libcmis_DocumentPtr actualVersion = libcmis_vector_document_get( versions, 0 ); + char* actualId = libcmis_object_getId( actualVersion ); + CPPUNIT_ASSERT( actualId != NULL ); + free( actualId ); + + // Free it all + libcmis_document_free( actualVersion ); + libcmis_vector_document_free( versions ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} + +void DocumentTest::getAllVersionsErrorTest( ) +{ + libcmis_DocumentPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // get all versions (tested method) + libcmis_vector_document_Ptr versions = libcmis_document_getAllVersions( tested, error ); + + // Check + CPPUNIT_ASSERT( NULL == versions ); + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + + // Free it all + libcmis_vector_document_free( versions ); + libcmis_error_free( error ); + libcmis_document_free( tested ); +} diff --git a/qa/libcmis-c/test-dummies.cxx b/qa/libcmis-c/test-dummies.cxx new file mode 100644 index 0000000..5056bcf --- /dev/null +++ b/qa/libcmis-c/test-dummies.cxx @@ -0,0 +1,633 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "test-dummies.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +bool isOutOfMemory = false; + +/// Ignore all tests results depending on this when running in valgrind +void * operator new ( size_t requestedSize ) +{ + if ( isOutOfMemory ) + { + throw bad_alloc( ); + } + + return malloc( requestedSize ); +} + +void operator delete ( void* ptr ) throw ( ) +{ + free( ptr ); +} + +#if __cplusplus > 201103L +void operator delete ( void* ptr, std::size_t ) throw ( ) +{ + free( ptr ); +} +#endif + +namespace dummies +{ + Session::Session( ) + { + } + + Session::~Session( ) + { + } + + libcmis::RepositoryPtr Session::getRepository( ) + { + libcmis::RepositoryPtr repo( new Repository( ) ); + return repo; + } + + + bool Session::setRepository( std::string ) + { + return true; + } + + vector< libcmis::RepositoryPtr > Session::getRepositories( ) + { + vector< libcmis::RepositoryPtr > repos; + libcmis::RepositoryPtr repo1( new Repository( ) ); + libcmis::RepositoryPtr repo2( new Repository( ) ); + repos.push_back( repo1 ); + repos.push_back( repo2 ); + return repos; + } + + libcmis::FolderPtr Session::getRootFolder() + { + libcmis::FolderPtr root( new Folder( true, false ) ); + return root; + } + + libcmis::ObjectPtr Session::getObject( string id ) + { + return getFolder( id ); + } + + libcmis::ObjectPtr Session::getObjectByPath( string path ) + { + return getFolder( path ); + } + + libcmis::FolderPtr Session::getFolder( string ) + { + libcmis::FolderPtr result( new Folder( false, false ) ); + return result; + } + + libcmis::ObjectTypePtr Session::getType( string ) + { + libcmis::ObjectTypePtr type( new ObjectType( true, false ) ); + return type; + } + + vector< libcmis::ObjectTypePtr > Session::getBaseTypes( ) + { + vector< libcmis::ObjectTypePtr > types; + libcmis::ObjectTypePtr type( new ObjectType( true, false ) ); + types.push_back( type ); + return types; + } + + std::string Session::getRefreshToken( ) + { + return string( ); + } + + Repository::Repository( ) : + libcmis::Repository( ) + { + m_id = string( "Repository::Id" ); + m_name = string( "Repository::Name" ); + m_description = string( "Repository::Description" ); + m_vendorName = string( "Repository::VendorName" ); + m_productName = string( "Repository::ProductName" ); + m_productVersion = string( "Repository::ProductVersion" ); + m_rootId = string( "Repository::RootId" ); + m_cmisVersionSupported = string( "Repository::CmisVersionSupported" ); + m_thinClientUri.reset( new string( "Repository::ThinClientUri" ) ); + m_principalAnonymous.reset( new string( "Repository::PrincipalAnonymous" ) ); + m_principalAnyone.reset( new string( "Repository::PrincipalAnyone" ) ); + } + + Repository::~Repository( ) + { + } + + PropertyType::PropertyType( string id, string xmlType ) : + libcmis::PropertyType( ) + { + setId( id ); + setLocalName( string( "PropertyType::LocalName" ) ); + setLocalNamespace( string( "PropertyType::LocalNamespace" ) ); + setDisplayName( string( "PropertyType::DisplayName" ) ); + setQueryName( string( "PropertyType::QueryName" ) ); + setTypeFromXml( xmlType ); + + // Setting true for the tests to see a difference with + // the default false result of the tested functions + setMultiValued( true ); + setUpdatable( true ); + setInherited( true ); + setRequired( true ); + setQueryable( true ); + setOrderable( true ); + setOpenChoice( true ); + } + + PropertyType::~PropertyType( ) + { + } + + AllowableActions::AllowableActions( ) : + libcmis::AllowableActions( ) + { + m_states.insert( pair< libcmis::ObjectAction::Type, bool >( libcmis::ObjectAction::GetProperties, true ) ); + m_states.insert( pair< libcmis::ObjectAction::Type, bool >( libcmis::ObjectAction::GetFolderParent, false ) ); + } + + AllowableActions::~AllowableActions( ) + { + } + + ObjectType::ObjectType( ) : + libcmis::ObjectType( ), + m_typeId( ), + m_childrenIds( ), + m_triggersFaults( false ) + { + } + + ObjectType::ObjectType( bool rootType, bool triggersFaults ) : + libcmis::ObjectType( ), + m_typeId( ), + m_childrenIds( ), + m_triggersFaults( triggersFaults ) + { + if ( rootType ) + m_typeId = "RootType"; + else + { + m_typeId = "ObjectType"; + m_parentTypeId = "ParentType"; + m_childrenIds.push_back( "ChildType1" ); + m_childrenIds.push_back( "ChildType2" ); + } + + m_baseTypeId = "RootType"; + libcmis::PropertyTypePtr propType1( new PropertyType( "Property1", "string" ) ); + m_propertiesTypes.insert( pair< string, libcmis::PropertyTypePtr >( propType1->getId( ), propType1 ) ); + libcmis::PropertyTypePtr propType2( new PropertyType( "Property2", "string" ) ); + m_propertiesTypes.insert( pair< string, libcmis::PropertyTypePtr >( propType2->getId( ), propType2 ) ); + libcmis::PropertyTypePtr propType3( new PropertyType( "Property3", "string" ) ); + m_propertiesTypes.insert( pair< string, libcmis::PropertyTypePtr >( propType3->getId( ), propType3 ) ); + + initMembers( ); + } + + void ObjectType::initMembers( ) + { + + m_id = m_typeId + "::Id"; + m_localName = m_typeId + "::LocalName"; + m_localNamespace = m_typeId + "::LocalNamespace"; + m_displayName = m_typeId + "::DisplayName"; + m_queryName = m_typeId + "::QueryName"; + m_description = m_typeId + "::Description"; + + m_creatable = true; + m_fileable = true; + m_queryable = true; + m_fulltextIndexed = true; + m_includedInSupertypeQuery = true; + m_controllablePolicy = true; + m_controllableAcl = true; + m_versionable = true; + m_contentStreamAllowed = libcmis::ObjectType::Allowed; + } + + ObjectType::~ObjectType( ) + { + } + + libcmis::ObjectTypePtr ObjectType::getParentType( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + ObjectType* parent = NULL; + if ( !m_parentTypeId.empty( ) ) + { + parent = new ObjectType( ); + parent->m_typeId = m_parentTypeId; + parent->m_parentTypeId = m_baseTypeId; + parent->m_baseTypeId = m_baseTypeId; + parent->m_childrenIds.push_back( m_id ); + parent->m_triggersFaults = m_triggersFaults; + parent->m_propertiesTypes = m_propertiesTypes; + + parent->initMembers( ); + } + + libcmis::ObjectTypePtr result( parent ); + return result; + } + + libcmis::ObjectTypePtr ObjectType::getBaseType( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + ObjectType* base = this; + if ( m_typeId != m_baseTypeId ) + { + base = new ObjectType( ); + base->m_typeId = m_baseTypeId; + base->m_baseTypeId = m_baseTypeId; + base->m_childrenIds.push_back( m_id ); + base->m_triggersFaults = m_triggersFaults; + base->m_propertiesTypes = m_propertiesTypes; + + base->initMembers( ); + } + + libcmis::ObjectTypePtr result( base ); + return result; + } + + vector< libcmis::ObjectTypePtr > ObjectType::getChildren( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + vector< libcmis::ObjectTypePtr > children; + + for ( vector< string >::iterator it = m_childrenIds.begin( ); it != m_childrenIds.end( ); ++it ) + { + ObjectType* child = new ObjectType( ); + child->m_typeId = *it; + child->m_parentTypeId = m_typeId; + child->m_baseTypeId = m_baseTypeId; + child->m_triggersFaults = m_triggersFaults; + child->m_propertiesTypes = m_propertiesTypes; + + child->initMembers( ); + + libcmis::ObjectTypePtr result( child ); + children.push_back( result ); + } + + return children; + } + + string ObjectType::toString( ) + { + return m_typeId + "::toString"; + } + + Object::Object( bool triggersFaults, string type ): + libcmis::Object( NULL ), + m_type( type ), + m_triggersFaults( triggersFaults ) + { + libcmis::PropertyTypePtr propertyType( new PropertyType( "Property1", "string" ) ); + vector< string > values; + values.push_back( "Value1" ); + libcmis::PropertyPtr property( new libcmis::Property( propertyType, values ) ); + m_properties.insert( pair< string, libcmis::PropertyPtr >( propertyType->getId( ), property ) ); + } + + string Object::getId( ) + { + return m_type + "::Id"; + } + + string Object::getName( ) + { + return m_type + "::Name"; + } + + vector< string > Object::getPaths( ) + { + vector< string > paths; + paths.push_back( string( "/Path1/" ) ); + paths.push_back( string( "/Path2/" ) ); + + return paths; + } + + string Object::getBaseType( ) + { + return m_type + "::BaseType"; + } + + string Object::getType( ) + { + return m_type + "::Type"; + } + + boost::posix_time::ptime Object::getCreationDate( ) + { + boost::posix_time::ptime now( boost::posix_time::second_clock::local_time( ) ); + return now; + } + + boost::posix_time::ptime Object::getLastModificationDate( ) + { + boost::posix_time::ptime now( boost::posix_time::second_clock::local_time( ) ); + return now; + } + + libcmis::ObjectPtr Object::updateProperties( + const PropertyPtrMap& ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + time( &m_refreshTimestamp ); + libcmis::ObjectPtr result( new Object( false ) ); + return result; + } + + libcmis::ObjectTypePtr Object::getTypeDescription( ) + { + libcmis::ObjectTypePtr type( new ObjectType( false, m_triggersFaults ) ); + return type; + } + + libcmis::AllowableActionsPtr Object::getAllowableActions( ) + { + libcmis::AllowableActionsPtr allowableActions( new AllowableActions( ) ); + return allowableActions; + } + + void Object::refresh( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + time( &m_refreshTimestamp ); + } + + void Object::remove( bool ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + time( &m_refreshTimestamp ); + } + + void Object::move( libcmis::FolderPtr, libcmis::FolderPtr ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + time( &m_refreshTimestamp ); + } + + void Object::toXml( xmlTextWriterPtr ) + { + } + + Folder::Folder( bool isRoot, bool triggersFaults ) : + libcmis::Object( NULL ), + libcmis::Folder( NULL ), + dummies::Object( triggersFaults, "Folder" ), + m_isRoot( isRoot ) + { + } + + libcmis::FolderPtr Folder::getFolderParent( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + libcmis::FolderPtr parent; + + if ( !m_isRoot ) + parent.reset( new Folder( true, m_triggersFaults ) ); + + return parent; + } + + vector< libcmis::ObjectPtr > Folder::getChildren( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + vector< libcmis::ObjectPtr > children; + + libcmis::ObjectPtr child1( new Object( m_triggersFaults ) ); + children.push_back( child1 ); + libcmis::ObjectPtr child2( new Object( m_triggersFaults ) ); + children.push_back( child2 ); + + return children; + } + + string Folder::getPath( ) + { + return string( "/Path/" ); + } + + bool Folder::isRootFolder( ) + { + return m_isRoot; + } + + libcmis::FolderPtr Folder::createFolder( const PropertyPtrMap& ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + libcmis::FolderPtr created( new Folder( true, m_triggersFaults ) ); + return created; + } + + libcmis::DocumentPtr Folder::createDocument( const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType, string filename ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + dummies::Document* document = new dummies::Document( true, false ); + + PropertyPtrMap propertiesCopy( properties ); + document->getProperties( ).swap( propertiesCopy ); + document->setContentStream( os, contentType, filename ); + + libcmis::DocumentPtr created( document ); + return created; + } + + vector< string > Folder::removeTree( bool, libcmis::UnfileObjects::Type, + bool ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + time( &m_refreshTimestamp ); + + vector< string > failed; + failed.push_back( "failed 1" ); + return failed; + } + + Document::Document( bool isFiled, bool triggersFaults ) : + libcmis::Object( NULL ), + libcmis::Document( NULL ), + dummies::Object( triggersFaults, "Document" ), + m_isFiled( isFiled ), + m_contentString( "Document::ContentStream" ) + { + } + + vector< libcmis::FolderPtr > Document::getParents( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + vector< libcmis::FolderPtr > parents; + if ( m_isFiled ) + { + libcmis::FolderPtr parent1( new Folder( true, m_triggersFaults ) ); + parents.push_back( parent1 ); + libcmis::FolderPtr parent2( new Folder( false, m_triggersFaults ) ); + parents.push_back( parent2 ); + } + + return parents; + } + + boost::shared_ptr< istream > Document::getContentStream( string /*streamId*/ ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + bool oldOutOfMem = isOutOfMemory; + isOutOfMemory = false; + boost::shared_ptr< istream > stream( new stringstream( m_contentString ) ); + isOutOfMemory = oldOutOfMem; + return stream; + } + + void Document::setContentStream( boost::shared_ptr< ostream > os, string, string, bool ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + istream is( os->rdbuf( ) ); + stringstream out; + is.seekg( 0 ); + int bufSize = 2048; + char* buf = new char[ bufSize ]; + while ( !is.eof( ) ) + { + is.read( buf, bufSize ); + size_t read = is.gcount( ); + out.write( buf, read ); + } + delete[] buf; + + m_contentString = out.str( ); + + time( &m_refreshTimestamp ); + } + + string Document::getContentType( ) + { + return "Document::ContentType"; + } + + string Document::getContentFilename( ) + { + return "Document::ContentFilename"; + } + + long Document::getContentLength( ) + { + return long( 12345 ); + } + + libcmis::DocumentPtr Document::checkOut( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + time( &m_refreshTimestamp ); + + libcmis::DocumentPtr result( new Document( true, m_triggersFaults ) ); + return result; + } + + void Document::cancelCheckout( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + time( &m_refreshTimestamp ); + } + + libcmis::DocumentPtr Document::checkIn( bool, string, const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType, string filename ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + m_properties = properties; + setContentStream( os, contentType, filename ); + time( &m_refreshTimestamp ); + + return libcmis::DocumentPtr( new Document( true, false ) ); + } + + vector< libcmis::DocumentPtr > Document::getAllVersions( ) + { + if ( m_triggersFaults ) + throw libcmis::Exception( "Fault triggered" ); + + vector< libcmis::DocumentPtr > versions; + + libcmis::DocumentPtr version1( new Document( true, false ) ); + versions.push_back( version1 ); + libcmis::DocumentPtr version2( new Document( true, false ) ); + versions.push_back( version2 ); + + return versions; + } +} diff --git a/qa/libcmis-c/test-dummies.hxx b/qa/libcmis-c/test-dummies.hxx new file mode 100644 index 0000000..161c813 --- /dev/null +++ b/qa/libcmis-c/test-dummies.hxx @@ -0,0 +1,223 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_TEST_DUMMIES_HXX_ +#define _LIBCMIS_TEST_DUMMIES_HXX_ + + +#include <libcmis/allowable-actions.hxx> +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> +#include <libcmis/object-type.hxx> +#include <libcmis/property-type.hxx> +#include <libcmis/repository.hxx> +#include <libcmis/session.hxx> + +/** This namespace contains dummy classes to simulate the libcmis layer + in the libcmis-c unit tests. + */ +namespace dummies +{ + class Session : public libcmis::Session + { + public: + Session( ); + ~Session( ); + + virtual libcmis::RepositoryPtr getRepository( ); + virtual bool setRepository( std::string repositoryId ); + virtual std::vector< libcmis::RepositoryPtr > getRepositories( ); + virtual libcmis::FolderPtr getRootFolder(); + virtual libcmis::ObjectPtr getObject( std::string id ); + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + virtual libcmis::FolderPtr getFolder( std::string id ); + virtual libcmis::ObjectTypePtr getType( std::string id ); + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + virtual std::string getRefreshToken( ); + virtual void setNoSSLCertificateCheck( bool /*noCheck*/ ) { } + }; + + class Repository : public libcmis::Repository + { + public: + Repository( ); + ~Repository( ); + }; + + class PropertyType : public libcmis::PropertyType + { + public: + PropertyType( std::string id, std::string xmlType ); + ~PropertyType( ); + }; + + /** Dummy for testing the C API for allowable actions. The dummy has only the + following actions defined: + \li \c GetProperties, defined to \c true + \li \c GetFolderParent, defined to \c false + */ + class AllowableActions : public libcmis::AllowableActions + { + public: + AllowableActions( ); + ~AllowableActions( ); + }; + + class ObjectType : public libcmis::ObjectType + { + private: + std::string m_typeId; + std::vector< std::string > m_childrenIds; + bool m_triggersFaults; + + ObjectType( ); + void initMembers( ); + + public: + ObjectType( bool rootType, bool triggersFaults ); + ~ObjectType( ); + + virtual boost::shared_ptr< libcmis::ObjectType > getParentType( ); + virtual boost::shared_ptr< libcmis::ObjectType > getBaseType( ); + virtual std::vector< boost::shared_ptr< libcmis::ObjectType > > getChildren( ); + + virtual std::string toString( ); + }; + + class Object : public virtual libcmis::Object + { + public: + std::string m_type; + bool m_triggersFaults; + + public: + Object( bool triggersFaults, std::string m_type = "Object" ); + ~Object( ) { } + + virtual std::string getId( ); + virtual std::string getName( ); + + virtual std::vector< std::string > getPaths( ); + + virtual std::string getBaseType( ); + virtual std::string getType( ); + + virtual std::string getCreatedBy( ) { return m_type + "::CreatedBy"; } + virtual boost::posix_time::ptime getCreationDate( ); + virtual std::string getLastModifiedBy( ) { return m_type + "::LastModifiedBy"; } + virtual boost::posix_time::ptime getLastModificationDate( ); + + virtual std::string getChangeToken( ) { return m_type + "::ChangeToken"; } + virtual bool isImmutable( ) { return true; }; + + virtual libcmis::ObjectPtr updateProperties( + const std::map< std::string, libcmis::PropertyPtr >& properties ); + + virtual libcmis::ObjectTypePtr getTypeDescription( ); + virtual libcmis::AllowableActionsPtr getAllowableActions( ); + + virtual void refresh( ); + + virtual void remove( bool allVersions = true ); + + virtual void move( libcmis::FolderPtr source, libcmis::FolderPtr destination ); + + virtual std::string toString( ) { return m_type + "::toString"; } + + virtual void toXml( xmlTextWriterPtr writer ); + }; + + class Folder : public libcmis::Folder, public Object + { + private: + bool m_isRoot; + + public: + Folder( bool isRoot, bool triggersFaults ); + ~Folder( ) { } + + virtual libcmis::FolderPtr getFolderParent( ); + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + virtual std::string getPath( ); + + virtual bool isRootFolder( ); + + virtual libcmis::FolderPtr createFolder( const std::map< std::string, libcmis::PropertyPtr >& properties ); + virtual libcmis::DocumentPtr createDocument( const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType, std::string filename ); + + virtual std::vector< std::string > removeTree( bool allVersion = true, + libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); + + virtual std::vector< std::string > getPaths( ) { return dummies::Object::getPaths( ); } + virtual std::string toString( ) { return dummies::Object::toString( ); } + }; + + class Document : public libcmis::Document, public Object + { + private: + bool m_isFiled; + std::string m_contentString; + + public: + Document( bool isFiled, bool triggersFaults ); + ~Document( ) { } + + std::string getContentString( ) { return m_contentString; } + + virtual std::vector< libcmis::FolderPtr > getParents( ); + + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, std::string contentType, + std::string fileName, bool overwrite = true ); + + virtual std::string getContentType( ); + + virtual std::string getContentFilename( ); + + virtual long getContentLength( ); + + virtual libcmis::DocumentPtr checkOut( ); + + virtual void cancelCheckout( ); + + virtual libcmis::DocumentPtr checkIn( bool isMajor, std::string comment, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string filename ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); + + virtual std::vector< std::string > getPaths( ) { return dummies::Object::getPaths( ); } + virtual std::string toString( ) { return dummies::Object::toString( ); } + }; +} + +#endif diff --git a/qa/libcmis-c/test-folder.cxx b/qa/libcmis-c/test-folder.cxx new file mode 100644 index 0000000..171c57d --- /dev/null +++ b/qa/libcmis-c/test-folder.cxx @@ -0,0 +1,433 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/document.h> +#include <libcmis-c/error.h> +#include <libcmis-c/folder.h> +#include <libcmis-c/object.h> +#include <libcmis-c/object-type.h> +#include <libcmis-c/property.h> +#include <libcmis-c/property-type.h> +#include <libcmis-c/vectors.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class FolderTest : public CppUnit::TestFixture +{ + private: + libcmis_FolderPtr getTested( bool isRoot, bool triggersFaults ); + dummies::Document* getDocumentImplementation( libcmis_DocumentPtr document ); + + public: + void objectCastTest( ); + void objectCastFailureTest( ); + void objectFunctionsTest( ); + void getParentTest( ); + void getParentRootTest( ); + void getParentErrorTest( ); + void getChildrenTest( ); + void getChildrenErrorTest( ); + void getPathTest( ); + void createFolderTest( ); + void createFolderErrorTest( ); + void createDocumentTest( ); + void createDocumentErrorTest( ); + void removeTreeTest( ); + void removeTreeErrorTest( ); + + CPPUNIT_TEST_SUITE( FolderTest ); + CPPUNIT_TEST( objectCastTest ); + CPPUNIT_TEST( objectCastFailureTest ); + CPPUNIT_TEST( objectFunctionsTest ); + CPPUNIT_TEST( getParentTest ); + CPPUNIT_TEST( getParentRootTest ); + CPPUNIT_TEST( getParentErrorTest ); + CPPUNIT_TEST( getChildrenTest ); + CPPUNIT_TEST( getChildrenErrorTest ); + CPPUNIT_TEST( getPathTest ); + CPPUNIT_TEST( createFolderTest ); + CPPUNIT_TEST( createFolderErrorTest ); + CPPUNIT_TEST( createDocumentTest ); + CPPUNIT_TEST( createDocumentErrorTest ); + CPPUNIT_TEST( removeTreeTest ); + CPPUNIT_TEST( removeTreeErrorTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( FolderTest ); + +libcmis_FolderPtr FolderTest::getTested( bool isRoot, bool triggersFaults ) +{ + libcmis_FolderPtr result = new libcmis_folder( ); + libcmis::FolderPtr handle( new dummies::Folder( isRoot, triggersFaults ) ); + result->handle = handle; + + return result; +} + +dummies::Document* FolderTest::getDocumentImplementation( libcmis_DocumentPtr document ) +{ + dummies::Document* impl = dynamic_cast< dummies::Document* >( document->handle.get( ) ); + return impl; +} + +void FolderTest::objectCastTest( ) +{ + // Create the test object to cast + libcmis_ObjectPtr tested = new libcmis_object( ); + libcmis::FolderPtr handle( new dummies::Folder( false, false ) ); + tested->handle = handle; + + // Test libcmis_is_folder + CPPUNIT_ASSERT( libcmis_is_folder( tested ) ); + + // Actually cast to a folder + libcmis_FolderPtr actual = libcmis_folder_cast( tested ); + + // Check the result + CPPUNIT_ASSERT( NULL != actual ); + + // Check that the libcmis_object-* functions are working with the cast result + char* actualId = libcmis_object_getId( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Folder::Id" ), string( actualId ) ); + free( actualId ); + + // Free it all + libcmis_folder_free( actual ); + libcmis_object_free( tested ); +} + +void FolderTest::objectCastFailureTest( ) +{ + // Create the test object to cast + libcmis_ObjectPtr tested = new libcmis_object( ); + libcmis::DocumentPtr handle( new dummies::Document( true, false ) ); + tested->handle = handle; + + // Test libcmis_is_folder + CPPUNIT_ASSERT( !libcmis_is_folder( tested ) ); + + // Actually cast to a folder + libcmis_FolderPtr actual = libcmis_folder_cast( tested ); + + // Check the result + CPPUNIT_ASSERT( NULL == actual ); + + libcmis_object_free( tested ); +} + +void FolderTest::objectFunctionsTest( ) +{ + libcmis_FolderPtr tested = getTested( false, false ); + char* actual = libcmis_object_getId( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Folder::Id" ), string( actual ) ); + free( actual ); + libcmis_folder_free( tested ); +} + +void FolderTest::getParentTest( ) +{ + libcmis_FolderPtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_FolderPtr parent = libcmis_folder_getParent( tested, error ); + CPPUNIT_ASSERT( NULL != parent ); + CPPUNIT_ASSERT( !libcmis_folder_isRootFolder( tested ) ); + libcmis_folder_free( parent ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::getParentRootTest( ) +{ + libcmis_FolderPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_FolderPtr parent = libcmis_folder_getParent( tested, error ); + CPPUNIT_ASSERT( NULL == parent ); + CPPUNIT_ASSERT( libcmis_folder_isRootFolder( tested ) ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::getParentErrorTest( ) +{ + libcmis_FolderPtr tested = getTested( false, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_FolderPtr parent = libcmis_folder_getParent( tested, error ); + CPPUNIT_ASSERT( NULL == parent ); + const char* actualMessage = libcmis_error_getMessage( error ); + CPPUNIT_ASSERT( !string( actualMessage ).empty( ) ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::getChildrenTest( ) +{ + libcmis_FolderPtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + libcmis_vector_object_Ptr children = libcmis_folder_getChildren( tested, error ); + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), libcmis_vector_object_size( children ) ); + libcmis_vector_object_free( children ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::getChildrenErrorTest( ) +{ + libcmis_FolderPtr tested = getTested( false, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + libcmis_vector_object_Ptr children = libcmis_folder_getChildren( tested, error ); + CPPUNIT_ASSERT( NULL == children ); + const char* actualMessage = libcmis_error_getMessage( error ); + CPPUNIT_ASSERT( !string( actualMessage ).empty( ) ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::getPathTest( ) +{ + libcmis_FolderPtr tested = getTested( false, false ); + char* actual = libcmis_folder_getPath( tested ); + CPPUNIT_ASSERT_EQUAL( string( "/Path/" ), string( actual ) ); + free( actual ); + libcmis_folder_free( tested ); +} + +void FolderTest::createFolderTest( ) +{ + libcmis_FolderPtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Create the properties for the new folder + libcmis_vector_property_Ptr properties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + const char* id = "Property1"; + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, id ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr property = libcmis_property_create( propertyType, values, size ); + delete[] values; + libcmis_vector_property_append( properties, property ); + + // Create the new folder (method to test) + libcmis_FolderPtr created = libcmis_folder_createFolder( tested, properties, error ); + + // Check + CPPUNIT_ASSERT( NULL != created ); + + // Free everything + libcmis_folder_free( created ); + libcmis_property_free( property ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( properties ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::createFolderErrorTest( ) +{ + libcmis_FolderPtr tested = getTested( false, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Create the properties for the new folder + libcmis_vector_property_Ptr properties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + const char* id = "Property1"; + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, id ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr property = libcmis_property_create( propertyType, values, size ); + delete[] values; + libcmis_vector_property_append( properties, property ); + + // Create the new folder (method to test) + libcmis_FolderPtr created = libcmis_folder_createFolder( tested, properties, error ); + + // Check + CPPUNIT_ASSERT( NULL == created ); + + const char* actualMessage = libcmis_error_getMessage( error ); + CPPUNIT_ASSERT( !string( actualMessage ).empty( ) ); + + // Free everything + libcmis_folder_free( created ); + libcmis_property_free( property ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( properties ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::createDocumentTest( ) +{ + libcmis_FolderPtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Prepare the content to set + FILE* tmp = tmpfile( ); + string expectedStream( "New Content Stream" ); + fwrite( expectedStream.c_str( ), 1, expectedStream.size( ), tmp ); + rewind( tmp ); + + // Create the properties for the new version + libcmis_vector_property_Ptr properties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + const char* id = "Property1"; + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, id ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr property = libcmis_property_create( propertyType, values, size ); + delete[] values; + libcmis_vector_property_append( properties, property ); + + // get the content into a temporary file (tested method) + const char* contentType = "content/type"; + const char* filename = "name.txt"; + libcmis_DocumentPtr actual = libcmis_folder_createDocument( tested, properties, + ( libcmis_readFn )fread, tmp, contentType, filename, error ); + fclose( tmp ); + + // Check + string actualStream = getDocumentImplementation( actual )->getContentString( ); + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT_EQUAL( expectedStream, actualStream ); + + libcmis_PropertyPtr checkedProperty = libcmis_object_getProperty( actual, "Property1" ); + libcmis_vector_string_Ptr newValues = libcmis_property_getStrings( checkedProperty ); + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), libcmis_vector_string_size( newValues ) ); + + // Free it all + libcmis_vector_string_free( newValues ); + libcmis_property_free( checkedProperty ); + libcmis_document_free( actual ); + libcmis_property_free( property ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( properties ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::createDocumentErrorTest( ) +{ + libcmis_FolderPtr tested = getTested( true, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Prepare the content to set + FILE* tmp = tmpfile( ); + string newStream( "New Content Stream" ); + fwrite( newStream.c_str( ), 1, newStream.size( ), tmp ); + rewind( tmp ); + + // Create the properties for the new version + libcmis_vector_property_Ptr properties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + const char* id = "Property1"; + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, id ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr property = libcmis_property_create( propertyType, values, size ); + delete[] values; + libcmis_vector_property_append( properties, property ); + + // get the content into a temporary file (tested method) + const char* contentType = "content/type"; + const char* filename = "name.txt"; + libcmis_DocumentPtr actual = libcmis_folder_createDocument( tested, properties, + ( libcmis_readFn )fread, tmp, contentType, filename, error ); + fclose( tmp ); + + // Check + CPPUNIT_ASSERT( !string( libcmis_error_getMessage( error ) ).empty( ) ); + CPPUNIT_ASSERT( NULL == actual ); + + // Free it all + libcmis_property_free( property ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( properties ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::removeTreeTest( ) +{ + libcmis_FolderPtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + + // Remove the tree (method to test) + libcmis_vector_string_Ptr failed = libcmis_folder_removeTree( tested, true, libcmis_Delete, true, error ); + + // Check + CPPUNIT_ASSERT_MESSAGE( "Timestamp not updated", 0 != libcmis_object_getRefreshTimestamp( tested ) ); + CPPUNIT_ASSERT_EQUAL( size_t( 1 ), libcmis_vector_string_size( failed ) ); + CPPUNIT_ASSERT_EQUAL( string( "failed 1" ), string( libcmis_vector_string_get( failed, 0 ) ) ); + + // Free everything + libcmis_vector_string_free( failed ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} + +void FolderTest::removeTreeErrorTest( ) +{ + libcmis_FolderPtr tested = getTested( false, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Remove the tree (method to test) + libcmis_vector_string_Ptr failed = libcmis_folder_removeTree( + tested, true, libcmis_Delete, true, error ); + + // Check + const char* actualMessage = libcmis_error_getMessage( error ); + CPPUNIT_ASSERT( !string( actualMessage ).empty( ) ); + + // Free everything + libcmis_vector_string_free( failed ); + libcmis_error_free( error ); + libcmis_folder_free( tested ); +} diff --git a/qa/libcmis-c/test-object-type.cxx b/qa/libcmis-c/test-object-type.cxx new file mode 100644 index 0000000..99a0768 --- /dev/null +++ b/qa/libcmis-c/test-object-type.cxx @@ -0,0 +1,377 @@ + +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/error.h> +#include <libcmis-c/object-type.h> +#include <libcmis-c/property-type.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class ObjectTypeTest : public CppUnit::TestFixture +{ + private: + libcmis_ObjectTypePtr getTested( bool rootType, bool triggersFaults ); + + public: + void getIdTest( ); + void getLocalNameTest( ); + void getLocalNamespaceTest( ); + void getQueryNameTest( ); + void getDisplayNameTest( ); + void getDescriptionTest( ); + void getParentTypeTest( ); + void getParentTypeRootTest( ); + void getParentTypeErrorTest( ); + void getBaseTypeTest( ); + void getBaseTypeErrorTest( ); + void getChildrenTest( ); + void getChildrenErrorTest( ); + void isCreatableTest( ); + void isFileableTest( ); + void isQueryableTest( ); + void isFulltextIndexedTest( ); + void isIncludedInSupertypeQueryTest( ); + void isControllablePolicyTest( ); + void isControllableACLTest( ); + void isVersionableTest( ); + void getContentStreamAllowedTest( ); + void getPropertiesTypesTest( ); + void getPropertyTypeTest( ); + void toStringTest( ); + + // TODO Add more tests + + CPPUNIT_TEST_SUITE( ObjectTypeTest ); + CPPUNIT_TEST( getIdTest ); + CPPUNIT_TEST( getLocalNameTest ); + CPPUNIT_TEST( getLocalNamespaceTest ); + CPPUNIT_TEST( getQueryNameTest ); + CPPUNIT_TEST( getDisplayNameTest ); + CPPUNIT_TEST( getDescriptionTest ); + CPPUNIT_TEST( getParentTypeTest ); + CPPUNIT_TEST( getParentTypeRootTest ); + CPPUNIT_TEST( getParentTypeErrorTest ); + CPPUNIT_TEST( getBaseTypeTest ); + CPPUNIT_TEST( getBaseTypeErrorTest ); + CPPUNIT_TEST( getChildrenTest ); + CPPUNIT_TEST( getChildrenErrorTest ); + CPPUNIT_TEST( isCreatableTest ); + CPPUNIT_TEST( isFileableTest ); + CPPUNIT_TEST( isQueryableTest ); + CPPUNIT_TEST( isFulltextIndexedTest ); + CPPUNIT_TEST( isIncludedInSupertypeQueryTest ); + CPPUNIT_TEST( isControllablePolicyTest ); + CPPUNIT_TEST( isControllableACLTest ); + CPPUNIT_TEST( isVersionableTest ); + CPPUNIT_TEST( getContentStreamAllowedTest ); + CPPUNIT_TEST( getPropertiesTypesTest ); + CPPUNIT_TEST( getPropertyTypeTest ); + CPPUNIT_TEST( toStringTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( ObjectTypeTest ); + +libcmis_ObjectTypePtr ObjectTypeTest::getTested( bool rootType, bool triggersFaults ) +{ + libcmis_ObjectTypePtr result = new libcmis_object_type( ); + libcmis::ObjectTypePtr handle( new dummies::ObjectType( rootType, triggersFaults ) ); + result->handle = handle; + + return result; +} + +void ObjectTypeTest::getIdTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + char* id = libcmis_object_type_getId( tested ); + CPPUNIT_ASSERT_EQUAL( + string( "ObjectType::Id" ), + string( id ) ); + free( id ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getLocalNameTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + char* actual = libcmis_object_type_getLocalName( tested ); + CPPUNIT_ASSERT_EQUAL( + string( "ObjectType::LocalName" ), + string( actual ) ); + free( actual ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getLocalNamespaceTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + char* actual = libcmis_object_type_getLocalNamespace( tested ); + CPPUNIT_ASSERT_EQUAL( + string( "ObjectType::LocalNamespace" ), + string( actual ) ); + free( actual ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getQueryNameTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + char* actual = libcmis_object_type_getQueryName( tested ); + CPPUNIT_ASSERT_EQUAL( + string( "ObjectType::QueryName" ), + string( actual ) ); + free( actual ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getDisplayNameTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + char* actual = libcmis_object_type_getDisplayName( tested ); + CPPUNIT_ASSERT_EQUAL( + string( "ObjectType::DisplayName" ), + string( actual ) ); + free( actual ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getDescriptionTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + char* actual = libcmis_object_type_getDescription( tested ); + CPPUNIT_ASSERT_EQUAL( + string( "ObjectType::Description" ), + string( actual ) ); + free( actual ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getParentTypeTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_ObjectTypePtr parent = libcmis_object_type_getParentType( tested, error ); + + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + char* id = libcmis_object_type_getId( parent ); + CPPUNIT_ASSERT_EQUAL( string( "ParentType::Id" ), string( id ) ); + free( id ); + + libcmis_error_free( error ); + libcmis_object_type_free( parent ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getParentTypeRootTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( true, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_ObjectTypePtr parent = libcmis_object_type_getParentType( tested, error ); + + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT( NULL == parent ); + + libcmis_error_free( error ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getParentTypeErrorTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_ObjectTypePtr parent = libcmis_object_type_getParentType( tested, error ); + + CPPUNIT_ASSERT( NULL != libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT( NULL == parent ); + + libcmis_error_free( error ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getBaseTypeTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_ObjectTypePtr base = libcmis_object_type_getBaseType( tested, error ); + + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + char* id = libcmis_object_type_getId( base ); + CPPUNIT_ASSERT_EQUAL( string( "RootType::Id" ), string( id ) ); + free( id ); + + libcmis_error_free( error ); + libcmis_object_type_free( base ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getBaseTypeErrorTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_ObjectTypePtr base = libcmis_object_type_getBaseType( tested, error ); + + CPPUNIT_ASSERT( NULL != libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT( NULL == base ); + + libcmis_error_free( error ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getChildrenTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_vector_object_type_Ptr children = libcmis_object_type_getChildren( tested, error ); + + CPPUNIT_ASSERT( NULL == libcmis_error_getMessage( error ) ); + size_t size = libcmis_vector_object_type_size( children ); + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), size ); + + libcmis_error_free( error ); + libcmis_vector_object_type_free( children ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getChildrenErrorTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, true ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_vector_object_type_Ptr children = libcmis_object_type_getChildren( tested, error ); + + CPPUNIT_ASSERT( NULL != libcmis_error_getMessage( error ) ); + CPPUNIT_ASSERT( NULL == children ); + + libcmis_error_free( error ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isCreatableTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isCreatable( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isFileableTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isFileable( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isQueryableTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isQueryable( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isFulltextIndexedTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isFulltextIndexed( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isIncludedInSupertypeQueryTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isIncludedInSupertypeQuery( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isControllablePolicyTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isControllablePolicy( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isControllableACLTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isControllableACL( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::isVersionableTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT( libcmis_object_type_isVersionable( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getContentStreamAllowedTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + CPPUNIT_ASSERT_EQUAL( + libcmis_Allowed, + libcmis_object_type_getContentStreamAllowed( tested ) ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getPropertiesTypesTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + libcmis_vector_property_type_Ptr propertiesTypes = libcmis_object_type_getPropertiesTypes( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 3 ), libcmis_vector_property_type_size( propertiesTypes ) ); + libcmis_vector_property_type_free( propertiesTypes ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::getPropertyTypeTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + string id( "Property2" ); + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( tested, id.c_str( ) ); + char* propertyId = libcmis_property_type_getId( propertyType ); + CPPUNIT_ASSERT_EQUAL( id, string( propertyId ) ); + free( propertyId ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( tested ); +} + +void ObjectTypeTest::toStringTest( ) +{ + libcmis_ObjectTypePtr tested = getTested( false, false ); + char* actual = libcmis_object_type_toString( tested ); + CPPUNIT_ASSERT_EQUAL( + string( "ObjectType::toString" ), + string( actual ) ); + free( actual ); + libcmis_object_type_free( tested ); +} diff --git a/qa/libcmis-c/test-object.cxx b/qa/libcmis-c/test-object.cxx new file mode 100644 index 0000000..1b7324b --- /dev/null +++ b/qa/libcmis-c/test-object.cxx @@ -0,0 +1,425 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/allowable-actions.h> +#include <libcmis-c/error.h> +#include <libcmis-c/folder.h> +#include <libcmis-c/object.h> +#include <libcmis-c/object-type.h> +#include <libcmis-c/property.h> +#include <libcmis-c/property-type.h> +#include <libcmis-c/vectors.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class ObjectTest : public CppUnit::TestFixture +{ + private: + libcmis_ObjectPtr getTested( bool triggersFaults ); + libcmis_FolderPtr getTestFolder( ); + + public: + void getIdTest( ); + void getNameTest( ); + void getPathsTest( ); + void getBaseTypeTest( ); + void getTypeTest( ); + void getCreatedByTest( ); + void getCreationDateTest( ); + void getLastModifiedByTest( ); + void getLastModificationDateTest( ); + void getChangeTokenTest( ); + void isImmutableTest( ); + void getPropertiesTest( ); + void getPropertyTest( ); + void getPropertyMissingTest( ); + void updatePropertiesTest( ); + void updatePropertiesErrorTest( ); + void getTypeDescriptionTest( ); + void getAllowableActionsTest( ); + void refreshTest( ); + void refreshErrorTest( ); + void removeTest( ); + void removeErrorTest( ); + void moveTest( ); + void moveErrorTest( ); + void toStringTest( ); + + CPPUNIT_TEST_SUITE( ObjectTest ); + CPPUNIT_TEST( getIdTest ); + CPPUNIT_TEST( getNameTest ); + CPPUNIT_TEST( getPathsTest ); + CPPUNIT_TEST( getBaseTypeTest ); + CPPUNIT_TEST( getTypeTest ); + CPPUNIT_TEST( getCreatedByTest ); + CPPUNIT_TEST( getCreationDateTest ); + CPPUNIT_TEST( getLastModifiedByTest ); + CPPUNIT_TEST( getLastModificationDateTest ); + CPPUNIT_TEST( getChangeTokenTest ); + CPPUNIT_TEST( isImmutableTest ); + CPPUNIT_TEST( getPropertiesTest ); + CPPUNIT_TEST( getPropertyTest ); + CPPUNIT_TEST( getPropertyMissingTest ); + CPPUNIT_TEST( updatePropertiesTest ); + CPPUNIT_TEST( updatePropertiesErrorTest ); + CPPUNIT_TEST( getTypeDescriptionTest ); + CPPUNIT_TEST( getAllowableActionsTest ); + CPPUNIT_TEST( refreshTest ); + CPPUNIT_TEST( refreshErrorTest ); + CPPUNIT_TEST( removeTest ); + CPPUNIT_TEST( removeErrorTest ); + CPPUNIT_TEST( moveTest ); + CPPUNIT_TEST( moveErrorTest ); + CPPUNIT_TEST( toStringTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( ObjectTest ); + +libcmis_ObjectPtr ObjectTest::getTested( bool triggersFaults ) +{ + libcmis_ObjectPtr result = new libcmis_object( ); + libcmis::ObjectPtr handle( new dummies::Object( triggersFaults ) ); + result->handle = handle; + + return result; +} + +libcmis_FolderPtr ObjectTest::getTestFolder( ) +{ + libcmis_FolderPtr result = new libcmis_folder( ); + libcmis::FolderPtr handle( new dummies::Folder( false, false ) ); + result->handle = handle; + + return result; +} + +void ObjectTest::getIdTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_getId( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::Id" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getNameTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_getName( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::Name" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getPathsTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + libcmis_vector_string_Ptr actual = libcmis_object_getPaths( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), libcmis_vector_string_size( actual ) ); + CPPUNIT_ASSERT_EQUAL( + string( "/Path1/" ), + string( libcmis_vector_string_get( actual, 0 ) ) ); + libcmis_vector_string_free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getBaseTypeTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_getBaseType( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::BaseType" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getTypeTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_getType( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::Type" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getCreatedByTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_getCreatedBy( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::CreatedBy" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getCreationDateTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + time_t actual = libcmis_object_getCreationDate( tested ); + CPPUNIT_ASSERT( 0 != actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getLastModifiedByTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_getLastModifiedBy( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::LastModifiedBy" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getLastModificationDateTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + time_t actual = libcmis_object_getLastModificationDate( tested ); + CPPUNIT_ASSERT( 0 != actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getChangeTokenTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_getChangeToken( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::ChangeToken" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::isImmutableTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + CPPUNIT_ASSERT( libcmis_object_isImmutable( tested ) ); + libcmis_object_free( tested ); +} + +void ObjectTest::getPropertiesTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + libcmis_vector_property_Ptr actual = libcmis_object_getProperties( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 1 ), libcmis_vector_property_size( actual ) ); + libcmis_vector_property_free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getPropertyTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + const char* id = "Property1"; + libcmis_PropertyPtr actual = libcmis_object_getProperty( tested, id ); + CPPUNIT_ASSERT( NULL != actual ); + libcmis_property_free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getPropertyMissingTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + const char* id = "MissingProperty"; + libcmis_PropertyPtr actual = libcmis_object_getProperty( tested, id ); + CPPUNIT_ASSERT( NULL == actual ); + libcmis_property_free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::updatePropertiesTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Create the changed properties map + libcmis_vector_property_Ptr newProperties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, "cmis:Property2" ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr newProperty = libcmis_property_create( propertyType, values, size ); + delete[ ] values; + libcmis_vector_property_append( newProperties, newProperty ); + + // Update the properties (method under test) + libcmis_ObjectPtr updated = libcmis_object_updateProperties( tested, newProperties, error ); + + // Checks + CPPUNIT_ASSERT_MESSAGE( "Timestamp not updated", 0 != libcmis_object_getRefreshTimestamp( tested ) ); + CPPUNIT_ASSERT( updated != NULL ); + + // Free it all + libcmis_object_free( updated ); + libcmis_property_free( newProperty ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( newProperties ); + libcmis_error_free( error ); + libcmis_object_free( tested ); +} + +void ObjectTest::updatePropertiesErrorTest( ) +{ + libcmis_ObjectPtr tested = getTested( true ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Create the changed properties map + libcmis_vector_property_Ptr newProperties = libcmis_vector_property_create( ); + libcmis_ObjectTypePtr objectType = libcmis_object_getTypeDescription( tested ); + libcmis_PropertyTypePtr propertyType = libcmis_object_type_getPropertyType( objectType, "cmis:Property2" ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "Value 1"; + values[1] = "Value 2"; + libcmis_PropertyPtr newProperty = libcmis_property_create( propertyType, values, size ); + delete[ ] values; + libcmis_vector_property_append( newProperties, newProperty ); + + // Update the properties (method under test) + libcmis_ObjectPtr updated = libcmis_object_updateProperties( tested, newProperties, error ); + + // Checks + CPPUNIT_ASSERT( updated == NULL ); + const char* actualMessage = libcmis_error_getMessage( error ); + CPPUNIT_ASSERT( !string( actualMessage ).empty( ) ); + + // Free it all + libcmis_object_free( updated ); + libcmis_property_free( newProperty ); + libcmis_property_type_free( propertyType ); + libcmis_object_type_free( objectType ); + libcmis_vector_property_free( newProperties ); + libcmis_error_free( error ); + libcmis_object_free( tested ); +} + +void ObjectTest::getTypeDescriptionTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + libcmis_ObjectTypePtr actual = libcmis_object_getTypeDescription( tested ); + char* actualId = libcmis_object_type_getId( actual ); + CPPUNIT_ASSERT( !string( actualId ).empty( ) ); + free( actualId ); + libcmis_object_type_free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::getAllowableActionsTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + libcmis_AllowableActionsPtr actual = libcmis_object_getAllowableActions( tested ); + CPPUNIT_ASSERT( libcmis_allowable_actions_isDefined( actual, libcmis_GetFolderParent ) ); + libcmis_allowable_actions_free( actual ); + libcmis_object_free( tested ); +} + +void ObjectTest::refreshTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_object_refresh( tested, error ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not updated", 0 != libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_error_free( error ); + libcmis_object_free( tested ); +} + +void ObjectTest::refreshErrorTest( ) +{ + libcmis_ObjectPtr tested = getTested( true ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_object_refresh( tested, error ); + const char* actualMessage = libcmis_error_getMessage( error ); + CPPUNIT_ASSERT( !string( actualMessage ).empty( ) ); + libcmis_error_free( error ); + libcmis_object_free( tested ); +} + +void ObjectTest::removeTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_object_remove( tested, true, error ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not updated", 0 != libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_error_free( error ); + libcmis_object_free( tested ); +} + +void ObjectTest::removeErrorTest( ) +{ + libcmis_ObjectPtr tested = getTested( true ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_ErrorPtr error = libcmis_error_create( ); + libcmis_object_remove( tested, true, error ); + const char* actualMessage = libcmis_error_getMessage( error ); + CPPUNIT_ASSERT( !string( actualMessage ).empty( ) ); + libcmis_error_free( error ); + libcmis_object_free( tested ); +} + +void ObjectTest::moveTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + CPPUNIT_ASSERT_MESSAGE( "Timestamp not set to 0 initially", 0 == libcmis_object_getRefreshTimestamp( tested ) ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + // Move the object from source to dest (tested method) + libcmis_FolderPtr source = getTestFolder( ); + libcmis_FolderPtr dest = getTestFolder( ); + libcmis_object_move( tested, source, dest, error ); + + // Check + CPPUNIT_ASSERT_MESSAGE( "Timestamp not updated", 0 != libcmis_object_getRefreshTimestamp( tested ) ); + + // Free it all + libcmis_folder_free( dest ); + libcmis_folder_free( source ); + libcmis_error_free( error ); + libcmis_object_free( tested ); +} + +void ObjectTest::moveErrorTest( ) +{ +} + +void ObjectTest::toStringTest( ) +{ + libcmis_ObjectPtr tested = getTested( false ); + char* actual = libcmis_object_toString( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Object::toString" ), string( actual ) ); + free( actual ); + libcmis_object_free( tested ); +} diff --git a/qa/libcmis-c/test-property-type.cxx b/qa/libcmis-c/test-property-type.cxx new file mode 100644 index 0000000..c8832f6 --- /dev/null +++ b/qa/libcmis-c/test-property-type.cxx @@ -0,0 +1,251 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/property-type.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class PropertyTypeTest : public CppUnit::TestFixture +{ + private: + libcmis_PropertyTypePtr getTested( string id, string xmlType ); + + public: + void getIdTest( ); + void getLocalNameTest( ); + void getLocalNamespaceTest( ); + void getDisplayNameTest( ); + void getQueryNameTest( ); + void getTypeTest( ); + void isMultiValuedTest( ); + void isUpdatableTest( ); + void isInheritedTest( ); + void isRequiredTest( ); + void isQueryableTest( ); + void isOrderableTest( ); + void isOpenChoiceTest( ); + + CPPUNIT_TEST_SUITE( PropertyTypeTest ); + CPPUNIT_TEST( getIdTest ); + CPPUNIT_TEST( getLocalNameTest ); + CPPUNIT_TEST( getLocalNamespaceTest ); + CPPUNIT_TEST( getDisplayNameTest ); + CPPUNIT_TEST( getQueryNameTest ); + CPPUNIT_TEST( getTypeTest ); + CPPUNIT_TEST( isMultiValuedTest ); + CPPUNIT_TEST( isUpdatableTest ); + CPPUNIT_TEST( isInheritedTest ); + CPPUNIT_TEST( isRequiredTest ); + CPPUNIT_TEST( isQueryableTest ); + CPPUNIT_TEST( isOrderableTest ); + CPPUNIT_TEST( isOpenChoiceTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( PropertyTypeTest ); + +libcmis_PropertyTypePtr PropertyTypeTest::getTested( string id, string xmlType ) +{ + libcmis_PropertyTypePtr result = new libcmis_property_type( ); + libcmis::PropertyTypePtr handle( new dummies::PropertyType( id, xmlType ) ); + result->handle = handle; + + return result; +} + +void PropertyTypeTest::getIdTest( ) +{ + string id( "Id" ); + libcmis_PropertyTypePtr tested = getTested( id, "string" ); + char* actual = libcmis_property_type_getId( tested ); + CPPUNIT_ASSERT_EQUAL( id, string( actual ) ); + + free( actual ); + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::getLocalNameTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + char* actual = libcmis_property_type_getLocalName( tested ); + CPPUNIT_ASSERT_EQUAL( string( "PropertyType::LocalName" ), string( actual ) ); + + free( actual ); + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::getLocalNamespaceTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + char* actual = libcmis_property_type_getLocalNamespace( tested ); + CPPUNIT_ASSERT_EQUAL( string( "PropertyType::LocalNamespace" ), string( actual ) ); + + free( actual ); + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::getDisplayNameTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + char* actual = libcmis_property_type_getDisplayName( tested ); + CPPUNIT_ASSERT_EQUAL( string( "PropertyType::DisplayName" ), string( actual ) ); + + free( actual ); + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::getQueryNameTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + char* actual = libcmis_property_type_getQueryName( tested ); + CPPUNIT_ASSERT_EQUAL( string( "PropertyType::QueryName" ), string( actual ) ); + + free( actual ); + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::getTypeTest( ) +{ + // String + { + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + libcmis_property_type_Type actualType = libcmis_property_type_getType( tested ); + CPPUNIT_ASSERT_EQUAL( libcmis_String, actualType ); + char* actualXml = libcmis_property_type_getXmlType( tested ); + CPPUNIT_ASSERT_EQUAL( string( "String" ), string( actualXml ) ); + + free( actualXml ); + libcmis_property_type_free( tested ); + } + + // DateTime + { + libcmis_PropertyTypePtr tested = getTested( "id", "datetime" ); + libcmis_property_type_Type actualType = libcmis_property_type_getType( tested ); + CPPUNIT_ASSERT_EQUAL( libcmis_DateTime, actualType ); + char* actualXml = libcmis_property_type_getXmlType( tested ); + CPPUNIT_ASSERT_EQUAL( string( "DateTime" ), string( actualXml ) ); + + free( actualXml ); + libcmis_property_type_free( tested ); + } + + // Integer + { + libcmis_PropertyTypePtr tested = getTested( "id", "integer" ); + libcmis_property_type_Type actualType = libcmis_property_type_getType( tested ); + CPPUNIT_ASSERT_EQUAL( libcmis_Integer, actualType ); + char* actualXml = libcmis_property_type_getXmlType( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Integer" ), string( actualXml ) ); + + free( actualXml ); + libcmis_property_type_free( tested ); + } + + // Html + { + libcmis_PropertyTypePtr tested = getTested( "id", "html" ); + libcmis_property_type_Type actualType = libcmis_property_type_getType( tested ); + CPPUNIT_ASSERT_EQUAL( libcmis_String, actualType ); + char* actualXml = libcmis_property_type_getXmlType( tested ); + CPPUNIT_ASSERT_EQUAL( string( "Html" ), string( actualXml ) ); + + free( actualXml ); + libcmis_property_type_free( tested ); + } +} + +void PropertyTypeTest::isMultiValuedTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + bool actual = libcmis_property_type_isMultiValued( tested ); + CPPUNIT_ASSERT_EQUAL( true , actual ); + + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::isUpdatableTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + bool actual = libcmis_property_type_isUpdatable( tested ); + CPPUNIT_ASSERT_EQUAL( true , actual ); + + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::isInheritedTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + bool actual = libcmis_property_type_isInherited( tested ); + CPPUNIT_ASSERT_EQUAL( true , actual ); + + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::isRequiredTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + bool actual = libcmis_property_type_isRequired( tested ); + CPPUNIT_ASSERT_EQUAL( true , actual ); + + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::isQueryableTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + bool actual = libcmis_property_type_isQueryable( tested ); + CPPUNIT_ASSERT_EQUAL( true , actual ); + + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::isOrderableTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + bool actual = libcmis_property_type_isOrderable( tested ); + CPPUNIT_ASSERT_EQUAL( true , actual ); + + libcmis_property_type_free( tested ); +} + +void PropertyTypeTest::isOpenChoiceTest( ) +{ + libcmis_PropertyTypePtr tested = getTested( "id", "string" ); + bool actual = libcmis_property_type_isOpenChoice( tested ); + CPPUNIT_ASSERT_EQUAL( true , actual ); + + libcmis_property_type_free( tested ); +} diff --git a/qa/libcmis-c/test-property.cxx b/qa/libcmis-c/test-property.cxx new file mode 100644 index 0000000..99c9b2c --- /dev/null +++ b/qa/libcmis-c/test-property.cxx @@ -0,0 +1,242 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/property.h> +#include <libcmis-c/property-type.h> +#include <libcmis-c/vectors.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class PropertyTest : public CppUnit::TestFixture +{ + private: + libcmis_PropertyTypePtr getTestType( string xmlType ); + + public: + void getDateTimesTest( ); + void getBoolsTest( ); + void getStringsTest( ); + void getLongsTest( ); + void getDoublesTest( ); + void setValuesTest( ); + + CPPUNIT_TEST_SUITE( PropertyTest ); + CPPUNIT_TEST( getDateTimesTest ); + CPPUNIT_TEST( getBoolsTest ); + CPPUNIT_TEST( getStringsTest ); + CPPUNIT_TEST( getLongsTest ); + CPPUNIT_TEST( getDoublesTest ); + CPPUNIT_TEST( setValuesTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( PropertyTest ); + +libcmis_PropertyTypePtr PropertyTest::getTestType( string xmlType ) +{ + libcmis_PropertyTypePtr result = new libcmis_property_type( ); + libcmis::PropertyTypePtr handle( new dummies::PropertyType( "Id", xmlType ) ); + result->handle = handle; + + return result; +} + +void PropertyTest::getDateTimesTest( ) +{ + libcmis_PropertyTypePtr type = getTestType( "datetime" ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "2012-01-19T09:06:57.388Z"; + values[1] = "2012-02-19T09:06:57.388Z"; + libcmis_PropertyPtr tested = libcmis_property_create( type, values, size ); + + libcmis_vector_time_Ptr times = libcmis_property_getDateTimes( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_time_size( times ) ); + libcmis_vector_time_free( times ); + + libcmis_vector_string_Ptr strings = libcmis_property_getStrings( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_string_size( strings ) ); + CPPUNIT_ASSERT_EQUAL( string( values[1] ), string( libcmis_vector_string_get( strings, 1 ) ) ); + libcmis_vector_string_free( strings ); + delete[] values; + + libcmis_vector_bool_Ptr bools = libcmis_property_getBools( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 0 ), libcmis_vector_bool_size( bools ) ); + libcmis_vector_bool_free( bools ); + + libcmis_property_free( tested ); + libcmis_property_type_free( type ); +} + +void PropertyTest::getBoolsTest( ) +{ + libcmis_PropertyTypePtr type = getTestType( "boolean" ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "true"; + values[1] = "false"; + libcmis_PropertyPtr tested = libcmis_property_create( type, values, size ); + + libcmis_vector_bool_Ptr bools = libcmis_property_getBools( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_bool_size( bools ) ); + CPPUNIT_ASSERT_EQUAL( true, libcmis_vector_bool_get( bools, 0 ) ); + CPPUNIT_ASSERT_EQUAL( false, libcmis_vector_bool_get( bools, 1 ) ); + libcmis_vector_bool_free( bools ); + + libcmis_vector_string_Ptr strings = libcmis_property_getStrings( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_string_size( strings ) ); + CPPUNIT_ASSERT_EQUAL( string( values[1] ), string( libcmis_vector_string_get( strings, 1 ) ) ); + libcmis_vector_string_free( strings ); + delete[] values; + + libcmis_vector_long_Ptr longs = libcmis_property_getLongs( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 0 ), libcmis_vector_long_size( longs ) ); + libcmis_vector_long_free( longs ); + + libcmis_property_free( tested ); + libcmis_property_type_free( type ); +} + + +void PropertyTest::getStringsTest( ) +{ + libcmis_PropertyTypePtr type = getTestType( "string" ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "string 1"; + values[1] = "string 2"; + libcmis_PropertyPtr tested = libcmis_property_create( type, values, size ); + + libcmis_vector_string_Ptr strings = libcmis_property_getStrings( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_string_size( strings ) ); + CPPUNIT_ASSERT_EQUAL( string( values[0] ), string( libcmis_vector_string_get( strings, 0 ) ) ); + CPPUNIT_ASSERT_EQUAL( string( values[1] ), string( libcmis_vector_string_get( strings, 1 ) ) ); + libcmis_vector_string_free( strings ); + delete[] values; + + libcmis_vector_double_Ptr doubles = libcmis_property_getDoubles( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 0 ), libcmis_vector_double_size( doubles ) ); + libcmis_vector_double_free( doubles ); + + libcmis_property_free( tested ); + libcmis_property_type_free( type ); +} + + +void PropertyTest::getLongsTest( ) +{ + libcmis_PropertyTypePtr type = getTestType( "integer" ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "123456"; + values[1] = "789"; + libcmis_PropertyPtr tested = libcmis_property_create( type, values, size ); + + libcmis_vector_long_Ptr longs = libcmis_property_getLongs( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_long_size( longs ) ); + CPPUNIT_ASSERT_EQUAL( long( 123456 ), libcmis_vector_long_get( longs, 0 ) ); + CPPUNIT_ASSERT_EQUAL( long( 789 ), libcmis_vector_long_get( longs, 1 ) ); + libcmis_vector_long_free( longs ); + + libcmis_vector_string_Ptr strings = libcmis_property_getStrings( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_string_size( strings ) ); + CPPUNIT_ASSERT_EQUAL( string( values[1] ), string( libcmis_vector_string_get( strings, 1 ) ) ); + libcmis_vector_string_free( strings ); + delete[] values; + + libcmis_vector_time_Ptr times = libcmis_property_getDateTimes( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 0 ), libcmis_vector_time_size( times ) ); + libcmis_vector_time_free( times ); + + libcmis_property_free( tested ); + libcmis_property_type_free( type ); +} + + +void PropertyTest::getDoublesTest( ) +{ + libcmis_PropertyTypePtr type = getTestType( "decimal" ); + size_t size = 2; + const char** values = new const char*[size]; + values[0] = "123.456"; + values[1] = "7.89"; + libcmis_PropertyPtr tested = libcmis_property_create( type, values, size ); + + libcmis_vector_double_Ptr doubles = libcmis_property_getDoubles( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_double_size( doubles ) ); + CPPUNIT_ASSERT_EQUAL( 123.456, libcmis_vector_double_get( doubles, 0 ) ); + CPPUNIT_ASSERT_EQUAL( 7.89, libcmis_vector_double_get( doubles, 1 ) ); + libcmis_vector_double_free( doubles ); + + libcmis_vector_string_Ptr strings = libcmis_property_getStrings( tested ); + CPPUNIT_ASSERT_EQUAL( size, libcmis_vector_string_size( strings ) ); + CPPUNIT_ASSERT_EQUAL( string( values[1] ), string( libcmis_vector_string_get( strings, 1 ) ) ); + libcmis_vector_string_free( strings ); + delete[] values; + + libcmis_vector_long_Ptr longs = libcmis_property_getLongs( tested ); + CPPUNIT_ASSERT_EQUAL( size_t( 0 ), libcmis_vector_long_size( longs ) ); + libcmis_vector_long_free( longs ); + + libcmis_property_free( tested ); + libcmis_property_type_free( type ); +} + + +void PropertyTest::setValuesTest( ) +{ + libcmis_PropertyTypePtr type = getTestType( "string" ); + size_t size = 1; + const char** values = new const char*[size]; + values[0] = "string 1"; + libcmis_PropertyPtr tested = libcmis_property_create( type, values, size ); + delete[] values; + + size_t newSize = 2; + const char** newValues = new const char*[newSize]; + newValues[0] = "new string 1"; + newValues[1] = "new string 2"; + libcmis_property_setValues( tested, newValues, newSize ); + + libcmis_vector_string_Ptr newStrings = libcmis_property_getStrings( tested ); + CPPUNIT_ASSERT_EQUAL( newSize, libcmis_vector_string_size( newStrings ) ); + CPPUNIT_ASSERT_EQUAL( string( newValues[0] ), string( libcmis_vector_string_get( newStrings, 0 ) ) ); + CPPUNIT_ASSERT_EQUAL( string( newValues[1] ), string( libcmis_vector_string_get( newStrings, 1 ) ) ); + libcmis_vector_string_free( newStrings ); + delete[] newValues; + + libcmis_property_free( tested ); + libcmis_property_type_free( type ); +} diff --git a/qa/libcmis-c/test-repository.cxx b/qa/libcmis-c/test-repository.cxx new file mode 100644 index 0000000..63e7363 --- /dev/null +++ b/qa/libcmis-c/test-repository.cxx @@ -0,0 +1,205 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/repository.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class RepositoryTest : public CppUnit::TestFixture +{ + private: + libcmis_RepositoryPtr getTested( ); + + public: + void getIdTest( ); + void getNameTest( ); + void getDescriptionTest( ); + void getVendorNameTest( ); + void getProductNameTest( ); + void getProductVersionTest( ); + void getRootIdTest( ); + void getCmisVersionSupportedTest( ); + void getThinClientUriTest( ); + void getPrincipalAnonymousTest( ); + void getPrincipalAnyoneTest( ); + + CPPUNIT_TEST_SUITE( RepositoryTest ); + CPPUNIT_TEST( getIdTest ); + CPPUNIT_TEST( getNameTest ); + CPPUNIT_TEST( getDescriptionTest ); + CPPUNIT_TEST( getVendorNameTest ); + CPPUNIT_TEST( getProductNameTest ); + CPPUNIT_TEST( getProductVersionTest ); + CPPUNIT_TEST( getRootIdTest ); + CPPUNIT_TEST( getCmisVersionSupportedTest ); + CPPUNIT_TEST( getThinClientUriTest ); + CPPUNIT_TEST( getPrincipalAnonymousTest ); + CPPUNIT_TEST( getPrincipalAnyoneTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( RepositoryTest ); + +libcmis_RepositoryPtr RepositoryTest::getTested( ) +{ + libcmis_RepositoryPtr result = new libcmis_repository( ); + + libcmis::RepositoryPtr handle( new dummies::Repository( ) ); + result->handle = handle; + + return result; +} + +void RepositoryTest::getIdTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getId( tested ); + string expected( "Repository::Id" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getNameTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getName( tested ); + string expected( "Repository::Name" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getDescriptionTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getDescription( tested ); + string expected( "Repository::Description" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getVendorNameTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getVendorName( tested ); + string expected( "Repository::VendorName" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getProductNameTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getProductName( tested ); + string expected( "Repository::ProductName" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getProductVersionTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getProductVersion( tested ); + string expected( "Repository::ProductVersion" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getRootIdTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getRootId( tested ); + string expected( "Repository::RootId" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getCmisVersionSupportedTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getCmisVersionSupported( tested ); + string expected( "Repository::CmisVersionSupported" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getThinClientUriTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getThinClientUri( tested ); + string expected( "Repository::ThinClientUri" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getPrincipalAnonymousTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getPrincipalAnonymous( tested ); + string expected( "Repository::PrincipalAnonymous" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + +void RepositoryTest::getPrincipalAnyoneTest( ) +{ + libcmis_RepositoryPtr tested = getTested( ); + char* actual = libcmis_repository_getPrincipalAnyone( tested ); + string expected( "Repository::PrincipalAnyone" ); + CPPUNIT_ASSERT_EQUAL( expected, string( actual ) ); + + free( actual ); + libcmis_repository_free( tested ); +} + diff --git a/qa/libcmis-c/test-session.cxx b/qa/libcmis-c/test-session.cxx new file mode 100644 index 0000000..b236bc5 --- /dev/null +++ b/qa/libcmis-c/test-session.cxx @@ -0,0 +1,88 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <libcmis-c/libcmis-c.h> + +#include "internals.hxx" +#include "test-dummies.hxx" + +using namespace std; + +class SessionTest : public CppUnit::TestFixture +{ + private: + libcmis_SessionPtr getTested( ); + + public: + void getRepositoriesTest( ); + void getBaseTypesTest( ); + + CPPUNIT_TEST_SUITE( SessionTest ); + CPPUNIT_TEST( getRepositoriesTest ); + CPPUNIT_TEST( getBaseTypesTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( SessionTest ); + +libcmis_SessionPtr SessionTest::getTested( ) +{ + libcmis_SessionPtr result = new libcmis_session(); + libcmis::Session* handle = new dummies::Session( ); + result->handle = handle; + return result; +} + +void SessionTest::getRepositoriesTest( ) +{ + libcmis_SessionPtr session = getTested( ); + libcmis_vector_Repository_Ptr repos = libcmis_session_getRepositories( session ); + size_t actualSize = libcmis_vector_repository_size( repos ); + CPPUNIT_ASSERT_EQUAL( size_t( 2 ), actualSize ); + libcmis_vector_repository_free( repos ); + libcmis_session_free( session ); +} + +void SessionTest::getBaseTypesTest( ) +{ + libcmis_SessionPtr session = getTested( ); + libcmis_ErrorPtr error = libcmis_error_create( ); + + libcmis_vector_object_type_Ptr types = libcmis_session_getBaseTypes( session, error ); + + size_t size = libcmis_vector_object_type_size( types ); + CPPUNIT_ASSERT_EQUAL( size_t( 1 ), size ); + + libcmis_error_free( error ); + libcmis_vector_object_type_free( types ); + libcmis_session_free( session ); +} diff --git a/qa/libcmis/Makefile.am b/qa/libcmis/Makefile.am new file mode 100644 index 0000000..b300854 --- /dev/null +++ b/qa/libcmis/Makefile.am @@ -0,0 +1,190 @@ +if !OS_WIN32 +mockup_tests = \ + test-atom \ + test-factory \ + test-sharepoint \ + test-ws +endif + +# these tests need updating to work again +# mockup_tests += \ +# test-gdrive \ +# test-onedrive + +check_PROGRAMS = \ + test-json \ + test-utils \ + ${mockup_tests} + +check_LIBRARIES = \ + libtest.a + +libtest_a_SOURCES = \ + test-helpers.cxx \ + test-helpers.hxx \ + test-main.cxx + +if !OS_WIN32 +libtest_a_SOURCES += \ + test-mockup-helpers.cxx \ + test-mockup-helpers.hxx +endif + +libtest_a_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + -I$(top_srcdir)/qa/mockup \ + $(XML2_CFLAGS) \ + $(CURL_CFLAGS) \ + $(BOOST_CPPFLAGS) + +test_utils_SOURCES = \ + test-commons.cxx \ + test-decoder.cxx \ + test-soap.cxx \ + test-xmlutils.cxx + +test_utils_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + $(XML2_CFLAGS) \ + $(CURL_CFLAGS) \ + $(BOOST_CPPFLAGS) + +test_utils_LDADD = \ + libtest.a \ + $(top_builddir)/src/libcmis/libcmis.la \ + $(XML2_LIBS) \ + $(CURL_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +test_atom_SOURCES = \ + test-atom.cxx + +test_atom_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + -I$(top_srcdir)/qa/mockup \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) \ + -DDATA_DIR=\"$(top_srcdir)/qa/libcmis/data\" + +test_atom_LDADD = \ + libtest.a \ + $(top_builddir)/qa/mockup/libcmis-mockup.la \ + $(XML2_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +test_gdrive_SOURCES = \ + test-gdrive.cxx + +test_gdrive_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + -I$(top_srcdir)/qa/mockup \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) \ + -DDATA_DIR=\"$(top_srcdir)/qa/libcmis/data\" + +test_gdrive_LDADD = \ + libtest.a \ + $(top_builddir)/qa/mockup/libcmis-mockup.la \ + $(XML2_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +test_onedrive_SOURCES = \ + test-onedrive.cxx + +test_onedrive_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + -I$(top_srcdir)/qa/mockup \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) \ + -DDATA_DIR=\"$(top_srcdir)/qa/libcmis/data\" + +test_onedrive_LDADD = \ + libtest.a \ + $(top_builddir)/qa/mockup/libcmis-mockup.la \ + $(XML2_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +test_sharepoint_SOURCES = \ + test-sharepoint.cxx + +test_sharepoint_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + -I$(top_srcdir)/qa/mockup \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) \ + -DDATA_DIR=\"$(top_srcdir)/qa/libcmis/data\" + +test_sharepoint_LDADD = \ + libtest.a \ + $(top_builddir)/qa/mockup/libcmis-mockup.la \ + $(XML2_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + + +test_ws_SOURCES = \ + test-ws.cxx + +test_ws_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + -I$(top_srcdir)/qa/mockup \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) \ + -DDATA_DIR=\"$(top_srcdir)/qa/libcmis/data\" + +test_ws_LDADD = \ + libtest.a \ + $(top_builddir)/qa/mockup/libcmis-mockup.la \ + $(XML2_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +test_json_SOURCES = \ + test-jsonutils.cxx + +test_json_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) \ + -DDATA_DIR=\"$(top_srcdir)/qa/libcmis/data\" + +test_json_LDADD = \ + libtest.a \ + $(top_builddir)/src/libcmis/libcmis.la \ + $(XML2_LIBS) \ + $(CPPUNIT_LIBS) \ + $(CURL_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + + +test_factory_SOURCES = \ + test-factory.cxx + +test_factory_CPPFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + -I$(top_srcdir)/qa/mockup \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) \ + -DDATA_DIR=\"$(top_srcdir)/qa/libcmis/data\" + +test_factory_LDADD = \ + libtest.a \ + $(top_builddir)/qa/mockup/libcmis-mockup.la \ + $(XML2_LIBS) \ + $(CPPUNIT_LIBS) \ + $(BOOST_DATE_TIME_LIBS) + +TESTS = test-utils test-json ${mockup_tests} diff --git a/qa/libcmis/data/atom/allowable-actions.xml b/qa/libcmis/data/atom/allowable-actions.xml new file mode 100644 index 0000000..3e59c9d --- /dev/null +++ b/qa/libcmis/data/atom/allowable-actions.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<ns2:allowableActions xmlns="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:ns3="http://docs.oasis-open.org/ns/cmis/restatom/200908/"> + <ns2:canDeleteObject>true</ns2:canDeleteObject> + <ns2:canUpdateProperties>true</ns2:canUpdateProperties> + <ns2:canGetFolderTree>true</ns2:canGetFolderTree> + <ns2:canGetProperties>true</ns2:canGetProperties> + <ns2:canGetObjectRelationships>false</ns2:canGetObjectRelationships> + <ns2:canGetObjectParents>true</ns2:canGetObjectParents> + <ns2:canGetFolderParent>true</ns2:canGetFolderParent> + <ns2:canGetDescendants>true</ns2:canGetDescendants> + <ns2:canMoveObject>true</ns2:canMoveObject> + <ns2:canDeleteContentStream>false</ns2:canDeleteContentStream> + <ns2:canCheckOut>false</ns2:canCheckOut> + <ns2:canCancelCheckOut>false</ns2:canCancelCheckOut> + <ns2:canCheckIn>false</ns2:canCheckIn> + <ns2:canSetContentStream>false</ns2:canSetContentStream> + <ns2:canGetAllVersions>false</ns2:canGetAllVersions> + <ns2:canAddObjectToFolder>false</ns2:canAddObjectToFolder> + <ns2:canRemoveObjectFromFolder>false</ns2:canRemoveObjectFromFolder> + <ns2:canGetContentStream>false</ns2:canGetContentStream> + <ns2:canApplyPolicy>false</ns2:canApplyPolicy> + <ns2:canGetAppliedPolicies>false</ns2:canGetAppliedPolicies> + <ns2:canRemovePolicy>false</ns2:canRemovePolicy> + <ns2:canGetChildren>true</ns2:canGetChildren> + <ns2:canCreateDocument>true</ns2:canCreateDocument> + <ns2:canCreateFolder>true</ns2:canCreateFolder> + <ns2:canCreateRelationship>false</ns2:canCreateRelationship> + <ns2:canDeleteTree>true</ns2:canDeleteTree> + <ns2:canGetRenditions>false</ns2:canGetRenditions> + <ns2:canGetACL>false</ns2:canGetACL> + <ns2:canApplyACL>false</ns2:canApplyACL> +</ns2:allowableActions> diff --git a/qa/libcmis/data/atom/create-document.xml b/qa/libcmis/data/atom/create-document.xml new file mode 100644 index 0000000..73372bc --- /dev/null +++ b/qa/libcmis/data/atom/create-document.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-28T14:10:06Z</atom:published> + <atom:title>create document</atom:title> + <app:edited>2013-01-28T14:10:06Z</app:edited> + <atom:updated>2013-01-28T14:10:06Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=create-document" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>create document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206736</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>create-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=create-document" type="application/atom+xml;type=entry" cmisra:id="create-document"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=create-document" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=create-document" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:document" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=create-document" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=create-document" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=create-document" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=create-document" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/create-folder-bad-type.xml b/qa/libcmis/data/atom/create-folder-bad-type.xml new file mode 100644 index 0000000..b43401c --- /dev/null +++ b/qa/libcmis/data/atom/create-folder-bad-type.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2012-11-29T16:14:47Z</atom:published> + <atom:title>create folder</atom:title> + <app:edited>2012-11-29T16:14:47Z</app:edited> + <atom:updated>2012-11-29T16:14:47Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/create folder</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>create folder</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>create-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=create-folder" type="application/atom+xml;type=entry" cmisra:id="create-folder"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=create-folder" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=create-folder" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Adocument" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=create-folder" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=create-folder" type="application/atom+xml;type=feed"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=create-folder" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=create-folder" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/create-folder.xml b/qa/libcmis/data/atom/create-folder.xml new file mode 100644 index 0000000..e7d3f9f --- /dev/null +++ b/qa/libcmis/data/atom/create-folder.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2012-11-29T16:14:47Z</atom:published> + <atom:title>create folder</atom:title> + <app:edited>2012-11-29T16:14:47Z</app:edited> + <atom:updated>2012-11-29T16:14:47Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/create folder</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>create folder</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>create-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=create-folder" type="application/atom+xml;type=entry" cmisra:id="create-folder"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=create-folder" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=create-folder" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Afolder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=create-folder" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=create-folder" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/children?id=create-folder" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=create-folder" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=create-folder" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=create-folder" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/get-versions.xml b/qa/libcmis/data/atom/get-versions.xml new file mode 100644 index 0000000..1960975 --- /dev/null +++ b/qa/libcmis/data/atom/get-versions.xml @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:title>Test Document</atom:title> + <app:edited>2013-01-28T14:10:06Z</app:edited> + <atom:updated>2013-01-28T14:10:06Z</atom:updated> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="via" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry"/> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-28T14:10:06Z</atom:published> + <atom:title>Test Document</atom:title> + <app:edited>2013-01-28T14:10:06Z</app:edited> + <atom:updated>2013-01-28T14:10:06Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=test-document-1" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>version-series</cmis:value> + </cmis:propertyId> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"> + <cmis:value>1.0</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206736</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document-1</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document-1</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=test-document-1" type="application/atom+xml;type=entry" cmisra:id="test-document-1"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=test-document-1" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=test-document-1" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=test-document-1" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=test-document-1" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=test-document-1" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=test-document-1" type="application/cmisacl+xml"/> + </atom:entry> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-28T14:10:06Z</atom:published> + <atom:title>Test Document</atom:title> + <app:edited>2013-01-28T14:10:06Z</app:edited> + <atom:updated>2013-01-28T14:10:06Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=test-document-0" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>version-series</cmis:value> + </cmis:propertyId> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"> + <cmis:value>0.1</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-27T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206754</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document-0</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-27T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document-0</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=test-document-0" type="application/atom+xml;type=entry" cmisra:id="test-document-0"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=test-document-0" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=test-document-0" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=test-document-0" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=test-document-0" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=test-document-0" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=test-document-0" type="application/cmisacl+xml"/> + </atom:entry> +</atom:feed> diff --git a/qa/libcmis/data/atom/root-children.xml b/qa/libcmis/data/atom/root-children.xml new file mode 100644 index 0000000..cb31611 --- /dev/null +++ b/qa/libcmis/data/atom/root-children.xml @@ -0,0 +1,389 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:title>Root Folder</atom:title> + <app:edited>2013-01-30T09:26:10Z</app:edited> + <atom:updated>2013-01-30T09:26:10Z</atom:updated> + <cmisra:numItems>5</cmisra:numItems> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/children?id=root-folder" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=root-folder" type="application/cmisallowableactions+xml"/> + <atom:link rel="down" href="http://mockup/mock/children?id=root-folder" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=root-folder" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=root-folder" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=root-folder" type="application/cmisacl+xml"/> + <app:collection href="http://mockup/mock/children?id=root-folder"> + <atom:title type="text">Folder collection</atom:title> + <app:accept>application/cmisatom+xml</app:accept> + </app:collection> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-30T09:26:13Z</atom:published> + <atom:title>Child 1</atom:title> + <app:edited>2013-01-30T09:26:13Z</app:edited> + <atom:updated>2013-01-30T09:26:13Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=child1" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>33446</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 1</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:13.932Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537973932</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child1</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:13.932Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/entry?id=child1" type="application/atom+xml;type=entry" cmisra:id="child1"/> + <atom:link rel="enclosure" href="http://mockup/mock/entry?id=child1" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/entry?id=child1" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=child1" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=child1" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=child1" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=child1" type="application/cmisacl+xml"/> + </atom:entry> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-30T09:26:13Z</atom:published> + <atom:title>Child 2</atom:title> + <app:edited>2013-01-30T09:26:13Z</app:edited> + <atom:updated>2013-01-30T09:26:13Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=child2" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>33537</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 2</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:13.978Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537973978</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child2</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:13.978Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/entry?id=child2" type="application/atom+xml;type=entry" cmisra:id="child2"/> + <atom:link rel="enclosure" href="http://mockup/mock/entry?id=child2" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/entry?id=child2" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=child2" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=child2" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=child2" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=child2" type="application/cmisacl+xml"/> + </atom:entry> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-30T09:26:14Z</atom:published> + <atom:title>Child 3</atom:title> + <app:edited>2013-01-30T09:26:14Z</app:edited> + <atom:updated>2013-01-30T09:26:14Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=child3" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>33353</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 3</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:14.031Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537974031</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child3</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:14.031Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/entry?id=child3" type="application/atom+xml;type=entry" cmisra:id="child3"/> + <atom:link rel="enclosure" href="http://mockup/mock/entry?id=child3" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/entry?id=child3" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=child3" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=child3" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=child3" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=child3" type="application/cmisacl+xml"/> + </atom:entry> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-30T09:26:12Z</atom:published> + <atom:title>Child 4</atom:title> + <app:edited>2013-01-30T09:26:12Z</app:edited> + <atom:updated>2013-01-30T09:26:12Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Child 4</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 4</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child4</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:12.384Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537972384</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:12.384Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/entry?id=child4" type="application/atom+xml;type=entry" cmisra:id="child4"/> + <atom:link rel="enclosure" href="http://mockup/mock/entry?id=child4" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/entry?id=child4" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=child4" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=child4" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/children?id=child4" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=child4" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=child4" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=child4" type="application/cmisacl+xml"/> + </atom:entry> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-30T09:26:13Z</atom:published> + <atom:title>Child 5</atom:title> + <app:edited>2013-01-30T09:26:13Z</app:edited> + <atom:updated>2013-01-30T09:26:13Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Child 5</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 5</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child5</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:13.338Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537973338</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:13.338Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/entry?id=child5" type="application/atom+xml;type=entry" cmisra:id="child5"/> + <atom:link rel="enclosure" href="http://mockup/mock/entry?id=child5" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/entry?id=child5" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=child5" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=child5" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/children?id=child5" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=child5" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=child5" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=child5" type="application/cmisacl+xml"/> + </atom:entry> +</atom:feed> diff --git a/qa/libcmis/data/atom/root-folder.xml b/qa/libcmis/data/atom/root-folder.xml new file mode 100644 index 0000000..5baa288 --- /dev/null +++ b/qa/libcmis/data/atom/root-folder.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2012-11-29T16:14:47Z</atom:published> + <atom:title>Root Folder</atom:title> + <app:edited>2012-11-29T16:14:47Z</app:edited> + <atom:updated>2012-11-29T16:14:47Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Root Folder</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"/> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>true</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>true</cmis:canGetFolderParent> + <cmis:canGetDescendants>true</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>false</cmis:canDeleteContentStream> + <cmis:canCheckOut>false</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>false</cmis:canSetContentStream> + <cmis:canGetAllVersions>false</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>false</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>false</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>false</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>true</cmis:canGetChildren> + <cmis:canCreateDocument>true</cmis:canCreateDocument> + <cmis:canCreateFolder>true</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>true</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=root-folder" type="application/atom+xml;type=entry" cmisra:id="root-folder"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=root-folder" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=root-folder" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Afolder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=root-folder" type="application/cmisallowableactions+xml"/> + <atom:link rel="down" href="http://mockup/mock/children?id=root-folder" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=root-folder" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=root-folder" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=root-folder" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/test-document-parents.xml b/qa/libcmis/data/atom/test-document-parents.xml new file mode 100644 index 0000000..3e24f0e --- /dev/null +++ b/qa/libcmis/data/atom/test-document-parents.xml @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some Obscure Id</atom:id> + <atom:title>Test Document</atom:title> + <app:edited>2013-01-31T08:04:37Z</app:edited> + <atom:updated>2013-01-31T08:04:37Z</atom:updated> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/parents?id=test-document" type="application/atom+xml;type=entry"/> + <atom:entry> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some Obscure Id</atom:id> + <atom:published>2013-01-31T08:04:35Z</atom:published> + <atom:title>Parent 1</atom:title> + <app:edited>2013-01-31T08:04:35Z</app:edited> + <atom:updated>2013-01-31T08:04:35Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Parent 1</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Parent 1</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>parent1</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-31T08:04:35.866Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359619475867</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-31T08:04:35.867Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <cmisra:relativePathSegment>Test Document</cmisra:relativePathSegment> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/entry?id=parent1" type="application/atom+xml;type=entry" cmisra:id="parent1"/> + <atom:link rel="enclosure" href="http://mockup/mock/entry?id=parent1" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/entry?id=parent1" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=parent1" type="application/cmisallowableactions+xml"/> + <atom:link rel="down" href="http://mockup/mock/children?id=parent1" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=parent1" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=parent1" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=parent1" type="application/cmisacl+xml"/> + </atom:entry> + <atom:entry> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some Obscure Id</atom:id> + <atom:published>2013-01-31T08:04:35Z</atom:published> + <atom:title>Parent 2</atom:title> + <app:edited>2013-01-31T08:04:35Z</app:edited> + <atom:updated>2013-01-31T08:04:35Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Parent 2</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Parent 2</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>parent2</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-31T08:04:35.866Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359619475867</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-31T08:04:35.867Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmisra:object> + <cmisra:relativePathSegment>Test Document</cmisra:relativePathSegment> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/entry?id=parent2" type="application/atom+xml;type=entry" cmisra:id="parent2"/> + <atom:link rel="enclosure" href="http://mockup/mock/entry?id=parent2" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/entry?id=parent2" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=parent2" type="application/cmisallowableactions+xml"/> + <atom:link rel="down" href="http://mockup/mock/children?id=parent2" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=parent2" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=parent2" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=parent2" type="application/cmisacl+xml"/> + </atom:entry> +</atom:feed> diff --git a/qa/libcmis/data/atom/test-document-relationships.xml b/qa/libcmis/data/atom/test-document-relationships.xml new file mode 100644 index 0000000..bacfda8 --- /dev/null +++ b/qa/libcmis/data/atom/test-document-relationships.xml @@ -0,0 +1,179 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-28T14:10:06Z</atom:published> + <atom:title>Test Document</atom:title> + <app:edited>2013-01-28T14:10:06Z</app:edited> + <atom:updated>2013-01-28T14:10:06Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=test-document" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206736</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>false</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>false</cmis:canGetFolderParent> + <cmis:canGetDescendants>false</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>true</cmis:canDeleteContentStream> + <cmis:canCheckOut>true</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>true</cmis:canSetContentStream> + <cmis:canGetAllVersions>true</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>true</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>true</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>true</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>false</cmis:canGetChildren> + <cmis:canCreateDocument>false</cmis:canCreateDocument> + <cmis:canCreateFolder>false</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>false</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + <cmis:relationship> + <cmis:properties> + <cmis:propertyId displayName="Target Id" localName="targetId" propertyDefinitionId="cmis:targetId" queryName="cmis:targetId"> + <cmis:value>workspace://SpacesStore/5d8908d9-1b4a-4265-b1de-5d7244fcea70;2.2</cmis:value> + </cmis:propertyId> + <cmis:propertyId displayName="Object Type Id" localName="objectTypeId" propertyDefinitionId="cmis:objectTypeId" queryName="cmis:objectTypeId"> + <cmis:value>R:cm:original</cmis:value> + </cmis:propertyId> + <cmis:propertyString displayName="Last Modified By" localName="lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy" queryName="cmis:lastModifiedBy"> + <cmis:value>admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId displayName="Source Id" localName="sourceId" propertyDefinitionId="cmis:sourceId" queryName="cmis:sourceId"> + <cmis:value>workspace://SpacesStore/5d8908d9-1b4a-4265-b1de-5d7244fcea70;pwc</cmis:value> + </cmis:propertyId> + <cmis:propertyString displayName="Name" localName="name" propertyDefinitionId="cmis:name" queryName="cmis:name"> + <cmis:value>75|workspace://SpacesStore/3885d9a2-0540-41ab-810a-38ccb1b160d6|workspace://SpacesStore/5d8908d9-1b4a-4265-b1de-5d7244fcea70|{http://www.alfresco.org/model/content/1.0}original</cmis:value> + </cmis:propertyString> + <cmis:propertyString displayName="Created by" localName="createdBy" propertyDefinitionId="cmis:createdBy" queryName="cmis:createdBy"> + <cmis:value>admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId displayName="Object Id" localName="objectId" propertyDefinitionId="cmis:objectId" queryName="cmis:objectId"> + <cmis:value>assoc:75</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime displayName="Creation Date" localName="creationDate" propertyDefinitionId="cmis:creationDate" queryName="cmis:creationDate"> + <cmis:value>2010-05-01T00:00:00+02:00</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString displayName="Change token" localName="changeToken" propertyDefinitionId="cmis:changeToken" queryName="cmis:changeToken"/> + <cmis:propertyId displayName="Base Type Id" localName="baseTypeId" propertyDefinitionId="cmis:baseTypeId" queryName="cmis:baseTypeId"> + <cmis:value>cmis:relationship</cmis:value> + </cmis:propertyId> + <cmis:propertyId displayName="Alfresco Node Ref" localName="nodeRef" propertyDefinitionId="alfcmis:nodeRef" queryName="alfcmis:nodeRef"> + <cmis:value>75|workspace://SpacesStore/3885d9a2-0540-41ab-810a-38ccb1b160d6|workspace://SpacesStore/5d8908d9-1b4a-4265-b1de-5d7244fcea70|{http://www.alfresco.org/model/content/1.0}original</cmis:value> + </cmis:propertyId> + <cmis:propertyString displayName="Description" localName="description" propertyDefinitionId="cmis:description" queryName="cmis:description"/> + <cmis:propertyDateTime displayName="Last Modified Date" localName="lastModificationDate" propertyDefinitionId="cmis:lastModificationDate" queryName="cmis:lastModificationDate"> + <cmis:value>2010-05-01T00:00:00+02:00</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmis:relationship> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry" cmisra:id="test-document"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=test-document" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=test-document" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=test-document" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=test-document" type="application/cmisacl+xml"/> + <atom:link rel="version-history" href="http://mockup/mock/versions?id=test-document" type="application/atom+xml;type=feed"/> + <atom:link rel="alternate" href="http://mockup/mock/renditions?id=test-document-rendition1" type="image/png" cmisra:renditionKind="cmis:thumbnail" title="picture" length="40385"/> + <atom:link rel="alternate" href="http://mockup/mock/renditions?id=test-document-rendition2" type="application/pdf" cmisra:renditionKind="pdf" title="Doc as PDF"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/test-document-updated.xml b/qa/libcmis/data/atom/test-document-updated.xml new file mode 100644 index 0000000..31482f6 --- /dev/null +++ b/qa/libcmis/data/atom/test-document-updated.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-28T14:10:06Z</atom:published> + <atom:title>New name</atom:title> + <app:edited>2013-01-28T14:10:06Z</app:edited> + <atom:updated>2013-01-28T14:10:06Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=test-document" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>New name</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206736</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>false</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>false</cmis:canGetFolderParent> + <cmis:canGetDescendants>false</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>true</cmis:canDeleteContentStream> + <cmis:canCheckOut>false</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>true</cmis:canSetContentStream> + <cmis:canGetAllVersions>false</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>true</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>true</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>true</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>false</cmis:canGetChildren> + <cmis:canCreateDocument>false</cmis:canCreateDocument> + <cmis:canCreateFolder>false</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>false</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document</objectId> + <name>New name</name> + </exampleExtension:exampleExtension> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry" cmisra:id="test-document"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=ComplexType" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=test-document" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=test-document" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=test-document" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=test-document" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/test-document.xml b/qa/libcmis/data/atom/test-document.xml new file mode 100644 index 0000000..242ded2 --- /dev/null +++ b/qa/libcmis/data/atom/test-document.xml @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-01-28T14:10:06Z</atom:published> + <atom:title>Test Document</atom:title> + <app:edited>2013-01-28T14:10:06Z</app:edited> + <atom:updated>2013-01-28T14:10:06Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=test-document" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206736</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>false</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>false</cmis:canGetFolderParent> + <cmis:canGetDescendants>false</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>true</cmis:canDeleteContentStream> + <cmis:canCheckOut>true</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>true</cmis:canSetContentStream> + <cmis:canGetAllVersions>true</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>true</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>true</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>true</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>false</cmis:canGetChildren> + <cmis:canCreateDocument>false</cmis:canCreateDocument> + <cmis:canCreateFolder>false</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>false</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + <cmis:rendition> + <cmis:streamId>http://mockup/mock/renditions?id=test-document-rendition1</cmis:streamId> + <cmis:mimetype>image/png</cmis:mimetype> + <cmis:length>40385</cmis:length> + <cmis:kind>cmis:thumbnail</cmis:kind> + <cmis:title>picture</cmis:title> + <cmis:height>100</cmis:height> + <cmis:width>100</cmis:width> + </cmis:rendition> + <cmis:rendition> + <cmis:streamId>http://mockup/mock/renditions?id=test-document-rendition2</cmis:streamId> + <cmis:mimetype>application/pdf</cmis:mimetype> + <cmis:kind>pdf</cmis:kind> + <cmis:title>Doc as PDF</cmis:title> + </cmis:rendition> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry" cmisra:id="test-document"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=test-document" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=test-document" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=test-document" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=test-document" type="text/plain"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=test-document" type="application/cmisacl+xml"/> + <atom:link rel="version-history" href="http://mockup/mock/versions?id=test-document" type="application/atom+xml;type=feed"/> + <atom:link rel="alternate" href="http://mockup/mock/renditions?id=test-document-rendition1" type="image/png" cmisra:renditionKind="cmis:thumbnail" title="picture" length="40385"/> + <atom:link rel="alternate" href="http://mockup/mock/renditions?id=test-document-rendition2" type="application/pdf" cmisra:renditionKind="pdf" title="Doc as PDF"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/type-docLevel1.xml b/qa/libcmis/data/atom/type-docLevel1.xml new file mode 100644 index 0000000..20d70c4 --- /dev/null +++ b/qa/libcmis/data/atom/type-docLevel1.xml @@ -0,0 +1,404 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>Document Level 1</atom:title> + <app:edited>2013-01-25T09:47:39Z</app:edited> + <atom:updated>2013-01-25T09:47:39Z</atom:updated> + <cmisra:type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xsi:type="cmis:cmisTypeDocumentDefinitionType"> + <cmis:id>DocumentLevel1</cmis:id> + <cmis:localName>DocumentLevel1</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 1</cmis:displayName> + <cmis:queryName>DocumentLevel1</cmis:queryName> + <cmis:description>Description of Document Level 1 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>cmis:document</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmisra:type> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/type?id=DocumentLevel1" type="application/atom+xml;type=entry" cmisra:id="DocumentLevel1"/> + <atom:link rel="enclosure" href="http://mockup/mock/type?id=DocumentLevel1" type="application/atom+xml;type=entry"/> + <atom:link rel="up" href="http://mockup/mock/type?id=cmis%3Adocument" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/types?typeId=DocumentLevel1" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=DocumentLevel1" type="application/cmistree+xml"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Adocument" type="application/atom+xml;type=entry"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/type-docLevel2.xml b/qa/libcmis/data/atom/type-docLevel2.xml new file mode 100644 index 0000000..eb7deeb --- /dev/null +++ b/qa/libcmis/data/atom/type-docLevel2.xml @@ -0,0 +1,675 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>Document Level 2</atom:title> + <app:edited>2013-01-25T09:46:50Z</app:edited> + <atom:updated>2013-01-25T09:46:50Z</atom:updated> + <cmisra:type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xsi:type="cmis:cmisTypeDocumentDefinitionType"> + <cmis:id>DocumentLevel2</cmis:id> + <cmis:localName>DocumentLevel2</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 2</cmis:displayName> + <cmis:queryName>DocumentLevel2</cmis:queryName> + <cmis:description>Description of Document Level 2 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>DocumentLevel1</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyHtmlDefinition> + <cmis:id>HtmlProp</cmis:id> + <cmis:localName>HtmlProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Html Property</cmis:displayName> + <cmis:queryName>HtmlProp</cmis:queryName> + <cmis:description>This is a Sample Html Property property.</cmis:description> + <cmis:propertyType>html</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyHtmlDefinition> + <cmis:propertyIdDefinition> + <cmis:id>IdProp</cmis:id> + <cmis:localName>IdProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Id Property</cmis:displayName> + <cmis:queryName>IdProp</cmis:queryName> + <cmis:description>This is a Sample Id Property property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>DateTimePropMV</cmis:id> + <cmis:localName>DateTimePropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample DateTime multi-value Property</cmis:displayName> + <cmis:queryName>DateTimePropMV</cmis:queryName> + <cmis:description>This is a Sample DateTime multi-value Property property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyUriDefinition> + <cmis:id>UriProp</cmis:id> + <cmis:localName>UriProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Uri Property</cmis:displayName> + <cmis:queryName>UriProp</cmis:queryName> + <cmis:description>This is a Sample Uri Property property.</cmis:description> + <cmis:propertyType>uri</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyUriDefinition> + <cmis:propertyDecimalDefinition> + <cmis:id>DecimalProp</cmis:id> + <cmis:localName>DecimalProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Decimal Property</cmis:displayName> + <cmis:queryName>DecimalProp</cmis:queryName> + <cmis:description>This is a Sample Decimal Property property.</cmis:description> + <cmis:propertyType>decimal</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDecimalDefinition> + <cmis:propertyUriDefinition> + <cmis:id>UriPropMV</cmis:id> + <cmis:localName>UriPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Uri multi-value Property</cmis:displayName> + <cmis:queryName>UriPropMV</cmis:queryName> + <cmis:description>This is a Sample Uri multi-value Property property.</cmis:description> + <cmis:propertyType>uri</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyUriDefinition> + <cmis:propertyIdDefinition> + <cmis:id>IdPropMV</cmis:id> + <cmis:localName>IdPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Id Html multi-value Property</cmis:displayName> + <cmis:queryName>IdPropMV</cmis:queryName> + <cmis:description>This is a Sample Id Html multi-value Property property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>PickListProp</cmis:id> + <cmis:localName>PickListProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Pick List Property</cmis:displayName> + <cmis:queryName>PickListProp</cmis:queryName> + <cmis:description>This is a Sample Pick List Property property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + <cmis:defaultValue propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:defaultValue> + <cmis:choice displayName=""> + <cmis:value>red</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>green</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>blue</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>black</cmis:value> + </cmis:choice> + </cmis:propertyStringDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>IntProp</cmis:id> + <cmis:localName>IntProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Int Property</cmis:displayName> + <cmis:queryName>IntProp</cmis:queryName> + <cmis:description>This is a Sample Int Property property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyHtmlDefinition> + <cmis:id>HtmlPropMV</cmis:id> + <cmis:localName>HtmlPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Html multi-value Property</cmis:displayName> + <cmis:queryName>HtmlPropMV</cmis:queryName> + <cmis:description>This is a Sample Html multi-value Property property.</cmis:description> + <cmis:propertyType>html</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyHtmlDefinition> + <cmis:propertyStringDefinition> + <cmis:id>StringProp</cmis:id> + <cmis:localName>StringProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample String Property</cmis:displayName> + <cmis:queryName>StringProp</cmis:queryName> + <cmis:description>This is a Sample String Property property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDecimalDefinition> + <cmis:id>DecimalPropMV</cmis:id> + <cmis:localName>DecimalPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Decimal multi-value Property</cmis:displayName> + <cmis:queryName>DecimalPropMV</cmis:queryName> + <cmis:description>This is a Sample Decimal multi-value Property property.</cmis:description> + <cmis:propertyType>decimal</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDecimalDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>DateTimeProp</cmis:id> + <cmis:localName>DateTimeProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample DateTime Property</cmis:displayName> + <cmis:queryName>DateTimeProp</cmis:queryName> + <cmis:description>This is a Sample DateTime Property property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>BooleanProp</cmis:id> + <cmis:localName>BooleanProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Boolean Property</cmis:displayName> + <cmis:queryName>BooleanProp</cmis:queryName> + <cmis:description>This is a Sample Boolean Property property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>BooleanPropMV</cmis:id> + <cmis:localName>BooleanPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Boolean multi-value Property</cmis:displayName> + <cmis:queryName>BooleanPropMV</cmis:queryName> + <cmis:description>This is a Sample Boolean multi-value Property property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>IntPropMV</cmis:id> + <cmis:localName>IntPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Int multi-value Property</cmis:displayName> + <cmis:queryName>IntPropMV</cmis:queryName> + <cmis:description>This is a Sample Int multi-value Property property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmisra:type> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry" cmisra:id="DocumentLevel2"/> + <atom:link rel="enclosure" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="up" href="http://mockup/mock/type?id=DocumentLevel1" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/types?typeId=DocumentLevel2" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=DocumentLevel2" type="application/cmistree+xml"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:document" type="application/atom+xml;type=entry"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/type-document.xml b/qa/libcmis/data/atom/type-document.xml new file mode 100644 index 0000000..9bd661d --- /dev/null +++ b/qa/libcmis/data/atom/type-document.xml @@ -0,0 +1,402 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>CMIS Document</atom:title> + <app:edited>2013-01-25T09:47:55Z</app:edited> + <atom:updated>2013-01-25T09:47:55Z</atom:updated> + <cmisra:type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xsi:type="cmis:cmisTypeDocumentDefinitionType"> + <cmis:id>cmis:document</cmis:id> + <cmis:localName>cmis:document</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>CMIS Document</cmis:displayName> + <cmis:queryName>cmis:document</cmis:queryName> + <cmis:description>Description of CMIS Document Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmisra:type> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/type?id=cmis:document" type="application/atom+xml;type=entry" cmisra:id="cmis:document"/> + <atom:link rel="enclosure" href="http://mockup/mock/type?id=cmis:document" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/types?typeId=cmis:document" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=cmis:document" type="application/cmistree+xml"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:document" type="application/atom+xml;type=entry"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/type-folder.xml b/qa/libcmis/data/atom/type-folder.xml new file mode 100644 index 0000000..5aed5ae --- /dev/null +++ b/qa/libcmis/data/atom/type-folder.xml @@ -0,0 +1,224 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>CMIS Folder</atom:title> + <app:edited>2012-11-29T16:39:44Z</app:edited> + <atom:updated>2012-11-29T16:39:44Z</atom:updated> + <cmisra:type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xsi:type="cmis:cmisTypeFolderDefinitionType"> + <cmis:id>cmis:folder</cmis:id> + <cmis:localName>cmis:folder</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>CMIS Folder</cmis:displayName> + <cmis:queryName>cmis:folder</cmis:queryName> + <cmis:description>Description of CMIS Folder Type</cmis:description> + <cmis:baseId>cmis:folder</cmis:baseId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyIdDefinition> + <cmis:id>cmis:allowedChildObjectTypeIds</cmis:id> + <cmis:localName>cmis:allowedChildObjectTypeIds</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Allowed Child Types</cmis:displayName> + <cmis:queryName>cmis:allowedChildObjectTypeIds</cmis:queryName> + <cmis:description>This is a Allowed Child Types property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:path</cmis:id> + <cmis:localName>cmis:path</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Path</cmis:displayName> + <cmis:queryName>cmis:path</cmis:queryName> + <cmis:description>This is a Path property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:parentId</cmis:id> + <cmis:localName>cmis:parentId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Parent Id</cmis:displayName> + <cmis:queryName>cmis:parentId</cmis:queryName> + <cmis:description>This is a Parent Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + </cmisra:type> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry" cmisra:id="cmis:folder"/> + <atom:link rel="enclosure" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/types?typeId=cmis:folder" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=cmis:folder" type="application/cmistree+xml"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis:folder" type="application/atom+xml;type=entry"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/typechildren-docLevel1.xml b/qa/libcmis/data/atom/typechildren-docLevel1.xml new file mode 100644 index 0000000..0d15971 --- /dev/null +++ b/qa/libcmis/data/atom/typechildren-docLevel1.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>Document Level 1</atom:title> + <app:edited>2013-01-25T10:11:03Z</app:edited> + <atom:updated>2013-01-25T10:11:03Z</atom:updated> + <cmisra:numItems>1</cmisra:numItems> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/types?typeId=DocumentLevel1&includePropertyDefinitions=false" type="application/atom+xml;type=entry" cmisra:id="DocumentLevel1"/> + <atom:link rel="via" href="http://mockup/mock/type?id=DocumentLevel1" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=DocumentLevel1" type="application/cmistree+xml"/> + <atom:link rel="up" href="http://mockup/mock/type?id=cmis%3Adocument" type="application/atom+xml;type=entry"/> + <atom:link rel="next" href="http://mockup/mock/types?typeId=DocumentLevel1&includePropertyDefinitions=false&skipCount=100&maxItems=100" type="application/atom+xml;type=feed"/> + <app:collection href="http://mockup/mock/types?typeId=DocumentLevel1"> + <atom:title type="text">Types Collection</atom:title> + <app:accept/> + </app:collection> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>Document Level 2</atom:title> + <app:edited>2013-01-25T10:11:03Z</app:edited> + <atom:updated>2013-01-25T10:11:03Z</atom:updated> + <cmisra:type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xsi:type="cmis:cmisTypeDocumentDefinitionType"> + <cmis:id>DocumentLevel2</cmis:id> + <cmis:localName>DocumentLevel2</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 2</cmis:displayName> + <cmis:queryName>DocumentLevel2</cmis:queryName> + <cmis:description>Description of Document Level 2 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>DocumentLevel1</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmisra:type> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry" cmisra:id="DocumentLevel2"/> + <atom:link rel="enclosure" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="up" href="http://mockup/mock/type?id=DocumentLevel1" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/types?typeId=DocumentLevel2" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=DocumentLevel2" type="application/cmistree+xml"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Adocument" type="application/atom+xml;type=entry"/> + </atom:entry> +</atom:feed> diff --git a/qa/libcmis/data/atom/typechildren-document.xml b/qa/libcmis/data/atom/typechildren-document.xml new file mode 100644 index 0000000..b60c1b4 --- /dev/null +++ b/qa/libcmis/data/atom/typechildren-document.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>CMIS Document</atom:title> + <app:edited>2013-01-25T10:05:56Z</app:edited> + <atom:updated>2013-01-25T10:05:56Z</atom:updated> + <cmisra:numItems>1</cmisra:numItems> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/types?typeId=cmis:document&includePropertyDefinitions=false" type="application/atom+xml;type=entry" cmisra:id="cmis:document"/> + <atom:link rel="via" href="http://mockup/mock/type?id=cmis:document" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=cmis:document" type="application/cmistree+xml"/> + <atom:link rel="next" href="http://mockup/mock/types?typeId=cmis:document&includePropertyDefinitions=false&skipCount=100&maxItems=100" type="application/atom+xml;type=feed"/> + <app:collection href="http://mockup/mock/types?typeId=cmis:document"> + <atom:title type="text">Types Collection</atom:title> + <app:accept/> + </app:collection> + <atom:entry> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>http://mockup/mock/obscure id</atom:id> + <atom:title>Document Level 1</atom:title> + <app:edited>2013-01-25T10:05:56Z</app:edited> + <atom:updated>2013-01-25T10:05:56Z</atom:updated> + <cmisra:type xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xsi:type="cmis:cmisTypeDocumentDefinitionType"> + <cmis:id>DocumentLevel1</cmis:id> + <cmis:localName>DocumentLevel1</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 1</cmis:displayName> + <cmis:queryName>DocumentLevel1</cmis:queryName> + <cmis:description>Description of Document Level 1 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>cmis:document</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmisra:type> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/type?id=DocumentLevel1" type="application/atom+xml;type=entry" cmisra:id="DocumentLevel1"/> + <atom:link rel="enclosure" href="http://mockup/mock/type?id=DocumentLevel1" type="application/atom+xml;type=entry"/> + <atom:link rel="up" href="http://mockup/mock/type?id=cmis%3Adocument" type="application/atom+xml;type=entry"/> + <atom:link rel="down" href="http://mockup/mock/types?typeId=DocumentLevel1" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/typedesc?typeId=DocumentLevel1" type="application/cmistree+xml"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Adocument" type="application/atom+xml;type=entry"/> + </atom:entry> +</atom:feed> diff --git a/qa/libcmis/data/atom/valid-object-noactions.xml b/qa/libcmis/data/atom/valid-object-noactions.xml new file mode 100644 index 0000000..697c2a7 --- /dev/null +++ b/qa/libcmis/data/atom/valid-object-noactions.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2012-11-29T16:14:47Z</atom:published> + <atom:title>Valid Object</atom:title> + <app:edited>2012-11-29T16:14:47Z</app:edited> + <atom:updated>2012-11-29T16:14:47Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Valid Object</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Valid Object</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>valid-object</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <exampleExtension:exampleExtension xmlns="http://my/cmis/extension" xmlns:exampleExtension="http://my/cmis/extension"> + <objectId xmlns:ns0="http://my/cmis/extension" ns0:type="cmis:folder">valid-object</objectId> + <name>Valid Object</name> + </exampleExtension:exampleExtension> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=valid-object" type="application/atom+xml;type=entry" cmisra:id="valid-object"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=valid-object" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=valid-object" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Afolder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=valid-object" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=valid-object" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/children?id=valid-object" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=valid-object" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=valid-object" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=valid-object" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/valid-object.xml b/qa/libcmis/data/atom/valid-object.xml new file mode 100644 index 0000000..65815b3 --- /dev/null +++ b/qa/libcmis/data/atom/valid-object.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>Admin</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2012-11-29T16:14:47Z</atom:published> + <atom:title>Valid Object</atom:title> + <app:edited>2012-11-29T16:14:47Z</app:edited> + <atom:updated>2012-11-29T16:14:47Z</atom:updated> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Valid Object</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Valid Object</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>valid-object</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>true</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>true</cmis:canGetFolderParent> + <cmis:canGetDescendants>true</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>false</cmis:canDeleteContentStream> + <cmis:canCheckOut>false</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>false</cmis:canSetContentStream> + <cmis:canGetAllVersions>false</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>false</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>false</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>false</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>true</cmis:canGetChildren> + <cmis:canCreateDocument>true</cmis:canCreateDocument> + <cmis:canCreateFolder>true</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>true</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://my/cmis/extension" xmlns:exampleExtension="http://my/cmis/extension"> + <objectId xmlns:ns0="http://my/cmis/extension" ns0:type="cmis:folder">valid-object</objectId> + <name>Valid Object</name> + </exampleExtension:exampleExtension> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=valid-object" type="application/atom+xml;type=entry" cmisra:id="valid-object"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=valid-object" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=valid-object" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=cmis%3Afolder" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=valid-object" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=valid-object" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/children?id=valid-object" type="application/atom+xml;type=feed"/> + <atom:link rel="down" href="http://mockup/mock/descendants?id=valid-object" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=valid-object" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=valid-object" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/working-copy.xml b/qa/libcmis/data/atom/working-copy.xml new file mode 100644 index 0000000..42a9bc7 --- /dev/null +++ b/qa/libcmis/data/atom/working-copy.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:app="http://www.w3.org/2007/app"> + <atom:author> + <atom:name>unknown</atom:name> + </atom:author> + <atom:id>Some obscure Id</atom:id> + <atom:published>2013-05-21T13:50:45Z</atom:published> + <atom:title>Test Document</atom:title> + <app:edited>2013-05-21T13:50:45Z</app:edited> + <atom:updated>2013-05-21T13:50:45Z</atom:updated> + <atom:content src="http://mockup/mock/content/data.txt?id=working-copy" type="text/plain"/> + <cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:properties> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"> + <cmis:value>working-copy</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="VersionedStringProp" displayName="Sample String Property" localName="VersionedStringProp" propertyDefinitionId="VersionedStringProp"/> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>version-series</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-05-21T13:50:45.300Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1369144245300</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"> + <cmis:value>V 0.2</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>working-copy</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-05-21T13:50:45.300Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">working-copy</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmisra:object> + <atom:link rel="service" href="http://mockup/mock" type="application/atomsvc+xml"/> + <atom:link rel="self" href="http://mockup/mock/id?id=working-copy" type="application/atom+xml;type=entry" cmisra:id="working-copy"/> + <atom:link rel="enclosure" href="http://mockup/mock/id?id=working-copy" type="application/atom+xml;type=entry"/> + <atom:link rel="edit" href="http://mockup/mock/id?id=working-copy" type="application/atom+xml;type=entry"/> + <atom:link rel="describedby" href="http://mockup/mock/type?id=DocumentLevel2" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions" href="http://mockup/mock/allowableactions?id=working-copy" type="application/cmisallowableactions+xml"/> + <atom:link rel="up" href="http://mockup/mock/parents?id=working-copy" type="application/atom+xml;type=feed"/> + <atom:link rel="version-history" href="http://mockup/mock/versions?id=working-copy&versionSeries=version-series" type="application/atom+xml;type=feed"/> + <atom:link rel="edit-media" href="http://mockup/mock/content?id=working-copy" type="text/plain"/> + <atom:link rel="working-copy" href="http://mockup/mock/id?id=working-copy" type="application/atom+xml;type=entry"/> + <atom:link rel="via" href="http://mockup/mock/id?id=working-copy" type="application/atom+xml;type=entry"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/acl" href="http://mockup/mock/acl?id=working-copy" type="application/cmisacl+xml"/> +</atom:entry> diff --git a/qa/libcmis/data/atom/workspaces.xml b/qa/libcmis/data/atom/workspaces.xml new file mode 100644 index 0000000..1448776 --- /dev/null +++ b/qa/libcmis/data/atom/workspaces.xml @@ -0,0 +1,238 @@ +<?xml version="1.0" encoding="UTF-8"?> +<app:service xmlns:app="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/"> + <app:workspace> + <atom:title>Mockup</atom:title> + <app:collection href="http://mockup/mock/children?id=root"> + <cmisra:collectionType>root</cmisra:collectionType> + <atom:title type="text">Root Collection</atom:title> + <app:accept>application/atom+xml;type=entry</app:accept> + <app:accept>application/cmisatom+xml</app:accept> + </app:collection> + <app:collection href="http://mockup/mock/types"> + <cmisra:collectionType>types</cmisra:collectionType> + <atom:title type="text">Types Collection</atom:title> + <app:accept/> + </app:collection> + <app:collection href="http://mockup/mock/query"> + <cmisra:collectionType>query</cmisra:collectionType> + <atom:title type="text">Query Collection</atom:title> + <app:accept>application/cmisquery+xml</app:accept> + </app:collection> + <app:collection href="http://mockup/mock/checkedout"> + <cmisra:collectionType>checkedout</cmisra:collectionType> + <atom:title type="text">Checked Out Collection</atom:title> + <app:accept>application/cmisatom+xml</app:accept> + </app:collection> + <app:collection href="http://mockup/mock/unfiled"> + <cmisra:collectionType>unfiled</cmisra:collectionType> + <atom:title type="text">Unfiled Collection</atom:title> + <app:accept>application/cmisatom+xml</app:accept> + </app:collection> + <cmisra:repositoryInfo xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmis:repositoryId>mock</cmis:repositoryId> + <cmis:repositoryName>Mockup</cmis:repositoryName> + <cmis:repositoryDescription>Repository sent by mockup server</cmis:repositoryDescription> + <cmis:vendorName>libcmis</cmis:vendorName> + <cmis:productName>Libcmis mockup</cmis:productName> + <cmis:productVersion>some-version</cmis:productVersion> + <cmis:rootFolderId>root-folder</cmis:rootFolderId> + <cmis:latestChangeLogToken>0</cmis:latestChangeLogToken> + <cmis:capabilities> + <cmis:capabilityACL>manage</cmis:capabilityACL> + <cmis:capabilityAllVersionsSearchable>false</cmis:capabilityAllVersionsSearchable> + <cmis:capabilityChanges>none</cmis:capabilityChanges> + <cmis:capabilityContentStreamUpdatability>anytime</cmis:capabilityContentStreamUpdatability> + <cmis:capabilityGetDescendants>true</cmis:capabilityGetDescendants> + <cmis:capabilityGetFolderTree>true</cmis:capabilityGetFolderTree> + <cmis:capabilityMultifiling>true</cmis:capabilityMultifiling> + <cmis:capabilityPWCSearchable>false</cmis:capabilityPWCSearchable> + <cmis:capabilityPWCUpdatable>true</cmis:capabilityPWCUpdatable> + <cmis:capabilityQuery>bothcombined</cmis:capabilityQuery> + <cmis:capabilityRenditions>none</cmis:capabilityRenditions> + <cmis:capabilityUnfiling>true</cmis:capabilityUnfiling> + <cmis:capabilityVersionSpecificFiling>false</cmis:capabilityVersionSpecificFiling> + <cmis:capabilityJoin>none</cmis:capabilityJoin> + </cmis:capabilities> + <cmis:aclCapability> + <cmis:supportedPermissions>basic</cmis:supportedPermissions> + <cmis:propagation>objectonly</cmis:propagation> + <cmis:permissions> + <cmis:permission>cmis:read</cmis:permission> + <cmis:description>Read</cmis:description> + </cmis:permissions> + <cmis:permissions> + <cmis:permission>cmis:write</cmis:permission> + <cmis:description>Write</cmis:description> + </cmis:permissions> + <cmis:permissions> + <cmis:permission>cmis:all</cmis:permission> + <cmis:description>All</cmis:description> + </cmis:permissions> + <cmis:mapping> + <cmis:key>canGetDescendents.Folder</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetChildren.Folder</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetParents.Folder</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetFolderParent.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateDocument.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateFolder.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateRelationship.Source</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateRelationship.Target</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetProperties.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canViewContent.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canUpdateProperties.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canMove.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canMove.Target</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canMove.Source</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canDelete.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canDeleteTree.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canSetContent.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canDeleteContent.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddToFolder.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddToFolder.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemoveFromFolder.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemoveFromFolder.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCheckout.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCancelCheckout.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCheckin.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetAllVersions.VersionSeries</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetObjectRelationships.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddPolicy.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddPolicy.Policy</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemovePolicy.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemovePolicy.Policy</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetAppliedPolicies.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetACL.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canApplyACL.Object</cmis:key> + <cmis:permission>cmis:all</cmis:permission> + </cmis:mapping> + </cmis:aclCapability> + <cmis:cmisVersionSupported>1.1</cmis:cmisVersionSupported> + <cmis:thinClientURI/> + <cmis:changesIncomplete>true</cmis:changesIncomplete> + <cmis:principalAnonymous>anonymous</cmis:principalAnonymous> + <cmis:principalAnyone>anyone</cmis:principalAnyone> + </cmisra:repositoryInfo> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/typedescendants" href="http://mockup/mock/typedesc" type="application/atom+xml;type=feed"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://mockup/mock/foldertree?id=root" type="application/cmistree+xml"/> + <atom:link rel="http://docs.oasis-open.org/ns/cmis/link/200908/rootdescendants" href="http://mockup/mock/descendants?id=root" type="application/cmistree+xml" cmisra:id="root"/> + <cmisra:uritemplate> + <cmisra:template>http://mockup/mock/id?id={id}&filter={filter}&includeAllowableActions={includeAllowableActions}&includeACL={includeACL}&includePolicyIds={includePolicyIds}&includeRelationships={includeRelationships}&renditionFilter={renditionFilter}</cmisra:template> + <cmisra:type>objectbyid</cmisra:type> + <cmisra:mediatype>application/atom+xml;type=entry</cmisra:mediatype> + </cmisra:uritemplate> + <cmisra:uritemplate> + <cmisra:template>http://mockup/mock/path?path={path}&filter={filter}&includeAllowableActions={includeAllowableActions}&includeACL={includeACL}&includePolicyIds={includePolicyIds}&includeRelationships={includeRelationships}&renditionFilter={renditionFilter}</cmisra:template> + <cmisra:type>objectbypath</cmisra:type> + <cmisra:mediatype>application/atom+xml;type=entry</cmisra:mediatype> + </cmisra:uritemplate> + <cmisra:uritemplate> + <cmisra:template>http://mockup/mock/type?id={id}</cmisra:template> + <cmisra:type>typebyid</cmisra:type> + <cmisra:mediatype>application/atom+xml;type=entry</cmisra:mediatype> + </cmisra:uritemplate> + <cmisra:uritemplate> + <cmisra:template>http://mockup/mock/query?q={q}&searchAllVersions={searchAllVersions}&includeAllowableActions={includeAllowableActions}&includeRelationships={includeRelationships}&maxItems={maxItems}&skipCount={skipCount}</cmisra:template> + <cmisra:type>query</cmisra:type> + <cmisra:mediatype>application/atom+xml;type=feed</cmisra:mediatype> + </cmisra:uritemplate> + </app:workspace> +</app:service> diff --git a/qa/libcmis/data/gdrive/allVersions.json b/qa/libcmis/data/gdrive/allVersions.json new file mode 100644 index 0000000..cdbba67 --- /dev/null +++ b/qa/libcmis/data/gdrive/allVersions.json @@ -0,0 +1,49 @@ +{ + "kind": "drive#revisionList", + "etag": "revisionEtag", + "selfLink": "revisionsLink", + "items": [ + { + "kind": "drive#revision", + "etag": "\"anEtag\"", + "id": "versionId0", + "selfLink": "aSelfLink", + "mimeType": "application/vnd.google-apps.form", + "modifiedDate": "2010-07-04T12:30:07.355Z", + "published": false, + "exportLinks": { + "application/pdf": "pdfLink", + "application/x-vnd.oasis.opendocument.spreadsheet": "ODFLinks", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "msLink" + } + }, + { + "kind": "drive#revision", + "etag": "\"anEtag\"", + "id": "versionId1", + "selfLink": "aSelfLink", + "mimeType": "application/vnd.google-apps.form", + "modifiedDate": "2010-07-04T12:30:07.355Z", + "published": false, + "exportLinks": { + "application/pdf": "pdfLink", + "application/x-vnd.oasis.opendocument.spreadsheet": "ODFLinks", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "msLink" + } + }, + { + "kind": "drive#revision", + "etag": "\"anEtag\"", + "id": "versionId2", + "selfLink": "aSelfLink", + "mimeType": "application/vnd.google-apps.form", + "modifiedDate": "2010-07-04T12:30:07.355Z", + "published": false, + "exportLinks": { + "application/pdf": "pdfLink", + "application/x-vnd.oasis.opendocument.spreadsheet": "ODFLinks", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "msLink" + } + } + ] +} diff --git a/qa/libcmis/data/gdrive/approve.html b/qa/libcmis/data/gdrive/approve.html new file mode 100644 index 0000000..cf4959b --- /dev/null +++ b/qa/libcmis/data/gdrive/approve.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<body> +<form action="https://approval/url" method="POST" id="submit_access_form"> +<input id="state_wrapper" name="state_wrapper" value="stateWrapper" type="hidden"> +<input id="submit_access" name="submit_access" value="" type="hidden"> + +</form> +</body> +</html> diff --git a/qa/libcmis/data/gdrive/authcode.html b/qa/libcmis/data/gdrive/authcode.html new file mode 100644 index 0000000..7d28ba1 --- /dev/null +++ b/qa/libcmis/data/gdrive/authcode.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> +<body> +<input id="code" readonly="readonly" value="AuthCode"> +</body> +</html> diff --git a/qa/libcmis/data/gdrive/challenge.html b/qa/libcmis/data/gdrive/challenge.html new file mode 100644 index 0000000..a92124d --- /dev/null +++ b/qa/libcmis/data/gdrive/challenge.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html lang="en"> +<body> +<form novalidate="" id="skip" action="/signin/challenge/skip" method="post"> + <input id="skipChallenge" type="submit"> +</form> +<form novalidate="" id="resend" action="/signin/challenge" method="post"> + <input name="subAction" type="hidden" value="startChallenge"> + <input name="challengeId" type="hidden" value="1"> +</form> +<form novalidate="" id="challenge" action="/challenge/url" method="post"> + <input name="continue" id="continue" value="redirectLink&scope=Scope" type="hidden"> + <input name="service" id="service" value="lso" type="hidden"> + <input name="GALX" value="cookie" type="hidden"> + <input name="Pin" id="Pin"> +</form> +<form novalidate="" id="select_challenge" action="/signin/selectchallenge" method="post"> + <input id="selectChallenge" type="submit"> +</form> +</body> +</html> diff --git a/qa/libcmis/data/gdrive/document-updated.json b/qa/libcmis/data/gdrive/document-updated.json new file mode 100644 index 0000000..e60ee79 --- /dev/null +++ b/qa/libcmis/data/gdrive/document-updated.json @@ -0,0 +1,67 @@ +{ + "kind": "drive#file", + "id": "aFileId", + "etag": "\"an example tag\"", + "selfLink": "fileSelfLink", + "title": "New Title", + "mimeType": "application/vnd.google-apps.form", + "labels": { + "starred": false, + "hidden": false, + "trashed": false, + "restricted": false, + "viewed": true + }, + "createdDate": "2010-04-28T14:53:23.141Z", + "modifiedDate": "2010-09-30T21:01:37.504Z", + "lastViewedByMeDate": "2013-02-26T15:40:30.404Z", + "fileSize": "123", + "parents": [ + { + "kind": "drive#parentReference", + "id": "aFolderId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + }, + { + "kind": "drive#parentReference", + "id": "aNewFolderId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + } + ], + "exportLinks": { + "application/pdf": "pdflink", + "application/x-vnd.oasis.opendocument.spreadsheet": "https://downloadLink", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlslink" + }, + "thumbnailLink": "https://aThumbnailLink", + "userPermission": { + "kind": "drive#permission", + "id": "me", + "role": "owner", + "type": "user" + }, + "quotaBytesUsed": "0", + "ownerNames": [ + "Owner Real Name" + ], + "owners": [ + { + "kind": "drive#user", + "displayName": "Mock file owner", + "isAuthenticatedUser": true + } + ], + "lastModifyingUserName": "Mock user", + "lastModifyingUser": { + "kind": "drive#user", + "displayName": "User name", + "isAuthenticatedUser": false + }, + "editable": true, + "writersCanShare": true, + "shared": true +} diff --git a/qa/libcmis/data/gdrive/document.json b/qa/libcmis/data/gdrive/document.json new file mode 100644 index 0000000..016a5bd --- /dev/null +++ b/qa/libcmis/data/gdrive/document.json @@ -0,0 +1,67 @@ +{ + "kind": "drive#file", + "id": "aFileId", + "etag": "\"an example tag\"", + "selfLink": "fileSelfLink", + "title": "GDrive File", + "mimeType": "application/vnd.google-apps.form", + "labels": { + "starred": false, + "hidden": false, + "trashed": false, + "restricted": false, + "viewed": true + }, + "createdDate": "2010-04-28T14:53:23.141Z", + "modifiedDate": "2010-09-30T21:01:37.504Z", + "lastViewedByMeDate": "2013-02-26T15:40:30.404Z", + "fileSize": "123", + "parents": [ + { + "kind": "drive#parentReference", + "id": "aFolderId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + }, + { + "kind": "drive#parentReference", + "id": "aNewFolderId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + } + ], + "exportLinks": { + "application/pdf": "pdflink", + "application/x-vnd.oasis.opendocument.spreadsheet": "https://downloadLink", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlslink" + }, + "thumbnailLink": "https://aThumbnailLink", + "userPermission": { + "kind": "drive#permission", + "id": "me", + "role": "owner", + "type": "user" + }, + "quotaBytesUsed": "0", + "ownerNames": [ + "Owner Real Name" + ], + "owners": [ + { + "kind": "drive#user", + "displayName": "Mock file owner", + "isAuthenticatedUser": true + } + ], + "lastModifyingUserName": "Mock user", + "lastModifyingUser": { + "kind": "drive#user", + "displayName": "User name", + "isAuthenticatedUser": false + }, + "editable": true, + "writersCanShare": true, + "shared": true +} diff --git a/qa/libcmis/data/gdrive/document2.json b/qa/libcmis/data/gdrive/document2.json new file mode 100644 index 0000000..c55475e --- /dev/null +++ b/qa/libcmis/data/gdrive/document2.json @@ -0,0 +1,62 @@ +{ + "kind": "drive#file", + "id": "aFileId", + "etag": "\"an example tag\"", + "selfLink": "fileSelfLink", + "title": "GDrive File", + "mimeType": "application/vnd.google-apps.form", + "labels": { + "starred": false, + "hidden": false, + "trashed": false, + "restricted": false, + "viewed": true + }, + "createdDate": "2010-04-28T14:53:23.141Z", + "modifiedDate": "2010-09-30T21:01:37.504Z", + "lastViewedByMeDate": "2013-02-26T15:40:30.404Z", + "fileSize": "123", + "downloadUrl": "https://download/url", + "parents": [ + { + "kind": "drive#parentReference", + "id": "aFolderId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + }, + { + "kind": "drive#parentReference", + "id": "anotherFolderId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + } + ], + "userPermission": { + "kind": "drive#permission", + "id": "me", + "role": "owner", + "type": "user" + }, + "quotaBytesUsed": "0", + "ownerNames": [ + "Owner Real Name" + ], + "owners": [ + { + "kind": "drive#user", + "displayName": "Mock file owner", + "isAuthenticatedUser": true + } + ], + "lastModifyingUserName": "Mock user", + "lastModifyingUser": { + "kind": "drive#user", + "displayName": "User name", + "isAuthenticatedUser": false + }, + "editable": true, + "writersCanShare": true, + "shared": true +} diff --git a/qa/libcmis/data/gdrive/document_parents.json b/qa/libcmis/data/gdrive/document_parents.json new file mode 100644 index 0000000..21c033f --- /dev/null +++ b/qa/libcmis/data/gdrive/document_parents.json @@ -0,0 +1,14 @@ +{ + "kind": "drive#parentList", + "etag": "A parent etag", + "selfLink": "SelfLink", + "items": [ + { + "kind": "drive#parentReference", + "id": "aFolderId", + "selfLink": "aSelfLink", + "parentLink": "AParentLink", + "isRoot": true + } + ] +} diff --git a/qa/libcmis/data/gdrive/folder.json b/qa/libcmis/data/gdrive/folder.json new file mode 100644 index 0000000..2b07a48 --- /dev/null +++ b/qa/libcmis/data/gdrive/folder.json @@ -0,0 +1,47 @@ +{ + "kind": "drive#file", + "id": "aFolderId", + "etag": "a folder etag", + "selfLink": "SelfLINK", + "title": "testFolder", + "mimeType": "application/vnd.google-apps.folder", + "labels": { + "starred": false, + "hidden": false, + "trashed": false, + "restricted": false, + "viewed": true + }, + "createdDate": "2013-03-22T17:53:10.290Z", + "modifiedDate": "2013-03-22T17:53:44.212Z", + "modifiedByMeDate": "2013-03-22T17:53:44.212Z", + "lastViewedByMeDate": "2013-04-05T15:27:47.261Z", + "parents": [ + { + "kind": "drive#parentReference", + "id": "parentID", + "isRoot": true + } + ], + "quotaBytesUsed": "0", + "ownerNames": [ + "User" + ], + "owners": [ + { + "kind": "drive#user", + "displayName": "User name", + "isAuthenticatedUser": true + } + ], + "lastModifyingUserName": "User name of the last person modify", + "lastModifyingUser": { + "kind": "drive#user", + "displayName": "Last mofifying username", + "isAuthenticatedUser": true + }, + "editable": true, + "writersCanShare": true, + "shared": false, + "appDataContents": false +} diff --git a/qa/libcmis/data/gdrive/folder2.json b/qa/libcmis/data/gdrive/folder2.json new file mode 100644 index 0000000..1f54206 --- /dev/null +++ b/qa/libcmis/data/gdrive/folder2.json @@ -0,0 +1,47 @@ +{ + "kind": "drive#file", + "id": "aNewFolderId", + "etag": "a folder etag", + "selfLink": "SelfLINK", + "title": "testFolder", + "mimeType": "application/vnd.google-apps.folder", + "labels": { + "starred": false, + "hidden": false, + "trashed": false, + "restricted": false, + "viewed": true + }, + "createdDate": "2013-03-22T17:53:10.290Z", + "modifiedDate": "2013-03-22T17:53:44.212Z", + "modifiedByMeDate": "2013-03-22T17:53:44.212Z", + "lastViewedByMeDate": "2013-04-05T15:27:47.261Z", + "parents": [ + { + "kind": "drive#parentReference", + "id": "parentID", + "isRoot": true + } + ], + "quotaBytesUsed": "0", + "ownerNames": [ + "User" + ], + "owners": [ + { + "kind": "drive#user", + "displayName": "User name", + "isAuthenticatedUser": true + } + ], + "lastModifyingUserName": "User name of the last person modify", + "lastModifyingUser": { + "kind": "drive#user", + "displayName": "Last mofifying username", + "isAuthenticatedUser": true + }, + "editable": true, + "writersCanShare": true, + "shared": false, + "appDataContents": false +} diff --git a/qa/libcmis/data/gdrive/folder_children.json b/qa/libcmis/data/gdrive/folder_children.json new file mode 100644 index 0000000..55604e3 --- /dev/null +++ b/qa/libcmis/data/gdrive/folder_children.json @@ -0,0 +1,20 @@ +{ + "kind": "drive#childrenList", + "etag": "A children etag", + "selfLink": "SelfLink", + "items": [ + { + "kind": "drive#childrenReference", + "id": "aChildFolder", + "mimeType": "application/vnd.google-apps.folder", + "selfLink": "aSelfLink", + "childLink": "AchildrenLink" + }, + { + "kind": "drive#childrenReference", + "id": "aChildDocument", + "selfLink": "aSelfLink2", + "childLink": "AchildrenLink2" + } + ] +} diff --git a/qa/libcmis/data/gdrive/gdoc-file.json b/qa/libcmis/data/gdrive/gdoc-file.json new file mode 100644 index 0000000..b55690e --- /dev/null +++ b/qa/libcmis/data/gdrive/gdoc-file.json @@ -0,0 +1,58 @@ +{ + "kind": "drive#file", + "id": "aFileId", + "etag": "\"an example tag\"", + "selfLink": "fileSelfLink", + "title": "a file title", + "mimeType": "application/vnd.google-apps.form", + "labels": { + "starred": false, + "hidden": false, + "trashed": false, + "restricted": false, + "viewed": true + }, + "createdDate": "2010-04-28T14:53:23.141Z", + "modifiedDate": "2010-09-30T21:01:37.504Z", + "lastViewedByMeDate": "2013-02-26T15:40:30.404Z", + "parents": [ + { + "kind": "drive#parentReference", + "id": "parentId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + } + ], + "exportLinks": { + "application/pdf": "pdflink", + "application/x-vnd.oasis.opendocument.spreadsheet": "opendocumentlinks", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlslink" + }, + "userPermission": { + "kind": "drive#permission", + "id": "me", + "role": "owner", + "type": "user" + }, + "quotaBytesUsed": "0", + "ownerNames": [ + "Owner Real Name" + ], + "owners": [ + { + "kind": "drive#user", + "displayName": "Mock file owner", + "isAuthenticatedUser": true + } + ], + "lastModifyingUserName": "Mock user", + "lastModifyingUser": { + "kind": "drive#user", + "displayName": "User name", + "isAuthenticatedUser": false + }, + "editable": true, + "writersCanShare": true, + "shared": true +} diff --git a/qa/libcmis/data/gdrive/jsontest-good.json b/qa/libcmis/data/gdrive/jsontest-good.json new file mode 100644 index 0000000..939e599 --- /dev/null +++ b/qa/libcmis/data/gdrive/jsontest-good.json @@ -0,0 +1,60 @@ +{ + "kind": "drive#file", + "id": "aFileId", + "etag": "\"an example tag\"", + "selfLink": "fileSelfLink", + "title": "a file title", + "mimeType": "application/vnd.google-apps.form", + "labels": { + "starred": false, + "hidden": false, + "trashed": false, + "restricted": false, + "viewed": true + }, + "createdDate": "2010-04-28T14:53:23.141Z", + "modifiedDate": "2010-09-30T21:01:37.504Z", + "lastViewedByMeDate": "2013-02-26T15:40:30.404Z", + "intTest": "-123", + "doubleTest": "-123.456", + "parents": [ + { + "kind": "drive#parentReference", + "id": "parentId", + "selfLink": "parentSelfLink", + "parentLink": "parentLink", + "isRoot": true + } + ], + "exportLinks": { + "application/pdf": "pdflink", + "application/x-vnd.oasis.opendocument.spreadsheet": "opendocumentlinks", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlslink" + }, + "userPermission": { + "kind": "drive#permission", + "id": "me", + "role": "owner", + "type": "user" + }, + "quotaBytesUsed": "0", + "ownerNames": [ + "Owner Real Name" + ], + "owners": [ + { + "kind": "drive#user", + "displayName": "Mock file owner", + "isAuthenticatedUser": true + } + ], + "lastModifyingUserName": "Mock user", + "lastModifyingUser": { + "kind": "drive#user", + "displayName": "User name", + "isAuthenticatedUser": false + }, + "editable": true, + "writersCanShare": true, + "shared": true +} diff --git a/qa/libcmis/data/gdrive/login1.html b/qa/libcmis/data/gdrive/login1.html new file mode 100644 index 0000000..b6da338 --- /dev/null +++ b/qa/libcmis/data/gdrive/login1.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> +<body> +<form novalidate="" id="gaia_loginform" action="https://login2/url" method="post"> + <input name="Page" type="hidden" value="PasswordSeparationSignIn"> + <input name="continue" id="continue" value="redirectLink&scope=Scope" type="hidden"> + <input name="service" id="service" value="lso" type="hidden"> + <input name="GALX" value="cookie" type="hidden"> + <input spellcheck="false" name="Email" id="Email" value="" type="email"> +</form> +</body> +</html> diff --git a/qa/libcmis/data/gdrive/login2.html b/qa/libcmis/data/gdrive/login2.html new file mode 100644 index 0000000..6425091 --- /dev/null +++ b/qa/libcmis/data/gdrive/login2.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html lang="en"> +<body> +<form novalidate="" id="gaia_loginform" action="https://login/url" method="post"> + <input name="continue" id="continue" value="redirectLink&scope=Scope" type="hidden"> + <input name="service" id="service" value="lso" type="hidden"> + <input name="GALX" value="cookie" type="hidden"> + <input name="Passwd" id="Passwd" type="password"> +</form> +</body> +</html> diff --git a/qa/libcmis/data/gdrive/refresh_response.json b/qa/libcmis/data/gdrive/refresh_response.json new file mode 100644 index 0000000..d4afc4b --- /dev/null +++ b/qa/libcmis/data/gdrive/refresh_response.json @@ -0,0 +1,5 @@ +{ + "access_token":"new-access-token", + "expires_in":3920, + "token_type":"Bearer" +} diff --git a/qa/libcmis/data/gdrive/root_child_missing.json b/qa/libcmis/data/gdrive/root_child_missing.json new file mode 100644 index 0000000..3659806 --- /dev/null +++ b/qa/libcmis/data/gdrive/root_child_missing.json @@ -0,0 +1,4 @@ +{ + "items": [ + ] +} diff --git a/qa/libcmis/data/gdrive/root_child_search.json b/qa/libcmis/data/gdrive/root_child_search.json new file mode 100644 index 0000000..79f5031 --- /dev/null +++ b/qa/libcmis/data/gdrive/root_child_search.json @@ -0,0 +1,13 @@ +{ + "kind": "drive#childrenList", + "etag": "A children etag", + "selfLink": "SelfLink", + "items": [ + { + "kind": "drive#childrenReference", + "id": "aRootChildId2", + "selfLink": "aSelfLink2", + "childLink": "AchildrenLink2" + } + ] +} diff --git a/qa/libcmis/data/gdrive/token-response.json b/qa/libcmis/data/gdrive/token-response.json new file mode 100644 index 0000000..36b233c --- /dev/null +++ b/qa/libcmis/data/gdrive/token-response.json @@ -0,0 +1,6 @@ +{ + "access_token":"mock-access-token", + "expires_in":3920, + "token_type":"Bearer", + "refresh_token":"mock-refresh-token" +}
\ No newline at end of file diff --git a/qa/libcmis/data/onedrive/file.json b/qa/libcmis/data/onedrive/file.json new file mode 100644 index 0000000..29e42e9 --- /dev/null +++ b/qa/libcmis/data/onedrive/file.json @@ -0,0 +1,24 @@ +{ + "id": "aFileId", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "OneDriveFile", + "description": "short description", + "parent_id": "aParentId", + "size": 42, + "upload_location": "https://base/url/aFileId/content", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "source": "sourceUrl", + "link": "linkUrl", + "type": "file", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/folder-listed.json b/qa/libcmis/data/onedrive/folder-listed.json new file mode 100644 index 0000000..ce66c4e --- /dev/null +++ b/qa/libcmis/data/onedrive/folder-listed.json @@ -0,0 +1,51 @@ +{ + "data": [ + { + "id": "aFolderId", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "OneDrive Folder", + "description": "short description", + "parent_id": "aParentId", + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "link": "linkUrl", + "type": "folder", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" + }, + { + "id": "aFileId", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "OneDrive File", + "description": "short description", + "parent_id": "aParentId", + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "source": "sourceUrl", + "link": "linkUrl", + "type": "file", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" + } + ] +} diff --git a/qa/libcmis/data/onedrive/folder.json b/qa/libcmis/data/onedrive/folder.json new file mode 100644 index 0000000..576ad10 --- /dev/null +++ b/qa/libcmis/data/onedrive/folder.json @@ -0,0 +1,23 @@ +{ + "id": "aFolderId", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "OneDrive Folder", + "description": "short description", + "parent_id": "aParentId", + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "link": "linkUrl", + "type": "folder", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/folderA.json b/qa/libcmis/data/onedrive/folderA.json new file mode 100644 index 0000000..d44cdf3 --- /dev/null +++ b/qa/libcmis/data/onedrive/folderA.json @@ -0,0 +1,23 @@ +{ + "id": "folderA", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "SkyDrive", + "description": "short description", + "parent_id": null, + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "link": "linkUrl", + "type": "folder", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/folderB.json b/qa/libcmis/data/onedrive/folderB.json new file mode 100644 index 0000000..1a69b00 --- /dev/null +++ b/qa/libcmis/data/onedrive/folderB.json @@ -0,0 +1,23 @@ +{ + "id": "folderB", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "Folder B", + "description": "short description", + "parent_id": "folderA", + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "link": "linkUrl", + "type": "folder", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/folderC.json b/qa/libcmis/data/onedrive/folderC.json new file mode 100644 index 0000000..f7b9854 --- /dev/null +++ b/qa/libcmis/data/onedrive/folderC.json @@ -0,0 +1,23 @@ +{ + "id": "folderC", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "Folder C", + "description": "short description", + "parent_id": "folderB", + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "link": "linkUrl", + "type": "folder", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/new-file.json b/qa/libcmis/data/onedrive/new-file.json new file mode 100644 index 0000000..0564a51 --- /dev/null +++ b/qa/libcmis/data/onedrive/new-file.json @@ -0,0 +1,5 @@ +{ + "id": "aFileId", + "name": "OneDrive File", + "source": "sourceUrl" +} diff --git a/qa/libcmis/data/onedrive/parent-folder.json b/qa/libcmis/data/onedrive/parent-folder.json new file mode 100644 index 0000000..972c926 --- /dev/null +++ b/qa/libcmis/data/onedrive/parent-folder.json @@ -0,0 +1,23 @@ +{ + "id": "aParentId", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "OneDrive Folder", + "description": "short description", + "parent_id": null, + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "link": "linkUrl", + "type": "folder", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/refresh-response.json b/qa/libcmis/data/onedrive/refresh-response.json new file mode 100644 index 0000000..abc8f01 --- /dev/null +++ b/qa/libcmis/data/onedrive/refresh-response.json @@ -0,0 +1,8 @@ +{ + "token_type":"bearer", + "expires_in":3600, + "scope":"wl.signin wl.skydrive", + "access_token":"new-access-token", + "refresh_token":"mock-refresh-token", + "user_id":"mock-user-id" +} diff --git a/qa/libcmis/data/onedrive/search-result.json b/qa/libcmis/data/onedrive/search-result.json new file mode 100644 index 0000000..2482429 --- /dev/null +++ b/qa/libcmis/data/onedrive/search-result.json @@ -0,0 +1,52 @@ +{ + "data":[ + { + "id":"wrongFileId", + "from":{ + "name":"aOneDriveUser", + "id":"1b4a1bc1bdb25a14" + }, + "name":"OneDriveFile", + "description":"", + "parent_id":"folderA", + "size":18047, + "upload_location":"https://apis.live.net/v5.0/wrongFileId/content/", + "comments_count":0, + "comments_enabled":false, + "is_embeddable":true, + "source":"sourceUrl", + "link":"link", + "type":"file", + "shared_with":{ + "access":"Just me" + }, + "created_time":"2014-07-17T15:16:43+0000", + "updated_time":"2014-07-17T15:41:45+0000", + "client_updated_time":"2014-07-17T15:41:45+0000" + }, + { + "id":"rightFile", + "from":{ + "name":"aOneDriveUser", + "id":"1b4a1bc1bdb25a14" + }, + "name":"OneDriveFile", + "description":"", + "parent_id":"folderC", + "size":4, + "upload_location":"https://apis.live.net/v5.0/rightFileId/content/", + "comments_count":0, + "comments_enabled":false, + "is_embeddable":true, + "source":"sourceUrl", + "link":"link", + "type":"file", + "shared_with":{ + "access":"Just me" + }, + "created_time":"2014-06-17T11:30:14+0000", + "updated_time":"2014-06-17T11:30:14+0000", + "client_updated_time":"2014-06-17T11:30:14+0000" + } + ] +} diff --git a/qa/libcmis/data/onedrive/searched-file.json b/qa/libcmis/data/onedrive/searched-file.json new file mode 100644 index 0000000..9a3785c --- /dev/null +++ b/qa/libcmis/data/onedrive/searched-file.json @@ -0,0 +1,24 @@ +{ + "id": "rightFile", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "OneDriveFile", + "description": "short description", + "parent_id": "folderC", + "size": 42, + "upload_location": "https://base/url/aFileId/content", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "source": "sourceUrl", + "link": "linkUrl", + "type": "file", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/searched-wrong-file.json b/qa/libcmis/data/onedrive/searched-wrong-file.json new file mode 100644 index 0000000..4ce9d14 --- /dev/null +++ b/qa/libcmis/data/onedrive/searched-wrong-file.json @@ -0,0 +1,24 @@ +{ + "id": "wrongFile", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "OneDriveFile", + "description": "short description", + "parent_id": "folderA", + "size": 42, + "upload_location": "https://base/url/aFileId/content", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "source": "sourceUrl", + "link": "linkUrl", + "type": "file", + "shared_with": { + "access": "Shared" + }, + "created_time": "2014-06-09T08:24:04+0000", + "updated_time": "2014-06-09T08:24:04+0000", + "client_updated_time": "2014-06-09T08:24:04+0000" +} diff --git a/qa/libcmis/data/onedrive/token-response.json b/qa/libcmis/data/onedrive/token-response.json new file mode 100644 index 0000000..93d8ea6 --- /dev/null +++ b/qa/libcmis/data/onedrive/token-response.json @@ -0,0 +1,8 @@ +{ + "token_type":"bearer", + "expires_in":3600, + "scope":"wl.signin wl.skydrive", + "access_token":"mock-access-token", + "refresh_token":"mock-refresh-token", + "user_id":"mock-user-id" +} diff --git a/qa/libcmis/data/onedrive/updated-file.json b/qa/libcmis/data/onedrive/updated-file.json new file mode 100644 index 0000000..d135ae9 --- /dev/null +++ b/qa/libcmis/data/onedrive/updated-file.json @@ -0,0 +1,24 @@ +{ + "id": "aNewFileId", + "from": { + "name": "onedriveUser", + "id": "aUserId" + }, + "name": "New File Name", + "description": "new description", + "parent_id": "aParentId", + "size": 42, + "upload_location": "uploadLocation", + "comments_count": 0, + "comments_enabled": true, + "is_embeddable": true, + "source": "sourceUrl", + "link": "linkUrl", + "type": "file", + "shared_with": { + "access": "Shared" + }, + "created_time": "createdTime", + "updated_time": "updatedTime", + "client_updated_time": "clientUpdatedTime" +} diff --git a/qa/libcmis/data/sharepoint/auth-resp.json b/qa/libcmis/data/sharepoint/auth-resp.json new file mode 100644 index 0000000..c238467 --- /dev/null +++ b/qa/libcmis/data/sharepoint/auth-resp.json @@ -0,0 +1,191 @@ +{ + "d":{ + "AllProperties":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/AllProperties" + } + }, + "AllowRssFeeds":true, + "AppInstanceId":"00000000-0000-0000-0000-000000000000", + "AssociatedMemberGroup":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/AssociatedMemberGroup" + } + }, + "AssociatedOwnerGroup":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/AssociatedOwnerGroup" + } + }, + "AssociatedVisitorGroup":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/AssociatedVisitorGroup" + } + }, + "AvailableContentTypes":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/AvailableContentTypes" + } + }, + "AvailableFields":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/AvailableFields" + } + }, + "Configuration":0, + "ContentTypes":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/ContentTypes" + } + }, + "Created":"2014-07-02T13:55:23", + "CurrentUser":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/CurrentUser" + } + }, + "CustomMasterUrl":"/_catalogs/masterpage/seattle.master", + "Description":"", + "DocumentLibraryCalloutOfficeWebAppPreviewersDisabled":false, + "EnableMinimalDownload":false, + "EventReceivers":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/EventReceivers" + } + }, + "Features":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/Features" + } + }, + "Fields":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/Fields" + } + }, + "FirstUniqueAncestorSecurableObject":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/FirstUniqueAncestorSecurableObject" + } + }, + "Folders":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/Folders" + } + }, + "Id":"98a58f26-7ed4-436c-917b-05fa37e06ab2", + "Language":1033, + "LastItemModifiedDate":"2014-07-03T10:51:32Z", + "ListTemplates":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/ListTemplates" + } + }, + "Lists":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/Lists" + } + }, + "MasterUrl":"/_catalogs/masterpage/seattle.master", + "Navigation":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/Navigation" + } + }, + "ParentWeb":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/ParentWeb" + } + }, + "PushNotificationSubscribers":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/PushNotificationSubscribers" + } + }, + "QuickLaunchEnabled":true, + "RecycleBin":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/RecycleBin" + } + }, + "RecycleBinEnabled":true, + "RegionalSettings":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/RegionalSettings" + } + }, + "RoleAssignments":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/RoleAssignments" + } + }, + "RoleDefinitions":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/RoleDefinitions" + } + }, + "RootFolder":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/RootFolder" + } + }, + "ServerRelativeUrl":"/", + "SiteGroups":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/SiteGroups" + } + }, + "SiteUserInfoList":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/SiteUserInfoList" + } + }, + "SiteUsers":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/SiteUsers" + } + }, + "SyndicationEnabled":true, + "ThemeInfo":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/ThemeInfo" + } + }, + "Title":"Home", + "TreeViewEnabled":false, + "UIVersion":15, + "UIVersionConfigurationEnabled":false, + "Url":"http://sp-cmis.cloudapp.net", + "UserCustomActions":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/UserCustomActions" + } + }, + "WebInfos":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/WebInfos" + } + }, + "WebTemplate":"STS", + "Webs":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/Webs" + } + }, + "WorkflowAssociations":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/WorkflowAssociations" + } + }, + "WorkflowTemplates":{ + "__deferred":{ + "uri":"http://sp-cmis.cloudapp.net/_api/Web/WorkflowTemplates" + } + }, + "__metadata":{ + "id":"http://sp-cmis.cloudapp.net/_api/Web", + "type":"SP.Web", + "uri":"http://sp-cmis.cloudapp.net/_api/Web" + } + } +}
\ No newline at end of file diff --git a/qa/libcmis/data/sharepoint/auth-xml-resp.xml b/qa/libcmis/data/sharepoint/auth-xml-resp.xml new file mode 100644 index 0000000..1bf3396 --- /dev/null +++ b/qa/libcmis/data/sharepoint/auth-xml-resp.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<entry xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xml:base="http://mock/_api/"> + <id>http://mock/_api/Web</id> + <category term="SP.Web" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> + <link rel="edit" href="Web" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/FirstUniqueAncestorSecurableObject" type="application/atom+xml;type=entry" title="FirstUniqueAncestorSecurableObject" href="Web/FirstUniqueAncestorSecurableObject" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RoleAssignments" type="application/atom+xml;type=feed" title="RoleAssignments" href="Web/RoleAssignments" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/AllProperties" type="application/atom+xml;type=entry" title="AllProperties" href="Web/AllProperties" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/AssociatedMemberGroup" type="application/atom+xml;type=entry" title="AssociatedMemberGroup" href="Web/AssociatedMemberGroup" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/AssociatedOwnerGroup" type="application/atom+xml;type=entry" title="AssociatedOwnerGroup" href="Web/AssociatedOwnerGroup" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/AssociatedVisitorGroup" type="application/atom+xml;type=entry" title="AssociatedVisitorGroup" href="Web/AssociatedVisitorGroup" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/AvailableContentTypes" type="application/atom+xml;type=feed" title="AvailableContentTypes" href="Web/AvailableContentTypes" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/AvailableFields" type="application/atom+xml;type=feed" title="AvailableFields" href="Web/AvailableFields" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ContentTypes" type="application/atom+xml;type=feed" title="ContentTypes" href="Web/ContentTypes" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/CurrentUser" type="application/atom+xml;type=entry" title="CurrentUser" href="Web/CurrentUser" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/EventReceivers" type="application/atom+xml;type=feed" title="EventReceivers" href="Web/EventReceivers" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Features" type="application/atom+xml;type=feed" title="Features" href="Web/Features" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Fields" type="application/atom+xml;type=feed" title="Fields" href="Web/Fields" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Folders" type="application/atom+xml;type=feed" title="Folders" href="Web/Folders" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Lists" type="application/atom+xml;type=feed" title="Lists" href="Web/Lists" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ListTemplates" type="application/atom+xml;type=feed" title="ListTemplates" href="Web/ListTemplates" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Navigation" type="application/atom+xml;type=entry" title="Navigation" href="Web/Navigation" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ParentWeb" type="application/atom+xml;type=entry" title="ParentWeb" href="Web/ParentWeb" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/PushNotificationSubscribers" type="application/atom+xml;type=feed" title="PushNotificationSubscribers" href="Web/PushNotificationSubscribers" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RecycleBin" type="application/atom+xml;type=feed" title="RecycleBin" href="Web/RecycleBin" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RegionalSettings" type="application/atom+xml;type=entry" title="RegionalSettings" href="Web/RegionalSettings" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RoleDefinitions" type="application/atom+xml;type=feed" title="RoleDefinitions" href="Web/RoleDefinitions" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RootFolder" type="application/atom+xml;type=entry" title="RootFolder" href="Web/RootFolder" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/SiteGroups" type="application/atom+xml;type=feed" title="SiteGroups" href="Web/SiteGroups" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/SiteUserInfoList" type="application/atom+xml;type=entry" title="SiteUserInfoList" href="Web/SiteUserInfoList" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/SiteUsers" type="application/atom+xml;type=feed" title="SiteUsers" href="Web/SiteUsers" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ThemeInfo" type="application/atom+xml;type=entry" title="ThemeInfo" href="Web/ThemeInfo" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/UserCustomActions" type="application/atom+xml;type=feed" title="UserCustomActions" href="Web/UserCustomActions" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Webs" type="application/atom+xml;type=feed" title="Webs" href="Web/Webs" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/WebInfos" type="application/atom+xml;type=feed" title="WebInfos" href="Web/WebInfos" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/WorkflowAssociations" type="application/atom+xml;type=feed" title="WorkflowAssociations" href="Web/WorkflowAssociations" /> + <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/WorkflowTemplates" type="application/atom+xml;type=feed" title="WorkflowTemplates" href="Web/WorkflowTemplates" /> + <title /> + <updated>2014-07-10T07:24:26Z</updated> + <author> + <name /> + </author> + <content type="application/xml"> + <m:properties> + <d:AllowRssFeeds m:type="Edm.Boolean">true</d:AllowRssFeeds> + <d:AppInstanceId m:type="Edm.Guid">00000000-0000-0000-0000-000000000000</d:AppInstanceId> + <d:Configuration m:type="Edm.Int16">0</d:Configuration> + <d:Created m:type="Edm.DateTime">2014-07-02T13:55:23</d:Created> + <d:CustomMasterUrl>/_catalogs/masterpage/seattle.master</d:CustomMasterUrl> + <d:Description /> + <d:DocumentLibraryCalloutOfficeWebAppPreviewersDisabled m:type="Edm.Boolean">false</d:DocumentLibraryCalloutOfficeWebAppPreviewersDisabled> + <d:EnableMinimalDownload m:type="Edm.Boolean">false</d:EnableMinimalDownload> + <d:Id m:type="Edm.Guid">98a58f26-7ed4-436c-917b-05fa37e06ab2</d:Id> + <d:Language m:type="Edm.Int32">1033</d:Language> + <d:LastItemModifiedDate m:type="Edm.DateTime">2014-07-08T09:29:30Z</d:LastItemModifiedDate> + <d:MasterUrl>/_catalogs/masterpage/seattle.master</d:MasterUrl> + <d:QuickLaunchEnabled m:type="Edm.Boolean">true</d:QuickLaunchEnabled> + <d:RecycleBinEnabled m:type="Edm.Boolean">true</d:RecycleBinEnabled> + <d:ServerRelativeUrl>/</d:ServerRelativeUrl> + <d:SyndicationEnabled m:type="Edm.Boolean">true</d:SyndicationEnabled> + <d:Title>Home</d:Title> + <d:TreeViewEnabled m:type="Edm.Boolean">false</d:TreeViewEnabled> + <d:UIVersion m:type="Edm.Int32">15</d:UIVersion> + <d:UIVersionConfigurationEnabled m:type="Edm.Boolean">false</d:UIVersionConfigurationEnabled> + <d:Url>http://mock</d:Url> + <d:WebTemplate>STS</d:WebTemplate> + </m:properties> + </content> +</entry> diff --git a/qa/libcmis/data/sharepoint/author.json b/qa/libcmis/data/sharepoint/author.json new file mode 100644 index 0000000..91eb0fb --- /dev/null +++ b/qa/libcmis/data/sharepoint/author.json @@ -0,0 +1,28 @@ +{ + "d":{ + "Email":"", + "Groups":{ + "__deferred":{ + "uri":"http://mock/_api/Web/GetUserById(1)/Groups" + } + }, + "Id":1, + "IsHiddenInUI":false, + "IsSiteAdmin":true, + "LoginName":"i:0#.w|sp-cmis\\aUserId", + "PrincipalType":1, + "Title":"aUserId", + "UserId":{ + "NameId":"s-1-5-21-1673017749-4204619129-3521412262-500", + "NameIdIssuer":"urn:office:idp:activedirectory", + "__metadata":{ + "type":"SP.UserIdInfo" + } + }, + "__metadata":{ + "id":"http://mock/_api/Web/GetUserById(1)", + "type":"SP.User", + "uri":"http://mock/_api/Web/GetUserById(1)" + } + } +} diff --git a/qa/libcmis/data/sharepoint/children-files.json b/qa/libcmis/data/sharepoint/children-files.json new file mode 100644 index 0000000..b09d46e --- /dev/null +++ b/qa/libcmis/data/sharepoint/children-files.json @@ -0,0 +1,60 @@ +{ + "d":{ + "results":[ + { + "Author":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/Author" + } + }, + "CheckInComment":"aCheckinComment", + "CheckOutType":2, + "CheckedOutByUser":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/CheckedOutByUser" + } + }, + "ContentTag":"{AD7FC895-3E19-4553-AD77-9ADFC2A3B69A},1,2", + "CustomizedPageStatus":0, + "ETag":"\"{AD7FC895-3E19-4553-AD77-9ADFC2A3B69A},1\"", + "Exists":true, + "Length":"18045", + "Level":1, + "ListItemAllFields":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/ListItemAllFields" + } + }, + "LockedByUser":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/LockedByUser" + } + }, + "MajorVersion":2, + "MinorVersion":0, + "ModifiedBy":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/ModifiedBy" + } + }, + "Name":"SharePoint File", + "ServerRelative_api/Web":"/Shared Documents/file.txt", + "TimeCreated":"2014-07-08T09:29:29Z", + "TimeLastModified":"2014-07-08T09:29:29Z", + "Title":"", + "UIVersion":512, + "UIVersionLabel":"1.0", + "Versions":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/Versions" + } + }, + "__metadata":{ + "id":"http://base/_api/Web/aFileId", + "type":"SP.File", + "uri":"http://base/_api/Web/aFileId" + } + } + ] + } +} diff --git a/qa/libcmis/data/sharepoint/children-folders.json b/qa/libcmis/data/sharepoint/children-folders.json new file mode 100644 index 0000000..25e4cb2 --- /dev/null +++ b/qa/libcmis/data/sharepoint/children-folders.json @@ -0,0 +1,42 @@ +{ + "d": { + "results":[ + { + "Files": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/Files" + } + }, + "Folders": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/Folders" + } + }, + "ItemCount": 3, + "ListItemAllFields": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/ListItemAllFields" + } + }, + "Name": "SharePoint Folder", + "ParentFolder": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/ParentFolder" + } + }, + "Properties": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/Properties" + } + }, + "ServerRelativeUrl": "/Shared Documents", + "WelcomePage": "", + "__metadata": { + "id": "http://base/_api/Web/aFolderId", + "type": "SP.Folder", + "uri": "http://base/_api/Web/aFolderId" + } + } + ] + } +} diff --git a/qa/libcmis/data/sharepoint/file-v1.json b/qa/libcmis/data/sharepoint/file-v1.json new file mode 100644 index 0000000..a40382e --- /dev/null +++ b/qa/libcmis/data/sharepoint/file-v1.json @@ -0,0 +1,56 @@ +{ + "d":{ + "Author":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/Author" + } + }, + "CheckInComment":"aCheckinComment", + "CheckOutType":2, + "CheckedOutByUser":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/CheckedOutByUser" + } + }, + "ContentTag":"{AD7FC895-3E19-4553-AD77-9ADFC2A3B69A},1,2", + "CustomizedPageStatus":0, + "ETag":"\"{AD7FC895-3E19-4553-AD77-9ADFC2A3B69A},1\"", + "Exists":true, + "Length":"18045", + "Level":1, + "ListItemAllFields":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/ListItemAllFields" + } + }, + "LockedByUser":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/LockedByUser" + } + }, + "MajorVersion":2, + "MinorVersion":0, + "ModifiedBy":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/ModifiedBy" + } + }, + "Name":"SharePointFile", + "ServerRelativeUrl":"/Shared Documents/file.txt", + "TimeCreated":"2014-07-08T09:29:29Z", + "TimeLastModified":"2014-07-08T09:29:29Z", + "Title":"", + "UIVersion":512, + "UIVersionLabel":"1.0", + "Versions":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/Versions" + } + }, + "__metadata":{ + "id":"http://base/_api/Web/aFileId-v1", + "type":"SP.File", + "uri":"http://base/_api/Web/aFileId-v1" + } + } +} diff --git a/qa/libcmis/data/sharepoint/file.json b/qa/libcmis/data/sharepoint/file.json new file mode 100644 index 0000000..16d92a7 --- /dev/null +++ b/qa/libcmis/data/sharepoint/file.json @@ -0,0 +1,56 @@ +{ + "d":{ + "Author":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/Author" + } + }, + "CheckInComment":"aCheckinComment", + "CheckOutType":2, + "CheckedOutByUser":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/CheckedOutByUser" + } + }, + "ContentTag":"{AD7FC895-3E19-4553-AD77-9ADFC2A3B69A},1,2", + "CustomizedPageStatus":0, + "ETag":"\"{AD7FC895-3E19-4553-AD77-9ADFC2A3B69A},1\"", + "Exists":true, + "Length":"18045", + "Level":1, + "ListItemAllFields":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/ListItemAllFields" + } + }, + "LockedByUser":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/LockedByUser" + } + }, + "MajorVersion":2, + "MinorVersion":0, + "ModifiedBy":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/ModifiedBy" + } + }, + "Name":"SharePointFile", + "ServerRelativeUrl":"/Shared Documents/file.txt", + "TimeCreated":"2014-07-08T09:29:29Z", + "TimeLastModified":"2014-07-08T09:29:29Z", + "Title":"", + "UIVersion":512, + "UIVersionLabel":"1.0", + "Versions":{ + "__deferred":{ + "uri":"http://base/_api/Web/aFileId/Versions" + } + }, + "__metadata":{ + "id":"http://base/_api/Web/aFileId", + "type":"SP.File", + "uri":"http://base/_api/Web/aFileId" + } + } +} diff --git a/qa/libcmis/data/sharepoint/folder-properties.json b/qa/libcmis/data/sharepoint/folder-properties.json new file mode 100644 index 0000000..44147a9 --- /dev/null +++ b/qa/libcmis/data/sharepoint/folder-properties.json @@ -0,0 +1,35 @@ +{ + "d": { + "__metadata": { + "id": "http://base/_api/web/aFolderId/properties", + "type": "SP.PropertyValues", + "uri": "http://base/_api/web/aFolderId/properties" + }, + "vti_x005f_candeleteversion": "true", + "vti_x005f_dirlateststamp": "2014-07-28T16:10:28", + "vti_x005f_docstoretype": 1, + "vti_x005f_etag": "\"{61A348A0-D6D6-4B67-91E9-FFC87CCDE1BF},0\"", + "vti_x005f_folderitemcount": 3, + "vti_x005f_foldersubfolderitemcount": 0, + "vti_x005f_hassubdirs": "true", + "vti_x005f_isbrowsable": "true", + "vti_x005f_isexecutable": "false", + "vti_x005f_isscriptable": "false", + "vti_x005f_level": 1, + "vti_x005f_listbasetype": 1, + "vti_x005f_listenableminorversions": "true", + "vti_x005f_listenablemoderation": "false", + "vti_x005f_listenableversioning": "true", + "vti_x005f_listname": "{D0014F89-052C-4F0A-A65D-A61DFC837093}", + "vti_x005f_listrequirecheckout": "false", + "vti_x005f_listservertemplate": 101, + "vti_x005f_listtitle": "Documents", + "vti_x005f_metainfoversion": 1, + "vti_x005f_nexttolasttimemodified": "2014-07-08T09:29:29", + "vti_x005f_parentid": "{F3A8C5D9-525D-4BEB-A9DF-4CA122A5D074}", + "vti_x005f_replid": "rid:{61A348A0-D6D6-4B67-91E9-FFC87CCDE1BF}", + "vti_x005f_rtag": "rt:61A348A0-D6D6-4B67-91E9-FFC87CCDE1BF@00000000000", + "vti_x005f_timecreated": "2014-07-02T14:19:02", + "vti_x005f_timelastmodified": "2014-07-14T09:15:56" + } +} diff --git a/qa/libcmis/data/sharepoint/folder.json b/qa/libcmis/data/sharepoint/folder.json new file mode 100644 index 0000000..da658eb --- /dev/null +++ b/qa/libcmis/data/sharepoint/folder.json @@ -0,0 +1,38 @@ +{ + "d": { + "Files": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/Files" + } + }, + "Folders": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/Folders" + } + }, + "ItemCount": 3, + "ListItemAllFields": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/ListItemAllFields" + } + }, + "Name": "SharePointFolder", + "ParentFolder": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/ParentFolder" + } + }, + "Properties": { + "__deferred": { + "uri": "http://base/_api/Web/aFolderId/Properties" + } + }, + "ServerRelativeUrl": "/SharePointFolder", + "WelcomePage": "", + "__metadata": { + "id": "http://base/_api/Web/aFolderId", + "type": "SP.Folder", + "uri": "http://base/_api/Web/aFolderId" + } + } +} diff --git a/qa/libcmis/data/sharepoint/new-xdigest.json b/qa/libcmis/data/sharepoint/new-xdigest.json new file mode 100644 index 0000000..7d92f57 --- /dev/null +++ b/qa/libcmis/data/sharepoint/new-xdigest.json @@ -0,0 +1,20 @@ +{ + "d":{ + "GetContextWebInformation":{ + "FormDigestTimeoutSeconds":1800, + "FormDigestValue":"new-xdigest-code", + "LibraryVersion":"15.0.4420.1017", + "SiteFullUrl":"http://base/_api", + "SupportedSchemaVersions":{ + "results":[ + "14.0.0.0", + "15.0.0.0" + ] + }, + "WebFullUrl":"http://base/_api", + "__metadata":{ + "type":"SP.ContextWebInformation" + } + } + } +} diff --git a/qa/libcmis/data/sharepoint/root-folder.json b/qa/libcmis/data/sharepoint/root-folder.json new file mode 100644 index 0000000..7e60969 --- /dev/null +++ b/qa/libcmis/data/sharepoint/root-folder.json @@ -0,0 +1,38 @@ +{ + "d":{ + "Files":{ + "__deferred":{ + "uri":"http://base/_api/Web/rootFolderId/Files" + } + }, + "Folders":{ + "__deferred":{ + "uri":"http://base/_api/Web/rootFolderId/Folders" + } + }, + "ItemCount":0, + "ListItemAllFields":{ + "__deferred":{ + "uri":"http://base/_api/Web/rootFolderId/ListItemAllFields" + } + }, + "Name":"", + "ParentFolder":{ + "__deferred":{ + "uri":"http://base/_api/Web/rootFolderId/ParentFolder" + } + }, + "Properties":{ + "__deferred":{ + "uri":"http://base/_api/Web/rootFolderId/Properties" + } + }, + "ServerRelativeUrl":"/", + "WelcomePage":"SitePages/Home.aspx", + "__metadata":{ + "id":"http://base/_api/Web/rootFolderId", + "type":"SP.Folder", + "uri":"http://base/_api/Web/rootFolderId" + } + } +} diff --git a/qa/libcmis/data/sharepoint/versions.json b/qa/libcmis/data/sharepoint/versions.json new file mode 100644 index 0000000..83b7f5a --- /dev/null +++ b/qa/libcmis/data/sharepoint/versions.json @@ -0,0 +1,44 @@ +{ + "d": { + "results": [ + { + "CheckInComment": "checkin Comment", + "Created": "2014-07-25T12:07:57Z", + "CreatedBy": { + "__deferred": { + "uri": "http://sp-cmis.cloudapp.net/_api/SP.FileVersionc617ba22-0ea6-4d05-a9ac-6135d8cede1a/CreatedBy" + } + }, + "ID": 1, + "IsCurrentVersion": true, + "Size": 14027, + "Url": "_vti_history/512/Shared Documents/File Name", + "VersionLabel": "1.0", + "__metadata": { + "id": "http://sp-cmis.cloudapp.net/_api/SP.FileVersionc617ba22-0ea6-4d05-a9ac-6135d8cede1a", + "type": "SP.FileVersion", + "uri": "http://sp-cmis.cloudapp.net/_api/SP.FileVersionc617ba22-0ea6-4d05-a9ac-6135d8cede1a" + } + }, + { + "CheckInComment": "checkin Comment", + "Created": "2014-07-25T12:13:47Z", + "CreatedBy": { + "__deferred": { + "uri": "http://sp-cmis.cloudapp.net/_api/SP.FileVersion5e4560d1-2047-4d3d-9f14-5be91aaf1f6c/CreatedBy" + } + }, + "ID": 2, + "IsCurrentVersion": false, + "Size": 14120, + "Url": "_vti_history/513/Shared Documents/File Name", + "VersionLabel": "1.1", + "__metadata": { + "id": "http://sp-cmis.cloudapp.net/_api/SP.FileVersion5e4560d1-2047-4d3d-9f14-5be91aaf1f6c", + "type": "SP.FileVersion", + "uri": "http://sp-cmis.cloudapp.net/_api/SP.FileVersion5e4560d1-2047-4d3d-9f14-5be91aaf1f6c" + } + } + ] + } +} diff --git a/qa/libcmis/data/sharepoint/xdigest.json b/qa/libcmis/data/sharepoint/xdigest.json new file mode 100644 index 0000000..661502f --- /dev/null +++ b/qa/libcmis/data/sharepoint/xdigest.json @@ -0,0 +1,20 @@ +{ + "d":{ + "GetContextWebInformation":{ + "FormDigestTimeoutSeconds":1800, + "FormDigestValue":"0xEBC52E4F66DE48475242A8F8291E9C6CAE1C71525B2EFFC8100F87746F4A5780E5EFCA2ABF063C8B3B08B987EBF56BF7ADE4A94714934D2C3928E060FBA4009A,14 Jul 2014 08:31:38 -0000", + "LibraryVersion":"15.0.4420.1017", + "SiteFullUrl":"http://base/_api", + "SupportedSchemaVersions":{ + "results":[ + "14.0.0.0", + "15.0.0.0" + ] + }, + "WebFullUrl":"http://base/_api", + "__metadata":{ + "type":"SP.ContextWebInformation" + } + } + } +} diff --git a/qa/libcmis/data/ws/CMISWS-Service.wsdl b/qa/libcmis/data/ws/CMISWS-Service.wsdl new file mode 100644 index 0000000..5c998a6 --- /dev/null +++ b/qa/libcmis/data/ws/CMISWS-Service.wsdl @@ -0,0 +1,1262 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Content Management Interoperability Services (CMIS) Version 1.1 + Committee Specification 01 + 12 November 2012 + Copyright (c) OASIS Open 2012. All Rights Reserved. + Source: http://docs.oasis-open.org/cmis/CMIS/v1.1/cs01/schema/ + --> +<!-- + CMIS 1.1 WSDL + --> +<definitions xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:cmisw="http://docs.oasis-open.org/ns/cmis/ws/200908/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:ns="http://schemas.xmlsoap.org/soap/encoding/" xmlns:jaxws="http://java.sun.com/xml/ns/jaxws" targetNamespace="http://docs.oasis-open.org/ns/cmis/ws/200908/" name="CMISWebServices"> + <types> + <xsd:schema elementFormDefault="qualified" targetNamespace="http://docs.oasis-open.org/ns/cmis/ws/200908/"> + <xsd:import schemaLocation="CMIS-Core.xsd" namespace="http://docs.oasis-open.org/ns/cmis/core/200908/"/> + <xsd:import schemaLocation="CMIS-Messaging.xsd" namespace="http://docs.oasis-open.org/ns/cmis/messaging/200908/"/> + </xsd:schema> + </types> + <message name="cmisException"> + <part name="fault" element="cmism:cmisFault"/> + </message> + <message name="getACLRequest"> + <part name="parameters" element="cmism:getACL"/> + </message> + <message name="getACLResponse"> + <part name="parameters" element="cmism:getACLResponse"/> + </message> + <message name="applyACLRequest"> + <part name="parameters" element="cmism:applyACL"/> + </message> + <message name="applyACLResponse"> + <part name="parameters" element="cmism:applyACLResponse"/> + </message> + <message name="queryRequest"> + <part name="parameters" element="cmism:query"/> + </message> + <message name="queryResponse"> + <part name="parameters" element="cmism:queryResponse"/> + </message> + <message name="getContentChangesRequest"> + <part name="parameters" element="cmism:getContentChanges"/> + </message> + <message name="getContentChangesResponse"> + <part name="parameters" element="cmism:getContentChangesResponse"/> + </message> + <message name="addObjectToFolderRequest"> + <part name="parameters" element="cmism:addObjectToFolder"/> + </message> + <message name="addObjectToFolderResponse"> + <part name="parameters" element="cmism:addObjectToFolderResponse"/> + </message> + <message name="removeObjectFromFolderRequest"> + <part name="parameters" element="cmism:removeObjectFromFolder"/> + </message> + <message name="removeObjectFromFolderResponse"> + <part name="parameters" element="cmism:removeObjectFromFolderResponse"/> + </message> + <message name="getDescendantsRequest"> + <part name="parameters" element="cmism:getDescendants"/> + </message> + <message name="getDescendantsResponse"> + <part name="parameters" element="cmism:getDescendantsResponse"/> + </message> + <message name="getChildrenRequest"> + <part name="parameters" element="cmism:getChildren"/> + </message> + <message name="getChildrenResponse"> + <part name="parameters" element="cmism:getChildrenResponse"/> + </message> + <message name="getFolderParentRequest"> + <part name="parameters" element="cmism:getFolderParent"/> + </message> + <message name="getFolderParentResponse"> + <part name="parameters" element="cmism:getFolderParentResponse"/> + </message> + <message name="getObjectParentsRequest"> + <part name="parameters" element="cmism:getObjectParents"/> + </message> + <message name="getObjectParentsResponse"> + <part name="parameters" element="cmism:getObjectParentsResponse"/> + </message> + <message name="getRenditionsRequest"> + <part name="parameters" element="cmism:getRenditions"/> + </message> + <message name="getRenditionsResponse"> + <part name="parameters" element="cmism:getRenditionsResponse"/> + </message> + <message name="getCheckedOutDocsRequest"> + <part name="parameters" element="cmism:getCheckedOutDocs"/> + </message> + <message name="getCheckedOutDocsResponse"> + <part name="parameters" element="cmism:getCheckedOutDocsResponse"/> + </message> + <message name="createDocumentRequest"> + <part name="parameters" element="cmism:createDocument"/> + </message> + <message name="createDocumentResponse"> + <part name="parameters" element="cmism:createDocumentResponse"/> + </message> + <message name="createDocumentFromSourceRequest"> + <part name="parameters" element="cmism:createDocumentFromSource"/> + </message> + <message name="createDocumentFromSourceResponse"> + <part name="parameters" element="cmism:createDocumentFromSourceResponse"/> + </message> + <message name="createFolderRequest"> + <part name="parameters" element="cmism:createFolder"/> + </message> + <message name="createFolderResponse"> + <part name="parameters" element="cmism:createFolderResponse"/> + </message> + <message name="createRelationshipRequest"> + <part name="parameters" element="cmism:createRelationship"/> + </message> + <message name="createRelationshipResponse"> + <part name="parameters" element="cmism:createRelationshipResponse"/> + </message> + <message name="createPolicyRequest"> + <part name="parameters" element="cmism:createPolicy"/> + </message> + <message name="createPolicyResponse"> + <part name="parameters" element="cmism:createPolicyResponse"/> + </message> + <message name="createItemRequest"> + <part name="parameters" element="cmism:createItem"/> + </message> + <message name="createItemResponse"> + <part name="parameters" element="cmism:createItemResponse"/> + </message> + <message name="getAllowableActionsRequest"> + <part name="parameters" element="cmism:getAllowableActions"/> + </message> + <message name="getAllowableActionsResponse"> + <part name="parameters" element="cmism:getAllowableActionsResponse"/> + </message> + <message name="getObjectRequest"> + <part name="parameters" element="cmism:getObject"/> + </message> + <message name="getObjectResponse"> + <part name="parameters" element="cmism:getObjectResponse"/> + </message> + <message name="getPropertiesRequest"> + <part name="parameters" element="cmism:getProperties"/> + </message> + <message name="getPropertiesResponse"> + <part name="parameters" element="cmism:getPropertiesResponse"/> + </message> + <message name="getObjectByPathRequest"> + <part name="parameters" element="cmism:getObjectByPath"/> + </message> + <message name="getObjectByPathResponse"> + <part name="parameters" element="cmism:getObjectByPathResponse"/> + </message> + <message name="getContentStreamRequest"> + <part name="parameters" element="cmism:getContentStream"/> + </message> + <message name="getContentStreamResponse"> + <part name="parameters" element="cmism:getContentStreamResponse"/> + </message> + <message name="updatePropertiesRequest"> + <part name="parameters" element="cmism:updateProperties"/> + </message> + <message name="updatePropertiesResponse"> + <part name="parameters" element="cmism:updatePropertiesResponse"/> + </message> + <message name="bulkUpdatePropertiesRequest"> + <part name="parameters" element="cmism:bulkUpdateProperties"/> + </message> + <message name="bulkUpdatePropertiesResponse"> + <part name="parameters" element="cmism:bulkUpdatePropertiesResponse"/> + </message> + <message name="moveObjectRequest"> + <part name="parameters" element="cmism:moveObject"/> + </message> + <message name="moveObjectResponse"> + <part name="parameters" element="cmism:moveObjectResponse"/> + </message> + <message name="deleteObjectRequest"> + <part name="parameters" element="cmism:deleteObject"/> + </message> + <message name="deleteObjectResponse"> + <part name="parameters" element="cmism:deleteObjectResponse"/> + </message> + <message name="deleteTreeRequest"> + <part name="parameters" element="cmism:deleteTree"/> + </message> + <message name="deleteTreeResponse"> + <part name="parameters" element="cmism:deleteTreeResponse"/> + </message> + <message name="setContentStreamRequest"> + <part name="parameters" element="cmism:setContentStream"/> + </message> + <message name="setContentStreamResponse"> + <part name="parameters" element="cmism:setContentStreamResponse"/> + </message> + <message name="appendContentStreamRequest"> + <part name="parameters" element="cmism:appendContentStream"/> + </message> + <message name="appendContentStreamResponse"> + <part name="parameters" element="cmism:appendContentStreamResponse"/> + </message> + <message name="deleteContentStreamRequest"> + <part name="parameters" element="cmism:deleteContentStream"/> + </message> + <message name="deleteContentStreamResponse"> + <part name="parameters" element="cmism:deleteContentStreamResponse"/> + </message> + <message name="applyPolicyRequest"> + <part name="parameters" element="cmism:applyPolicy"/> + </message> + <message name="applyPolicyResponse"> + <part name="parameters" element="cmism:applyPolicyResponse"/> + </message> + <message name="removePolicyRequest"> + <part name="parameters" element="cmism:removePolicy"/> + </message> + <message name="removePolicyResponse"> + <part name="parameters" element="cmism:removePolicyResponse"/> + </message> + <message name="getAppliedPoliciesRequest"> + <part name="parameters" element="cmism:getAppliedPolicies"/> + </message> + <message name="getAppliedPoliciesResponse"> + <part name="parameters" element="cmism:getAppliedPoliciesResponse"/> + </message> + <message name="getObjectRelationshipsRequest"> + <part name="parameters" element="cmism:getObjectRelationships"/> + </message> + <message name="getObjectRelationshipsResponse"> + <part name="parameters" element="cmism:getObjectRelationshipsResponse"/> + </message> + <message name="getRepositoriesRequest"> + <part name="parameters" element="cmism:getRepositories"/> + </message> + <message name="getRepositoriesResponse"> + <part name="parameters" element="cmism:getRepositoriesResponse"/> + </message> + <message name="getRepositoryInfoRequest"> + <part name="parameters" element="cmism:getRepositoryInfo"/> + </message> + <message name="getRepositoryInfoResponse"> + <part name="parameters" element="cmism:getRepositoryInfoResponse"/> + </message> + <message name="getTypeChildrenRequest"> + <part name="parameters" element="cmism:getTypeChildren"/> + </message> + <message name="getTypeChildrenResponse"> + <part name="parameters" element="cmism:getTypeChildrenResponse"/> + </message> + <message name="getTypeDescendantsRequest"> + <part name="parameters" element="cmism:getTypeDescendants"/> + </message> + <message name="getTypeDescendantsResponse"> + <part name="parameters" element="cmism:getTypeDescendantsResponse"/> + </message> + <message name="getTypeDefinitionRequest"> + <part name="parameters" element="cmism:getTypeDefinition"/> + </message> + <message name="getTypeDefinitionResponse"> + <part name="parameters" element="cmism:getTypeDefinitionResponse"/> + </message> + <message name="createTypeRequest"> + <part name="parameters" element="cmism:createType"/> + </message> + <message name="createTypeResponse"> + <part name="parameters" element="cmism:createTypeResponse"/> + </message> + <message name="updateTypeRequest"> + <part name="parameters" element="cmism:updateType"/> + </message> + <message name="updateTypeResponse"> + <part name="parameters" element="cmism:updateTypeResponse"/> + </message> + <message name="deleteTypeRequest"> + <part name="parameters" element="cmism:deleteType"/> + </message> + <message name="deleteTypeResponse"> + <part name="parameters" element="cmism:deleteTypeResponse"/> + </message> + <message name="checkOutRequest"> + <part name="parameters" element="cmism:checkOut"/> + </message> + <message name="checkOutResponse"> + <part name="parameters" element="cmism:checkOutResponse"/> + </message> + <message name="cancelCheckOutRequest"> + <part name="parameters" element="cmism:cancelCheckOut"/> + </message> + <message name="cancelCheckOutResponse"> + <part name="parameters" element="cmism:cancelCheckOutResponse"/> + </message> + <message name="checkInRequest"> + <part name="parameters" element="cmism:checkIn"/> + </message> + <message name="checkInResponse"> + <part name="parameters" element="cmism:checkInResponse"/> + </message> + <message name="getObjectOfLatestVersionRequest"> + <part name="parameters" element="cmism:getObjectOfLatestVersion"/> + </message> + <message name="getObjectOfLatestVersionResponse"> + <part name="parameters" element="cmism:getObjectOfLatestVersionResponse"/> + </message> + <message name="getPropertiesOfLatestVersionRequest"> + <part name="parameters" element="cmism:getPropertiesOfLatestVersion"/> + </message> + <message name="getPropertiesOfLatestVersionResponse"> + <part name="parameters" element="cmism:getPropertiesOfLatestVersionResponse"/> + </message> + <message name="getAllVersionsRequest"> + <part name="parameters" element="cmism:getAllVersions"/> + </message> + <message name="getAllVersionsResponse"> + <part name="parameters" element="cmism:getAllVersionsResponse"/> + </message> + <message name="getFolderTreeRequest"> + <part name="parameters" element="cmism:getFolderTree"/> + </message> + <message name="getFolderTreeResponse"> + <part name="parameters" element="cmism:getFolderTreeResponse"/> + </message> + <portType name="DiscoveryServicePort"> + <operation name="query"> + <input message="cmisw:queryRequest"/> + <output message="cmisw:queryResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getContentChanges"> + <input message="cmisw:getContentChangesRequest"/> + <output message="cmisw:getContentChangesResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="MultiFilingServicePort"> + <operation name="addObjectToFolder"> + <input message="cmisw:addObjectToFolderRequest"/> + <output message="cmisw:addObjectToFolderResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="removeObjectFromFolder"> + <input message="cmisw:removeObjectFromFolderRequest"/> + <output message="cmisw:removeObjectFromFolderResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="NavigationServicePort"> + <operation name="getDescendants"> + <input message="cmisw:getDescendantsRequest"/> + <output message="cmisw:getDescendantsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getChildren"> + <input message="cmisw:getChildrenRequest"/> + <output message="cmisw:getChildrenResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getFolderParent"> + <input message="cmisw:getFolderParentRequest"/> + <output message="cmisw:getFolderParentResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getFolderTree"> + <input message="cmisw:getFolderTreeRequest"/> + <output message="cmisw:getFolderTreeResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getObjectParents"> + <input message="cmisw:getObjectParentsRequest"/> + <output message="cmisw:getObjectParentsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getCheckedOutDocs"> + <input message="cmisw:getCheckedOutDocsRequest"/> + <output message="cmisw:getCheckedOutDocsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="ObjectServicePort"> + <operation name="createDocument"> + <input message="cmisw:createDocumentRequest"/> + <output message="cmisw:createDocumentResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="createDocumentFromSource"> + <input message="cmisw:createDocumentFromSourceRequest"/> + <output message="cmisw:createDocumentFromSourceResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="createFolder"> + <input message="cmisw:createFolderRequest"/> + <output message="cmisw:createFolderResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="createRelationship"> + <input message="cmisw:createRelationshipRequest"/> + <output message="cmisw:createRelationshipResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="createPolicy"> + <input message="cmisw:createPolicyRequest"/> + <output message="cmisw:createPolicyResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="createItem"> + <input message="cmisw:createItemRequest"/> + <output message="cmisw:createItemResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getAllowableActions"> + <input message="cmisw:getAllowableActionsRequest"/> + <output message="cmisw:getAllowableActionsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getObject"> + <input message="cmisw:getObjectRequest"/> + <output message="cmisw:getObjectResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getProperties"> + <input message="cmisw:getPropertiesRequest"/> + <output message="cmisw:getPropertiesResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getRenditions"> + <input message="cmisw:getRenditionsRequest"/> + <output message="cmisw:getRenditionsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getObjectByPath"> + <input message="cmisw:getObjectByPathRequest"/> + <output message="cmisw:getObjectByPathResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getContentStream"> + <input message="cmisw:getContentStreamRequest"/> + <output message="cmisw:getContentStreamResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="updateProperties"> + <input message="cmisw:updatePropertiesRequest"/> + <output message="cmisw:updatePropertiesResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="bulkUpdateProperties"> + <input message="cmisw:bulkUpdatePropertiesRequest"/> + <output message="cmisw:bulkUpdatePropertiesResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="moveObject"> + <input message="cmisw:moveObjectRequest"/> + <output message="cmisw:moveObjectResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="deleteObject"> + <input message="cmisw:deleteObjectRequest"/> + <output message="cmisw:deleteObjectResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="deleteTree"> + <input message="cmisw:deleteTreeRequest"/> + <output message="cmisw:deleteTreeResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="setContentStream"> + <input message="cmisw:setContentStreamRequest"/> + <output message="cmisw:setContentStreamResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="appendContentStream"> + <input message="cmisw:appendContentStreamRequest"/> + <output message="cmisw:appendContentStreamResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="deleteContentStream"> + <input message="cmisw:deleteContentStreamRequest"/> + <output message="cmisw:deleteContentStreamResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="PolicyServicePort"> + <operation name="applyPolicy"> + <input message="cmisw:applyPolicyRequest"/> + <output message="cmisw:applyPolicyResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="removePolicy"> + <input message="cmisw:removePolicyRequest"/> + <output message="cmisw:removePolicyResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getAppliedPolicies"> + <input message="cmisw:getAppliedPoliciesRequest"/> + <output message="cmisw:getAppliedPoliciesResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="RelationshipServicePort"> + <operation name="getObjectRelationships"> + <input message="cmisw:getObjectRelationshipsRequest"/> + <output message="cmisw:getObjectRelationshipsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="RepositoryServicePort"> + <operation name="getRepositories"> + <input message="cmisw:getRepositoriesRequest"/> + <output message="cmisw:getRepositoriesResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getRepositoryInfo"> + <input message="cmisw:getRepositoryInfoRequest"/> + <output message="cmisw:getRepositoryInfoResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getTypeChildren"> + <input message="cmisw:getTypeChildrenRequest"/> + <output message="cmisw:getTypeChildrenResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getTypeDescendants"> + <input message="cmisw:getTypeDescendantsRequest"/> + <output message="cmisw:getTypeDescendantsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getTypeDefinition"> + <input message="cmisw:getTypeDefinitionRequest"/> + <output message="cmisw:getTypeDefinitionResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="createType"> + <input message="cmisw:createTypeRequest"/> + <output message="cmisw:createTypeResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="updateType"> + <input message="cmisw:updateTypeRequest"/> + <output message="cmisw:updateTypeResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="deleteType"> + <input message="cmisw:deleteTypeRequest"/> + <output message="cmisw:deleteTypeResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="VersioningServicePort"> + <operation name="checkOut"> + <input message="cmisw:checkOutRequest"/> + <output message="cmisw:checkOutResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="cancelCheckOut"> + <input message="cmisw:cancelCheckOutRequest"/> + <output message="cmisw:cancelCheckOutResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="checkIn"> + <input message="cmisw:checkInRequest"/> + <output message="cmisw:checkInResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getObjectOfLatestVersion"> + <input message="cmisw:getObjectOfLatestVersionRequest"/> + <output message="cmisw:getObjectOfLatestVersionResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getPropertiesOfLatestVersion"> + <input message="cmisw:getPropertiesOfLatestVersionRequest"/> + <output message="cmisw:getPropertiesOfLatestVersionResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="getAllVersions"> + <input message="cmisw:getAllVersionsRequest"/> + <output message="cmisw:getAllVersionsResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <portType name="ACLServicePort"> + <operation name="getACL"> + <input message="cmisw:getACLRequest"/> + <output message="cmisw:getACLResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + <operation name="applyACL"> + <input message="cmisw:applyACLRequest"/> + <output message="cmisw:applyACLResponse"/> + <fault message="cmisw:cmisException" name="cmisException"/> + </operation> + </portType> + <binding name="DiscoveryServicePortBinding" type="cmisw:DiscoveryServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="query"> + <soap:operation soapAction="query"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getContentChanges"> + <soap:operation soapAction="getContentChanges"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="MultiFilingServicePortBinding" type="cmisw:MultiFilingServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="addObjectToFolder"> + <soap:operation soapAction="addObjectToFolder"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="removeObjectFromFolder"> + <soap:operation soapAction="removeObjectFromFolder"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="NavigationServicePortBinding" type="cmisw:NavigationServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="getDescendants"> + <soap:operation soapAction="getDescendants"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getChildren"> + <soap:operation soapAction="getChildren"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getFolderParent"> + <soap:operation soapAction="getFolderParent"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getFolderTree"> + <soap:operation soapAction="getFolderTree"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getObjectParents"> + <soap:operation soapAction="getObjectParents"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getCheckedOutDocs"> + <soap:operation soapAction="getCheckedOutDocs"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="ObjectServicePortBinding" type="cmisw:ObjectServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="createDocument"> + <soap:operation soapAction="createDocument"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="createDocumentFromSource"> + <soap:operation soapAction="createDocumentFromSource"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="createFolder"> + <soap:operation soapAction="createFolder"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="createRelationship"> + <soap:operation soapAction="createRelationship"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="createPolicy"> + <soap:operation soapAction="createPolicy"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="createItem"> + <soap:operation soapAction=""/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getAllowableActions"> + <soap:operation soapAction="getAllowableActions"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getObject"> + <soap:operation soapAction="getObject"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getProperties"> + <soap:operation soapAction="getProperties"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getRenditions"> + <soap:operation soapAction="getRenditions"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getObjectByPath"> + <soap:operation soapAction="getObjectByPath"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getContentStream"> + <soap:operation soapAction="getContentStream"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="updateProperties"> + <soap:operation soapAction="updateProperties"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="bulkUpdateProperties"> + <soap:operation soapAction="bulkUpdateProperties"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="moveObject"> + <soap:operation soapAction="moveObject"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="deleteObject"> + <soap:operation soapAction="deleteObject"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="deleteTree"> + <soap:operation soapAction="deleteTree"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="setContentStream"> + <soap:operation soapAction="setContentStream"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="appendContentStream"> + <soap:operation soapAction="appendContentStream"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="deleteContentStream"> + <soap:operation soapAction="deleteContentStream"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="PolicyServicePortBinding" type="cmisw:PolicyServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="applyPolicy"> + <soap:operation soapAction="applyPolicy"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="removePolicy"> + <soap:operation soapAction="removePolicy"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getAppliedPolicies"> + <soap:operation soapAction="getAppliedPolicies"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="RelationshipServicePortBinding" type="cmisw:RelationshipServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="getObjectRelationships"> + <soap:operation soapAction="getObjectRelationships"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="RepositoryServicePortBinding" type="cmisw:RepositoryServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="getRepositories"> + <soap:operation soapAction="getRepositories"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getRepositoryInfo"> + <soap:operation soapAction="getRepositoryInfo"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getTypeChildren"> + <soap:operation soapAction="getTypeChildren"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getTypeDescendants"> + <soap:operation soapAction="getTypeDescendants"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getTypeDefinition"> + <soap:operation soapAction="getTypeDefinition"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="createType"> + <soap:operation soapAction="createType"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="updateType"> + <soap:operation soapAction="updateType"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="deleteType"> + <soap:operation soapAction="deleteType"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="VersioningServicePortBinding" type="cmisw:VersioningServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="checkOut"> + <soap:operation soapAction="checkOut"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="cancelCheckOut"> + <soap:operation soapAction="cancelCheckOut"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="checkIn"> + <soap:operation soapAction="checkIn"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getObjectOfLatestVersion"> + <soap:operation soapAction="getObjectOfLatestVersion"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getPropertiesOfLatestVersion"> + <soap:operation soapAction="getPropertiesOfLatestVersion"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="getAllVersions"> + <soap:operation soapAction="getAllVersions"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <binding name="ACLServicePortBinding" type="cmisw:ACLServicePort"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="getACL"> + <soap:operation soapAction="getACL"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + <operation name="applyACL"> + <soap:operation soapAction="applyACL"/> + <input> + <soap:body use="literal"/> + </input> + <output> + <soap:body use="literal"/> + </output> + <fault name="cmisException"> + <soap:fault name="cmisException" use="literal"/> + </fault> + </operation> + </binding> + <service name="DiscoveryService"> + <port name="DiscoveryServicePort" binding="cmisw:DiscoveryServicePortBinding"> + <soap:address location="http://mockup/ws/services/DiscoveryService"/> + </port> + </service> + <service name="MultiFilingService"> + <port name="MultiFilingServicePort" binding="cmisw:MultiFilingServicePortBinding"> + <soap:address location="http://mockup/ws/services/MultiFilingService"/> + </port> + </service> + <service name="NavigationService"> + <port name="NavigationServicePort" binding="cmisw:NavigationServicePortBinding"> + <soap:address location="http://mockup/ws/services/NavigationService"/> + </port> + </service> + <service name="ObjectService"> + <port name="ObjectServicePort" binding="cmisw:ObjectServicePortBinding"> + <soap:address location="http://mockup/ws/services/ObjectService"/> + </port> + </service> + <service name="PolicyService"> + <port name="PolicyServicePort" binding="cmisw:PolicyServicePortBinding"> + <soap:address location="http://mockup/ws/services/PolicyService"/> + </port> + </service> + <service name="RelationshipService"> + <port name="RelationshipServicePort" binding="cmisw:RelationshipServicePortBinding"> + <soap:address location="http://mockup/ws/services/RelationshipService"/> + </port> + </service> + <service name="RepositoryService"> + <port name="RepositoryServicePort" binding="cmisw:RepositoryServicePortBinding"> + <soap:address location="http://mockup/ws/services/RepositoryService"/> + </port> + </service> + <service name="VersioningService"> + <port name="VersioningServicePort" binding="cmisw:VersioningServicePortBinding"> + <soap:address location="http://mockup/ws/services/VersioningService"/> + </port> + </service> + <service name="ACLService"> + <port name="ACLServicePort" binding="cmisw:ACLServicePortBinding"> + <soap:address location="http://mockup/ws/services/ACLService"/> + </port> + </service> +</definitions> diff --git a/qa/libcmis/data/ws/cancel-checkout.http b/qa/libcmis/data/ws/cancel-checkout.http new file mode 100644 index 0000000..24a1c71 --- /dev/null +++ b/qa/libcmis/data/ws/cancel-checkout.http @@ -0,0 +1,25 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:cancelCheckOutResponse + xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" + xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"/> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/checked-in.http b/qa/libcmis/data/ws/checked-in.http new file mode 100644 index 0000000..0fcbb8e --- /dev/null +++ b/qa/libcmis/data/ws/checked-in.http @@ -0,0 +1,144 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"> + <cmis:value>2.0</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>some-other-token</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"> + <cmis:value>Some check-in comment</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>filename.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>false</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>false</cmis:canGetFolderParent> + <cmis:canGetDescendants>false</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>true</cmis:canDeleteContentStream> + <cmis:canCheckOut>true</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>true</cmis:canSetContentStream> + <cmis:canGetAllVersions>true</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>true</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>true</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>true</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>false</cmis:canGetChildren> + <cmis:canCreateDocument>false</cmis:canCreateDocument> + <cmis:canCreateFolder>false</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>false</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/checkin.http b/qa/libcmis/data/ws/checkin.http new file mode 100644 index 0000000..80868c1 --- /dev/null +++ b/qa/libcmis/data/ws/checkin.http @@ -0,0 +1,27 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:checkInResponse + xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" + xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objectId>test-document</cmism:objectId> + </cmism:checkInResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/checkout.http b/qa/libcmis/data/ws/checkout.http new file mode 100644 index 0000000..dc4e6aa --- /dev/null +++ b/qa/libcmis/data/ws/checkout.http @@ -0,0 +1,28 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:checkOutResponse + xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" + xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objectId>working-copy</cmism:objectId> + <cmism:contentCopied>true</cmism:contentCopied> + </cmism:checkOutResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/create-document.http b/qa/libcmis/data/ws/create-document.http new file mode 100644 index 0000000..fce2d65 --- /dev/null +++ b/qa/libcmis/data/ws/create-document.http @@ -0,0 +1,25 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:createDocumentResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objectId>created-document</cmism:objectId> + </cmism:createDocumentResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/create-folder-bad-type.http b/qa/libcmis/data/ws/create-folder-bad-type.http new file mode 100644 index 0000000..9e79cd9 --- /dev/null +++ b/qa/libcmis/data/ws/create-folder-bad-type.http @@ -0,0 +1,33 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <S:Fault> + <faultcode>S:Server</faultcode> + <faultstring>Can't create folder with type cmis:document</faultstring> + <detail> + <cmisFault:cmisFault xmlns="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:cmisFault="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/core/200908/"> + <type>constraint</type> + <code>562</code> + <message>Can't create folder with type cmis:document</message> + </cmisFault:cmisFault> + </detail> + </S:Fault> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/create-folder.http b/qa/libcmis/data/ws/create-folder.http new file mode 100644 index 0000000..5abd72c --- /dev/null +++ b/qa/libcmis/data/ws/create-folder.http @@ -0,0 +1,25 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:createFolderResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objectId>created-folder</cmism:objectId> + </cmism:createFolderResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/created-document.http b/qa/libcmis/data/ws/created-document.http new file mode 100644 index 0000000..72a1bc8 --- /dev/null +++ b/qa/libcmis/data/ws/created-document.http @@ -0,0 +1,85 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>create document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206736</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>create-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/created-folder.http b/qa/libcmis/data/ws/created-folder.http new file mode 100644 index 0000000..0e76c03 --- /dev/null +++ b/qa/libcmis/data/ws/created-folder.http @@ -0,0 +1,64 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/create folder</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>create folder</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>create-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/delete-object.http b/qa/libcmis/data/ws/delete-object.http new file mode 100644 index 0000000..eaac13d --- /dev/null +++ b/qa/libcmis/data/ws/delete-object.http @@ -0,0 +1,23 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:deleteObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"/> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/delete-tree.http b/qa/libcmis/data/ws/delete-tree.http new file mode 100644 index 0000000..b46c680 --- /dev/null +++ b/qa/libcmis/data/ws/delete-tree.http @@ -0,0 +1,27 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:deleteTreeResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:failedToDelete> + <cmism:objectIds>bad-delete</cmism:objectIds> + </cmism:failedToDelete> + </cmism:deleteTreeResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/get-content-stream.http b/qa/libcmis/data/ws/get-content-stream.http new file mode 100644 index 0000000..3ea7c59 --- /dev/null +++ b/qa/libcmis/data/ws/get-content-stream.http @@ -0,0 +1,37 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getContentStreamResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:contentStream> + <cmism:mimeType>text/plain</cmism:mimeType> + <cmism:filename>test.txt</cmism:filename> + <cmism:stream> + <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" + href="cid:stream*someid"/></cmism:stream> + </cmism:contentStream> + </cmism:getContentStreamResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <stream*someid> +Content-Type: application/xop+xml;charset=utf-8;type="text/plain" +Content-Transfer-Encoding: binary + +Some content stream +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/get-renditions.http b/qa/libcmis/data/ws/get-renditions.http new file mode 100644 index 0000000..383c888 --- /dev/null +++ b/qa/libcmis/data/ws/get-renditions.http @@ -0,0 +1,41 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getRenditionsResponse + xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" + xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:renditions> + <cmis:streamId>test-document-rendition1</cmis:streamId> + <cmis:mimetype>image/png</cmis:mimetype> + <cmis:length>40385</cmis:length> + <cmis:kind>cmis:thumbnail</cmis:kind> + <cmis:title>picture</cmis:title> + <cmis:height>100</cmis:height> + <cmis:width>100</cmis:width> + </cmism:renditions> + <cmism:renditions> + <cmis:streamId>test-document-rendition2</cmis:streamId> + <cmis:mimetype>application/pdf</cmis:mimetype> + <cmis:kind>pdf</cmis:kind> + <cmis:title>Doc as PDF</cmis:title> + </cmism:renditions> + </cmism:getRenditionsResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/get-versions.http b/qa/libcmis/data/ws/get-versions.http new file mode 100644 index 0000000..68e2956 --- /dev/null +++ b/qa/libcmis/data/ws/get-versions.http @@ -0,0 +1,204 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getAllVersionsResponse + xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" + xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objects> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>version-series</cmis:value> + </cmis:propertyId> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"> + <cmis:value>1.0</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206736</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document-1</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document-1</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmism:objects> + <cmism:objects> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>version-series</cmis:value> + </cmis:propertyId> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"> + <cmis:value>0.1</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-27T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359382206754</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document-0</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-27T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document-0</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmism:objects> + </cmism:getAllVersionsResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/getbypath-bad.http b/qa/libcmis/data/ws/getbypath-bad.http new file mode 100644 index 0000000..e641af8 --- /dev/null +++ b/qa/libcmis/data/ws/getbypath-bad.http @@ -0,0 +1,33 @@ +Content-Type: multipart/related;start="<rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8";start-info="text/xml" + +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8 +Content-Id: <rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version="1.0" encoding="UTF-8"?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-30T19:54:27Z</Created> + <Expires>2013-10-01T19:54:27Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <S:Fault> + <faultcode>S:Server</faultcode> + <faultstring>Path '/some/invalid/path' not found</faultstring> + <detail> + <cmisFault:cmisFault xmlns="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:cmisFault="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/core/200908/"> + <type>objectNotFound</type> + <code>562</code> + <message>Path not found: '/some/invalid/path'</message> + </cmisFault:cmisFault> + </detail> + </S:Fault> + </S:Body> +</S:Envelope> +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8-- + diff --git a/qa/libcmis/data/ws/move-object.http b/qa/libcmis/data/ws/move-object.http new file mode 100644 index 0000000..1399a33 --- /dev/null +++ b/qa/libcmis/data/ws/move-object.http @@ -0,0 +1,27 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:moveObjectResponse + xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" + xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objectId>test-document</cmism:objectId> + </cmism:moveObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/repositories.http b/qa/libcmis/data/ws/repositories.http new file mode 100644 index 0000000..74ae6ca --- /dev/null +++ b/qa/libcmis/data/ws/repositories.http @@ -0,0 +1,25 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <ns2:getRepositoriesResponse xmlns="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <ns2:repositories><ns2:repositoryId>mock</ns2:repositoryId><ns2:repositoryName>Mockup</ns2:repositoryName></ns2:repositories> + </ns2:getRepositoriesResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/repository-infos-bad.http b/qa/libcmis/data/ws/repository-infos-bad.http new file mode 100644 index 0000000..fc6e676 --- /dev/null +++ b/qa/libcmis/data/ws/repository-infos-bad.http @@ -0,0 +1,33 @@ +Content-Type: multipart/related;start="<rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8";start-info="text/xml" + +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8 +Content-Id: <rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version="1.0" encoding="UTF-8"?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-30T19:54:27Z</Created> + <Expires>2013-10-01T19:54:27Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <S:Fault> + <faultcode>S:Server</faultcode> + <faultstring>Repository 'bad' not found</faultstring> + <detail> + <cmisFault:cmisFault xmlns="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:cmisFault="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/core/200908/"> + <type>invalidArgument</type> + <code>562</code> + <message>Unknown repository id: 'bad'</message> + </cmisFault:cmisFault> + </detail> + </S:Fault> + </S:Body> +</S:Envelope> +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8-- + diff --git a/qa/libcmis/data/ws/repository-infos.http b/qa/libcmis/data/ws/repository-infos.http new file mode 100644 index 0000000..75f3ec8 --- /dev/null +++ b/qa/libcmis/data/ws/repository-infos.http @@ -0,0 +1,207 @@ +Content-Type: multipart/related;start="<rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8";start-info="text/xml" + +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8 +Content-Id: <rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version="1.0" encoding="UTF-8"?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-30T19:54:27Z</Created> + <Expires>2013-10-01T19:54:27Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <getRepositoryInfoResponse xmlns="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"> + <repositoryInfo> + <cmis:repositoryId>mock</cmis:repositoryId> + <cmis:repositoryName>Mockup</cmis:repositoryName> + <cmis:repositoryDescription>Repository sent by mockup server</cmis:repositoryDescription> + <cmis:vendorName>libcmis</cmis:vendorName> + <cmis:productName>Libcmis mockup</cmis:productName> + <cmis:productVersion>some-version</cmis:productVersion> + <cmis:rootFolderId>root-folder</cmis:rootFolderId> + <cmis:latestChangeLogToken>0</cmis:latestChangeLogToken> + <cmis:capabilities> + <cmis:capabilityACL>manage</cmis:capabilityACL> + <cmis:capabilityAllVersionsSearchable>false</cmis:capabilityAllVersionsSearchable> + <cmis:capabilityChanges>none</cmis:capabilityChanges> + <cmis:capabilityContentStreamUpdatability>anytime</cmis:capabilityContentStreamUpdatability> + <cmis:capabilityGetDescendants>true</cmis:capabilityGetDescendants> + <cmis:capabilityGetFolderTree>true</cmis:capabilityGetFolderTree> + <cmis:capabilityMultifiling>true</cmis:capabilityMultifiling> + <cmis:capabilityPWCSearchable>false</cmis:capabilityPWCSearchable> + <cmis:capabilityPWCUpdatable>true</cmis:capabilityPWCUpdatable> + <cmis:capabilityQuery>bothcombined</cmis:capabilityQuery> + <cmis:capabilityRenditions>read</cmis:capabilityRenditions> + <cmis:capabilityUnfiling>true</cmis:capabilityUnfiling> + <cmis:capabilityVersionSpecificFiling>false</cmis:capabilityVersionSpecificFiling> + <cmis:capabilityJoin>none</cmis:capabilityJoin> + </cmis:capabilities> + <cmis:aclCapability> + <cmis:supportedPermissions>basic</cmis:supportedPermissions> + <cmis:propagation>objectonly</cmis:propagation> + <cmis:permissions> + <cmis:permission>cmis:read</cmis:permission> + <cmis:description>Read</cmis:description> + </cmis:permissions> + <cmis:permissions> + <cmis:permission>cmis:write</cmis:permission> + <cmis:description>Write</cmis:description> + </cmis:permissions> + <cmis:permissions> + <cmis:permission>cmis:all</cmis:permission> + <cmis:description>All</cmis:description> + </cmis:permissions> + <cmis:mapping> + <cmis:key>canGetDescendents.Folder</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetChildren.Folder</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetParents.Folder</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetFolderParent.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateDocument.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateFolder.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateRelationship.Source</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCreateRelationship.Target</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetProperties.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canViewContent.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canUpdateProperties.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canMove.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canMove.Target</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canMove.Source</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canDelete.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canDeleteTree.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canSetContent.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canDeleteContent.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddToFolder.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddToFolder.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemoveFromFolder.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemoveFromFolder.Folder</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCheckout.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCancelCheckout.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canCheckin.Document</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetAllVersions.VersionSeries</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetObjectRelationships.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddPolicy.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canAddPolicy.Policy</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemovePolicy.Object</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canRemovePolicy.Policy</cmis:key> + <cmis:permission>cmis:write</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetAppliedPolicies.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canGetACL.Object</cmis:key> + <cmis:permission>cmis:read</cmis:permission> + </cmis:mapping> + <cmis:mapping> + <cmis:key>canApplyACL.Object</cmis:key> + <cmis:permission>cmis:all</cmis:permission> + </cmis:mapping> + </cmis:aclCapability> + <cmis:cmisVersionSupported>1.0</cmis:cmisVersionSupported> + <cmis:thinClientURI/> + <cmis:changesIncomplete>true</cmis:changesIncomplete> + <cmis:principalAnonymous>anonymous</cmis:principalAnonymous> + <cmis:principalAnyone>anyone</cmis:principalAnyone> + </repositoryInfo> + </getRepositoryInfoResponse> + </S:Body> +</S:Envelope> +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8-- + diff --git a/qa/libcmis/data/ws/root-children.http b/qa/libcmis/data/ws/root-children.http new file mode 100644 index 0000000..0d43497 --- /dev/null +++ b/qa/libcmis/data/ws/root-children.http @@ -0,0 +1,292 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getChildrenResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objects> + <cmism:objects> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>33446</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 1</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:13.932Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537973932</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child1</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:13.932Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>33537</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 2</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:13.978Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537973978</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child2</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:13.978Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>33353</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 3</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:14.031Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537974031</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child3</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:14.031Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + <cmism:object> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Child 4</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 4</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child4</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:12.384Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537972384</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:12.384Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + <cmism:object> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Child 5</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Child 5</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>child5</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-30T09:26:13.338Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359537973338</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-30T09:26:13.338Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + </cmism:objects> + </cmism:objects> + </cmism:getChildrenResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/root-folder.http b/qa/libcmis/data/ws/root-folder.http new file mode 100644 index 0000000..5639383 --- /dev/null +++ b/qa/libcmis/data/ws/root-folder.http @@ -0,0 +1,93 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Root Folder</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"/> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>true</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>true</cmis:canGetFolderParent> + <cmis:canGetDescendants>true</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>false</cmis:canDeleteContentStream> + <cmis:canCheckOut>false</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>false</cmis:canSetContentStream> + <cmis:canGetAllVersions>false</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>false</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>false</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>false</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>true</cmis:canGetChildren> + <cmis:canCreateDocument>true</cmis:canCreateDocument> + <cmis:canCreateFolder>true</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>true</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/secondary-type.http b/qa/libcmis/data/ws/secondary-type.http new file mode 100644 index 0000000..0d88b5b --- /dev/null +++ b/qa/libcmis/data/ws/secondary-type.http @@ -0,0 +1,57 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getTypeDefinitionResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:type> + <cmis:id>secondary-type</cmis:id> + <cmis:localName>secondary-type</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Secondary Type</cmis:displayName> + <cmis:queryName>secondary-type</cmis:queryName> + <cmis:description>Description of Secondary Type</cmis:description> + <cmis:baseId>cmis:secondary</cmis:baseId> + <cmis:parentId>cmis:secondary</cmis:parentId> + <cmis:creatable>false</cmis:creatable> + <cmis:fileable>false</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>false</cmis:controllableACL> + <cmis:propertyStringDefinition> + <cmis:id>secondary-prop</cmis:id> + <cmis:localName>secondary-prop</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>secondary-prop</cmis:displayName> + <cmis:queryName>secondary-prop</cmis:queryName> + <cmis:description>This is a Secondary type property</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + </cmism:type> + </cmism:getTypeDefinitionResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/set-content-stream.http b/qa/libcmis/data/ws/set-content-stream.http new file mode 100644 index 0000000..9112cc1 --- /dev/null +++ b/qa/libcmis/data/ws/set-content-stream.http @@ -0,0 +1,28 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:setContentStreamResponse + xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" + xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objectId>test-document</cmism:objectId> + <cmism:changeToken>set-content-token</cmism:changeToken> + </cmism:setContentStreamResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/test-document-add-secondary.http b/qa/libcmis/data/ws/test-document-add-secondary.http new file mode 100644 index 0000000..88433a9 --- /dev/null +++ b/qa/libcmis/data/ws/test-document-add-secondary.http @@ -0,0 +1,148 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>series-id</cmis:value> + </cmis:propertyId> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>some-change-token</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:secondaryObjectTypeIds" displayName="cmis:secondaryObjectTypeIds" localName="cmis:secondaryObjectTypeIds" propertyDefinitionId="cmis:secondaryObjectTypeIds"> + <cmis:value>secondary-type</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="secondary-prop" displayName="secondary-prop" localName="secondary-prop" propertyDefinitionId="secondary-prop"> + <cmis:value>some-value</cmis:value> + </cmis:propertyString> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>false</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>false</cmis:canGetFolderParent> + <cmis:canGetDescendants>false</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>true</cmis:canDeleteContentStream> + <cmis:canCheckOut>true</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>true</cmis:canSetContentStream> + <cmis:canGetAllVersions>true</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>true</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>true</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>true</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>false</cmis:canGetChildren> + <cmis:canCreateDocument>false</cmis:canCreateDocument> + <cmis:canCreateFolder>false</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>false</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/test-document-parents.http b/qa/libcmis/data/ws/test-document-parents.http new file mode 100644 index 0000000..1c5e398 --- /dev/null +++ b/qa/libcmis/data/ws/test-document-parents.http @@ -0,0 +1,106 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectParentsResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:parents> + <cmism:object> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Parent 1</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Parent 1</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>parent1</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-31T08:04:35.866Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359619475867</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-31T08:04:35.867Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + <cmism:object> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Parent 2</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Parent 2</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>parent2</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-31T08:04:35.866Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1359619475867</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-31T08:04:35.867Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + </cmism:object> + </cmism:parents> + </cmism:getObjectParentsResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/test-document-updated.http b/qa/libcmis/data/ws/test-document-updated.http new file mode 100644 index 0000000..a5c34e9 --- /dev/null +++ b/qa/libcmis/data/ws/test-document-updated.http @@ -0,0 +1,140 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"/> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>New name</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>some-new-change-token</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>false</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>false</cmis:canGetFolderParent> + <cmis:canGetDescendants>false</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>true</cmis:canDeleteContentStream> + <cmis:canCheckOut>true</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>true</cmis:canSetContentStream> + <cmis:canGetAllVersions>true</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>true</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>true</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>true</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>false</cmis:canGetChildren> + <cmis:canCreateDocument>false</cmis:canCreateDocument> + <cmis:canCreateFolder>false</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>false</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/test-document.http b/qa/libcmis/data/ws/test-document.http new file mode 100644 index 0000000..890274c --- /dev/null +++ b/qa/libcmis/data/ws/test-document.http @@ -0,0 +1,142 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"/> + <cmis:propertyHtml queryName="HtmlProp" displayName="Sample Html Property" localName="HtmlProp" propertyDefinitionId="HtmlProp"/> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"/> + <cmis:propertyId queryName="IdProp" displayName="Sample Id Property" localName="IdProp" propertyDefinitionId="IdProp"/> + <cmis:propertyUri queryName="UriProp" displayName="Sample Uri Property" localName="UriProp" propertyDefinitionId="UriProp"/> + <cmis:propertyDateTime queryName="DateTimePropMV" displayName="Sample DateTime multi-value Property" localName="DateTimePropMV" propertyDefinitionId="DateTimePropMV"/> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>series-id</cmis:value> + </cmis:propertyId> + <cmis:propertyDecimal queryName="DecimalProp" displayName="Sample Decimal Property" localName="DecimalProp" propertyDefinitionId="DecimalProp"/> + <cmis:propertyUri queryName="UriPropMV" displayName="Sample Uri multi-value Property" localName="UriPropMV" propertyDefinitionId="UriPropMV"/> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"/> + <cmis:propertyBoolean queryName="BooleanProp" displayName="Sample Boolean Property" localName="BooleanProp" propertyDefinitionId="BooleanProp"/> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="IdPropMV" displayName="Sample Id Html multi-value Property" localName="IdPropMV" propertyDefinitionId="IdPropMV"/> + <cmis:propertyString queryName="PickListProp" displayName="Sample Pick List Property" localName="PickListProp" propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:propertyString> + <cmis:propertyHtml queryName="HtmlPropMV" displayName="Sample Html multi-value Property" localName="HtmlPropMV" propertyDefinitionId="HtmlPropMV"/> + <cmis:propertyInteger queryName="IntProp" displayName="Sample Int Property" localName="IntProp" propertyDefinitionId="IntProp"/> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="StringProp" displayName="Sample String Property" localName="StringProp" propertyDefinitionId="StringProp"> + <cmis:value>My Doc StringProperty 6</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>some-change-token</cmis:value> + </cmis:propertyString> + <cmis:propertyDecimal queryName="DecimalPropMV" displayName="Sample Decimal multi-value Property" localName="DecimalPropMV" propertyDefinitionId="DecimalPropMV"/> + <cmis:propertyDateTime queryName="DateTimeProp" displayName="Sample DateTime Property" localName="DateTimeProp" propertyDefinitionId="DateTimeProp"/> + <cmis:propertyBoolean queryName="BooleanPropMV" displayName="Sample Boolean multi-value Property" localName="BooleanPropMV" propertyDefinitionId="BooleanPropMV"/> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>test-document</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyInteger queryName="IntPropMV" displayName="Sample Int multi-value Property" localName="IntPropMV" propertyDefinitionId="IntPropMV"/> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-01-28T14:10:06.736Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>false</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>false</cmis:canGetFolderParent> + <cmis:canGetDescendants>false</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>true</cmis:canDeleteContentStream> + <cmis:canCheckOut>true</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>true</cmis:canSetContentStream> + <cmis:canGetAllVersions>true</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>true</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>true</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>true</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>false</cmis:canGetChildren> + <cmis:canCreateDocument>false</cmis:canCreateDocument> + <cmis:canCreateFolder>false</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>false</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">test-document</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/type-bad.http b/qa/libcmis/data/ws/type-bad.http new file mode 100644 index 0000000..69825e7 --- /dev/null +++ b/qa/libcmis/data/ws/type-bad.http @@ -0,0 +1,33 @@ +Content-Type: multipart/related;start="<rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8";start-info="text/xml" + +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8 +Content-Id: <rootpart*abd9e41c-b3aa-40a7-a73a-997c472010e8@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version="1.0" encoding="UTF-8"?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-30T19:54:27Z</Created> + <Expires>2013-10-01T19:54:27Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <S:Fault> + <faultcode>S:Server</faultcode> + <faultstring>Type 'bad' not found</faultstring> + <detail> + <cmisFault:cmisFault xmlns="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:cmisFault="http://docs.oasis-open.org/ns/cmis/messaging/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/core/200908/"> + <type>objectNotFound</type> + <code>562</code> + <message>Unknown type id: 'bad'</message> + </cmisFault:cmisFault> + </detail> + </S:Fault> + </S:Body> +</S:Envelope> +--uuid:abd9e41c-b3aa-40a7-a73a-997c472010e8-- + diff --git a/qa/libcmis/data/ws/type-docLevel1.http b/qa/libcmis/data/ws/type-docLevel1.http new file mode 100644 index 0000000..5320af2 --- /dev/null +++ b/qa/libcmis/data/ws/type-docLevel1.http @@ -0,0 +1,411 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getTypeDefinitionResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:type> + <cmis:id>DocumentLevel1</cmis:id> + <cmis:localName>DocumentLevel1</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 1</cmis:displayName> + <cmis:queryName>DocumentLevel1</cmis:queryName> + <cmis:description>Description of Document Level 1 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>cmis:document</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmism:type> + </cmism:getTypeDefinitionResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/type-docLevel2-secondary.http b/qa/libcmis/data/ws/type-docLevel2-secondary.http new file mode 100644 index 0000000..7ea2313 --- /dev/null +++ b/qa/libcmis/data/ws/type-docLevel2-secondary.http @@ -0,0 +1,698 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getTypeDefinitionResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:type> + <cmis:id>DocumentLevel2</cmis:id> + <cmis:localName>DocumentLevel2</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 2</cmis:displayName> + <cmis:queryName>DocumentLevel2</cmis:queryName> + <cmis:description>Description of Document Level 2 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>DocumentLevel1</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyHtmlDefinition> + <cmis:id>HtmlProp</cmis:id> + <cmis:localName>HtmlProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Html Property</cmis:displayName> + <cmis:queryName>HtmlProp</cmis:queryName> + <cmis:description>This is a Sample Html Property property.</cmis:description> + <cmis:propertyType>html</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyHtmlDefinition> + <cmis:propertyIdDefinition> + <cmis:id>IdProp</cmis:id> + <cmis:localName>IdProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Id Property</cmis:displayName> + <cmis:queryName>IdProp</cmis:queryName> + <cmis:description>This is a Sample Id Property property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>DateTimePropMV</cmis:id> + <cmis:localName>DateTimePropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample DateTime multi-value Property</cmis:displayName> + <cmis:queryName>DateTimePropMV</cmis:queryName> + <cmis:description>This is a Sample DateTime multi-value Property property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyUriDefinition> + <cmis:id>UriProp</cmis:id> + <cmis:localName>UriProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Uri Property</cmis:displayName> + <cmis:queryName>UriProp</cmis:queryName> + <cmis:description>This is a Sample Uri Property property.</cmis:description> + <cmis:propertyType>uri</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyUriDefinition> + <cmis:propertyDecimalDefinition> + <cmis:id>DecimalProp</cmis:id> + <cmis:localName>DecimalProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Decimal Property</cmis:displayName> + <cmis:queryName>DecimalProp</cmis:queryName> + <cmis:description>This is a Sample Decimal Property property.</cmis:description> + <cmis:propertyType>decimal</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDecimalDefinition> + <cmis:propertyUriDefinition> + <cmis:id>UriPropMV</cmis:id> + <cmis:localName>UriPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Uri multi-value Property</cmis:displayName> + <cmis:queryName>UriPropMV</cmis:queryName> + <cmis:description>This is a Sample Uri multi-value Property property.</cmis:description> + <cmis:propertyType>uri</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyUriDefinition> + <cmis:propertyIdDefinition> + <cmis:id>IdPropMV</cmis:id> + <cmis:localName>IdPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Id Html multi-value Property</cmis:displayName> + <cmis:queryName>IdPropMV</cmis:queryName> + <cmis:description>This is a Sample Id Html multi-value Property property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>PickListProp</cmis:id> + <cmis:localName>PickListProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Pick List Property</cmis:displayName> + <cmis:queryName>PickListProp</cmis:queryName> + <cmis:description>This is a Sample Pick List Property property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + <cmis:defaultValue propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:defaultValue> + <cmis:choice displayName=""> + <cmis:value>red</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>green</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>blue</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>black</cmis:value> + </cmis:choice> + </cmis:propertyStringDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>IntProp</cmis:id> + <cmis:localName>IntProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Int Property</cmis:displayName> + <cmis:queryName>IntProp</cmis:queryName> + <cmis:description>This is a Sample Int Property property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyHtmlDefinition> + <cmis:id>HtmlPropMV</cmis:id> + <cmis:localName>HtmlPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Html multi-value Property</cmis:displayName> + <cmis:queryName>HtmlPropMV</cmis:queryName> + <cmis:description>This is a Sample Html multi-value Property property.</cmis:description> + <cmis:propertyType>html</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyHtmlDefinition> + <cmis:propertyStringDefinition> + <cmis:id>StringProp</cmis:id> + <cmis:localName>StringProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample String Property</cmis:displayName> + <cmis:queryName>StringProp</cmis:queryName> + <cmis:description>This is a Sample String Property property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDecimalDefinition> + <cmis:id>DecimalPropMV</cmis:id> + <cmis:localName>DecimalPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Decimal multi-value Property</cmis:displayName> + <cmis:queryName>DecimalPropMV</cmis:queryName> + <cmis:description>This is a Sample Decimal multi-value Property property.</cmis:description> + <cmis:propertyType>decimal</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDecimalDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>DateTimeProp</cmis:id> + <cmis:localName>DateTimeProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample DateTime Property</cmis:displayName> + <cmis:queryName>DateTimeProp</cmis:queryName> + <cmis:description>This is a Sample DateTime Property property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>BooleanProp</cmis:id> + <cmis:localName>BooleanProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Boolean Property</cmis:displayName> + <cmis:queryName>BooleanProp</cmis:queryName> + <cmis:description>This is a Sample Boolean Property property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>BooleanPropMV</cmis:id> + <cmis:localName>BooleanPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Boolean multi-value Property</cmis:displayName> + <cmis:queryName>BooleanPropMV</cmis:queryName> + <cmis:description>This is a Sample Boolean multi-value Property property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>IntPropMV</cmis:id> + <cmis:localName>IntPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Int multi-value Property</cmis:displayName> + <cmis:queryName>IntPropMV</cmis:queryName> + <cmis:description>This is a Sample Int multi-value Property property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:secondaryObjectTypeIds</cmis:id> + <cmis:localName>cmis:secondaryObjectTypeIds</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>cmis:secondaryObjectTypeIds</cmis:displayName> + <cmis:queryName>cmis:secondaryObjectTypeIds</cmis:queryName> + <cmis:description>This the property holding the Ids of the secondary types.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmism:type> + </cmism:getTypeDefinitionResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/type-docLevel2.http b/qa/libcmis/data/ws/type-docLevel2.http new file mode 100644 index 0000000..2524387 --- /dev/null +++ b/qa/libcmis/data/ws/type-docLevel2.http @@ -0,0 +1,682 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getTypeDefinitionResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:type> + <cmis:id>DocumentLevel2</cmis:id> + <cmis:localName>DocumentLevel2</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 2</cmis:displayName> + <cmis:queryName>DocumentLevel2</cmis:queryName> + <cmis:description>Description of Document Level 2 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>DocumentLevel1</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyHtmlDefinition> + <cmis:id>HtmlProp</cmis:id> + <cmis:localName>HtmlProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Html Property</cmis:displayName> + <cmis:queryName>HtmlProp</cmis:queryName> + <cmis:description>This is a Sample Html Property property.</cmis:description> + <cmis:propertyType>html</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyHtmlDefinition> + <cmis:propertyIdDefinition> + <cmis:id>IdProp</cmis:id> + <cmis:localName>IdProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Id Property</cmis:displayName> + <cmis:queryName>IdProp</cmis:queryName> + <cmis:description>This is a Sample Id Property property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>DateTimePropMV</cmis:id> + <cmis:localName>DateTimePropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample DateTime multi-value Property</cmis:displayName> + <cmis:queryName>DateTimePropMV</cmis:queryName> + <cmis:description>This is a Sample DateTime multi-value Property property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyUriDefinition> + <cmis:id>UriProp</cmis:id> + <cmis:localName>UriProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Uri Property</cmis:displayName> + <cmis:queryName>UriProp</cmis:queryName> + <cmis:description>This is a Sample Uri Property property.</cmis:description> + <cmis:propertyType>uri</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyUriDefinition> + <cmis:propertyDecimalDefinition> + <cmis:id>DecimalProp</cmis:id> + <cmis:localName>DecimalProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Decimal Property</cmis:displayName> + <cmis:queryName>DecimalProp</cmis:queryName> + <cmis:description>This is a Sample Decimal Property property.</cmis:description> + <cmis:propertyType>decimal</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDecimalDefinition> + <cmis:propertyUriDefinition> + <cmis:id>UriPropMV</cmis:id> + <cmis:localName>UriPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Uri multi-value Property</cmis:displayName> + <cmis:queryName>UriPropMV</cmis:queryName> + <cmis:description>This is a Sample Uri multi-value Property property.</cmis:description> + <cmis:propertyType>uri</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyUriDefinition> + <cmis:propertyIdDefinition> + <cmis:id>IdPropMV</cmis:id> + <cmis:localName>IdPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Id Html multi-value Property</cmis:displayName> + <cmis:queryName>IdPropMV</cmis:queryName> + <cmis:description>This is a Sample Id Html multi-value Property property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>PickListProp</cmis:id> + <cmis:localName>PickListProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Pick List Property</cmis:displayName> + <cmis:queryName>PickListProp</cmis:queryName> + <cmis:description>This is a Sample Pick List Property property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + <cmis:defaultValue propertyDefinitionId="PickListProp"> + <cmis:value>blue</cmis:value> + </cmis:defaultValue> + <cmis:choice displayName=""> + <cmis:value>red</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>green</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>blue</cmis:value> + </cmis:choice> + <cmis:choice displayName=""> + <cmis:value>black</cmis:value> + </cmis:choice> + </cmis:propertyStringDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>IntProp</cmis:id> + <cmis:localName>IntProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Int Property</cmis:displayName> + <cmis:queryName>IntProp</cmis:queryName> + <cmis:description>This is a Sample Int Property property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyHtmlDefinition> + <cmis:id>HtmlPropMV</cmis:id> + <cmis:localName>HtmlPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Html multi-value Property</cmis:displayName> + <cmis:queryName>HtmlPropMV</cmis:queryName> + <cmis:description>This is a Sample Html multi-value Property property.</cmis:description> + <cmis:propertyType>html</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyHtmlDefinition> + <cmis:propertyStringDefinition> + <cmis:id>StringProp</cmis:id> + <cmis:localName>StringProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample String Property</cmis:displayName> + <cmis:queryName>StringProp</cmis:queryName> + <cmis:description>This is a Sample String Property property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDecimalDefinition> + <cmis:id>DecimalPropMV</cmis:id> + <cmis:localName>DecimalPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Decimal multi-value Property</cmis:displayName> + <cmis:queryName>DecimalPropMV</cmis:queryName> + <cmis:description>This is a Sample Decimal multi-value Property property.</cmis:description> + <cmis:propertyType>decimal</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDecimalDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>DateTimeProp</cmis:id> + <cmis:localName>DateTimeProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample DateTime Property</cmis:displayName> + <cmis:queryName>DateTimeProp</cmis:queryName> + <cmis:description>This is a Sample DateTime Property property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>BooleanProp</cmis:id> + <cmis:localName>BooleanProp</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Boolean Property</cmis:displayName> + <cmis:queryName>BooleanProp</cmis:queryName> + <cmis:description>This is a Sample Boolean Property property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>BooleanPropMV</cmis:id> + <cmis:localName>BooleanPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Boolean multi-value Property</cmis:displayName> + <cmis:queryName>BooleanPropMV</cmis:queryName> + <cmis:description>This is a Sample Boolean multi-value Property property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>IntPropMV</cmis:id> + <cmis:localName>IntPropMV</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Sample Int multi-value Property</cmis:displayName> + <cmis:queryName>IntPropMV</cmis:queryName> + <cmis:description>This is a Sample Int multi-value Property property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmism:type> + </cmism:getTypeDefinitionResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/type-document.http b/qa/libcmis/data/ws/type-document.http new file mode 100644 index 0000000..7cda82b --- /dev/null +++ b/qa/libcmis/data/ws/type-document.http @@ -0,0 +1,410 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getTypeDefinitionResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:type> + <cmis:id>cmis:document</cmis:id> + <cmis:localName>cmis:document</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>CMIS Document</cmis:displayName> + <cmis:queryName>cmis:document</cmis:queryName> + <cmis:description>Description of CMIS Document Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmism:type> + </cmism:getTypeDefinitionResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/type-folder.http b/qa/libcmis/data/ws/type-folder.http new file mode 100644 index 0000000..bb47d01 --- /dev/null +++ b/qa/libcmis/data/ws/type-folder.http @@ -0,0 +1,232 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getTypeDefinitionResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:type> + <cmis:id>cmis:folder</cmis:id> + <cmis:localName>cmis:folder</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>CMIS Folder</cmis:displayName> + <cmis:queryName>cmis:folder</cmis:queryName> + <cmis:description>Description of CMIS Folder Type</cmis:description> + <cmis:baseId>cmis:folder</cmis:baseId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyIdDefinition> + <cmis:id>cmis:allowedChildObjectTypeIds</cmis:id> + <cmis:localName>cmis:allowedChildObjectTypeIds</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Allowed Child Types</cmis:displayName> + <cmis:queryName>cmis:allowedChildObjectTypeIds</cmis:queryName> + <cmis:description>This is a Allowed Child Types property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>multi</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:path</cmis:id> + <cmis:localName>cmis:path</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Path</cmis:displayName> + <cmis:queryName>cmis:path</cmis:queryName> + <cmis:description>This is a Path property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:parentId</cmis:id> + <cmis:localName>cmis:parentId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Parent Id</cmis:displayName> + <cmis:queryName>cmis:parentId</cmis:queryName> + <cmis:description>This is a Parent Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>false</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + </cmism:type> + </cmism:getTypeDefinitionResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/typechildren-document.http b/qa/libcmis/data/ws/typechildren-document.http new file mode 100644 index 0000000..d7df653 --- /dev/null +++ b/qa/libcmis/data/ws/typechildren-document.http @@ -0,0 +1,413 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getTypeChildrenResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:types> + <cmism:types> + <cmis:id>DocumentLevel1</cmis:id> + <cmis:localName>DocumentLevel1</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Document Level 1</cmis:displayName> + <cmis:queryName>DocumentLevel1</cmis:queryName> + <cmis:description>Description of Document Level 1 Type</cmis:description> + <cmis:baseId>cmis:document</cmis:baseId> + <cmis:parentId>cmis:document</cmis:parentId> + <cmis:creatable>true</cmis:creatable> + <cmis:fileable>true</cmis:fileable> + <cmis:queryable>false</cmis:queryable> + <cmis:fulltextIndexed>false</cmis:fulltextIndexed> + <cmis:includedInSupertypeQuery>true</cmis:includedInSupertypeQuery> + <cmis:controllablePolicy>false</cmis:controllablePolicy> + <cmis:controllableACL>true</cmis:controllableACL> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestMajorVersion</cmis:id> + <cmis:localName>cmis:isLatestMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Major Version</cmis:displayName> + <cmis:queryName>cmis:isLatestMajorVersion</cmis:queryName> + <cmis:description>This is a Is Latest Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:contentStreamId</cmis:id> + <cmis:localName>cmis:contentStreamId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Stream Id</cmis:displayName> + <cmis:queryName>cmis:contentStreamId</cmis:queryName> + <cmis:description>This is a Stream Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIntegerDefinition> + <cmis:id>cmis:contentStreamLength</cmis:id> + <cmis:localName>cmis:contentStreamLength</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Content Length</cmis:displayName> + <cmis:queryName>cmis:contentStreamLength</cmis:queryName> + <cmis:description>This is a Content Length property.</cmis:description> + <cmis:propertyType>integer</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIntegerDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionSeriesCheckedOutBy</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out By</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutBy</cmis:queryName> + <cmis:description>This is a Checked Out By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectTypeId</cmis:id> + <cmis:localName>cmis:objectTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Type-Id</cmis:displayName> + <cmis:queryName>cmis:objectTypeId</cmis:queryName> + <cmis:description>This is a Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>oncreate</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesCheckedOutId</cmis:id> + <cmis:localName>cmis:versionSeriesCheckedOutId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesCheckedOutId</cmis:queryName> + <cmis:description>This is a Checked Out Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:name</cmis:id> + <cmis:localName>cmis:name</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Name</cmis:displayName> + <cmis:queryName>cmis:name</cmis:queryName> + <cmis:description>This is a Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readwrite</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>true</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamMimeType</cmis:id> + <cmis:localName>cmis:contentStreamMimeType</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Mime Type</cmis:displayName> + <cmis:queryName>cmis:contentStreamMimeType</cmis:queryName> + <cmis:description>This is a Mime Type property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:versionSeriesId</cmis:id> + <cmis:localName>cmis:versionSeriesId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Series Id</cmis:displayName> + <cmis:queryName>cmis:versionSeriesId</cmis:queryName> + <cmis:description>This is a Version Series Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:creationDate</cmis:id> + <cmis:localName>cmis:creationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Creation Date</cmis:displayName> + <cmis:queryName>cmis:creationDate</cmis:queryName> + <cmis:description>This is a Creation Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:changeToken</cmis:id> + <cmis:localName>cmis:changeToken</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Change Token</cmis:displayName> + <cmis:queryName>cmis:changeToken</cmis:queryName> + <cmis:description>This is a Change Token property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isLatestVersion</cmis:id> + <cmis:localName>cmis:isLatestVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Latest Version</cmis:displayName> + <cmis:queryName>cmis:isLatestVersion</cmis:queryName> + <cmis:description>This is a Is Latest Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:versionLabel</cmis:id> + <cmis:localName>cmis:versionLabel</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Version Label</cmis:displayName> + <cmis:queryName>cmis:versionLabel</cmis:queryName> + <cmis:description>This is a Version Label property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isVersionSeriesCheckedOut</cmis:id> + <cmis:localName>cmis:isVersionSeriesCheckedOut</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checked Out</cmis:displayName> + <cmis:queryName>cmis:isVersionSeriesCheckedOut</cmis:queryName> + <cmis:description>This is a Checked Out property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:lastModifiedBy</cmis:id> + <cmis:localName>cmis:lastModifiedBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modified By</cmis:displayName> + <cmis:queryName>cmis:lastModifiedBy</cmis:queryName> + <cmis:description>This is a Modified By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:createdBy</cmis:id> + <cmis:localName>cmis:createdBy</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Created By</cmis:displayName> + <cmis:queryName>cmis:createdBy</cmis:queryName> + <cmis:description>This is a Created By property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:checkinComment</cmis:id> + <cmis:localName>cmis:checkinComment</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Checkin Comment</cmis:displayName> + <cmis:queryName>cmis:checkinComment</cmis:queryName> + <cmis:description>This is a Checkin Comment property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:objectId</cmis:id> + <cmis:localName>cmis:objectId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Object Id</cmis:displayName> + <cmis:queryName>cmis:objectId</cmis:queryName> + <cmis:description>This is a Object Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isMajorVersion</cmis:id> + <cmis:localName>cmis:isMajorVersion</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Is Major Version</cmis:displayName> + <cmis:queryName>cmis:isMajorVersion</cmis:queryName> + <cmis:description>This is a Is Major Version property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyBooleanDefinition> + <cmis:id>cmis:isImmutable</cmis:id> + <cmis:localName>cmis:isImmutable</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Immutable</cmis:displayName> + <cmis:queryName>cmis:isImmutable</cmis:queryName> + <cmis:description>This is a Immutable property.</cmis:description> + <cmis:propertyType>boolean</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyBooleanDefinition> + <cmis:propertyIdDefinition> + <cmis:id>cmis:baseTypeId</cmis:id> + <cmis:localName>cmis:baseTypeId</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Base-Type-Id</cmis:displayName> + <cmis:queryName>cmis:baseTypeId</cmis:queryName> + <cmis:description>This is a Base-Type-Id property.</cmis:description> + <cmis:propertyType>id</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyIdDefinition> + <cmis:propertyDateTimeDefinition> + <cmis:id>cmis:lastModificationDate</cmis:id> + <cmis:localName>cmis:lastModificationDate</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>Modification Date</cmis:displayName> + <cmis:queryName>cmis:lastModificationDate</cmis:queryName> + <cmis:description>This is a Modification Date property.</cmis:description> + <cmis:propertyType>datetime</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyDateTimeDefinition> + <cmis:propertyStringDefinition> + <cmis:id>cmis:contentStreamFileName</cmis:id> + <cmis:localName>cmis:contentStreamFileName</cmis:localName> + <cmis:localNamespace>local</cmis:localNamespace> + <cmis:displayName>File Name</cmis:displayName> + <cmis:queryName>cmis:contentStreamFileName</cmis:queryName> + <cmis:description>This is a File Name property.</cmis:description> + <cmis:propertyType>string</cmis:propertyType> + <cmis:cardinality>single</cmis:cardinality> + <cmis:updatability>readonly</cmis:updatability> + <cmis:inherited>true</cmis:inherited> + <cmis:required>false</cmis:required> + <cmis:queryable>true</cmis:queryable> + <cmis:orderable>true</cmis:orderable> + <cmis:openChoice>false</cmis:openChoice> + </cmis:propertyStringDefinition> + <cmis:versionable>false</cmis:versionable> + <cmis:contentStreamAllowed>allowed</cmis:contentStreamAllowed> + </cmism:types> + </cmism:types> + </cmism:getTypeChildrenResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/update-properties.http b/qa/libcmis/data/ws/update-properties.http new file mode 100644 index 0000000..0113a99 --- /dev/null +++ b/qa/libcmis/data/ws/update-properties.http @@ -0,0 +1,26 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:updatePropertiesResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:objectId>test-document</cmism:objectId> + <cmism:changeToken>some-new-changeToken</cmism:changeToken> + </cmism:updatePropertiesResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/valid-object.http b/qa/libcmis/data/ws/valid-object.http new file mode 100644 index 0000000..5815443 --- /dev/null +++ b/qa/libcmis/data/ws/valid-object.http @@ -0,0 +1,99 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyId queryName="cmis:allowedChildObjectTypeIds" displayName="Allowed Child Types" localName="cmis:allowedChildObjectTypeIds" propertyDefinitionId="cmis:allowedChildObjectTypeIds"> + <cmis:value>*</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:path" displayName="Path" localName="cmis:path" propertyDefinitionId="cmis:path"> + <cmis:value>/Valid Object</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>Admin</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Valid Object</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>valid-object</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2012-11-29T16:14:47.019Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1354205687020</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:folder</cmis:value> + </cmis:propertyId> + <cmis:propertyId queryName="cmis:parentId" displayName="Parent Id" localName="cmis:parentId" propertyDefinitionId="cmis:parentId"> + <cmis:value>root-folder</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2012-11-29T16:14:47.020Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <cmis:allowableActions> + <cmis:canDeleteObject>true</cmis:canDeleteObject> + <cmis:canUpdateProperties>true</cmis:canUpdateProperties> + <cmis:canGetFolderTree>true</cmis:canGetFolderTree> + <cmis:canGetProperties>true</cmis:canGetProperties> + <cmis:canGetObjectRelationships>false</cmis:canGetObjectRelationships> + <cmis:canGetObjectParents>true</cmis:canGetObjectParents> + <cmis:canGetFolderParent>true</cmis:canGetFolderParent> + <cmis:canGetDescendants>true</cmis:canGetDescendants> + <cmis:canMoveObject>true</cmis:canMoveObject> + <cmis:canDeleteContentStream>false</cmis:canDeleteContentStream> + <cmis:canCheckOut>false</cmis:canCheckOut> + <cmis:canCancelCheckOut>false</cmis:canCancelCheckOut> + <cmis:canCheckIn>false</cmis:canCheckIn> + <cmis:canSetContentStream>false</cmis:canSetContentStream> + <cmis:canGetAllVersions>false</cmis:canGetAllVersions> + <cmis:canAddObjectToFolder>false</cmis:canAddObjectToFolder> + <cmis:canRemoveObjectFromFolder>false</cmis:canRemoveObjectFromFolder> + <cmis:canGetContentStream>false</cmis:canGetContentStream> + <cmis:canApplyPolicy>false</cmis:canApplyPolicy> + <cmis:canGetAppliedPolicies>false</cmis:canGetAppliedPolicies> + <cmis:canRemovePolicy>false</cmis:canRemovePolicy> + <cmis:canGetChildren>true</cmis:canGetChildren> + <cmis:canCreateDocument>true</cmis:canCreateDocument> + <cmis:canCreateFolder>true</cmis:canCreateFolder> + <cmis:canCreateRelationship>false</cmis:canCreateRelationship> + <cmis:canDeleteTree>true</cmis:canDeleteTree> + <cmis:canGetRenditions>false</cmis:canGetRenditions> + <cmis:canGetACL>false</cmis:canGetACL> + <cmis:canApplyACL>false</cmis:canApplyACL> + </cmis:allowableActions> + <exampleExtension:exampleExtension xmlns="http://my/cmis/extension" xmlns:exampleExtension="http://my/cmis/extension"> + <objectId xmlns:ns0="http://my/cmis/extension" ns0:type="cmis:folder">valid-object</objectId> + <name>Valid Object</name> + </exampleExtension:exampleExtension> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/data/ws/working-copy.http b/qa/libcmis/data/ws/working-copy.http new file mode 100644 index 0000000..21d2e85 --- /dev/null +++ b/qa/libcmis/data/ws/working-copy.http @@ -0,0 +1,98 @@ +Content-Type: multipart/related;start="<rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:846b7f14-435a-4809-845f-b98822f936ab";start-info="text/xml" + +--uuid:846b7f14-435a-4809-845f-b98822f936ab +Content-Id: <rootpart*846b7f14-435a-4809-845f-b98822f936ab@example.jaxws.sun.com> +Content-Type: application/xop+xml;charset=utf-8;type="text/xml" +Content-Transfer-Encoding: binary + +<?xml version='1.0' encoding='UTF-8'?> +<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2013-09-16T13:34:16Z</Created> + <Expires>2013-09-17T13:34:16Z</Expires> + </Timestamp> + </Security> + </S:Header> + <S:Body> + <cmism:getObjectResponse xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"> + <cmism:object> + <cmis:properties> + <cmis:propertyBoolean queryName="cmis:isLatestMajorVersion" displayName="Is Latest Major Version" localName="cmis:isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyInteger queryName="cmis:contentStreamLength" displayName="Content Length" localName="cmis:contentStreamLength" propertyDefinitionId="cmis:contentStreamLength"> + <cmis:value>12345</cmis:value> + </cmis:propertyInteger> + <cmis:propertyString queryName="cmis:contentStreamId" displayName="Stream Id" localName="cmis:contentStreamId" propertyDefinitionId="cmis:contentStreamId"/> + <cmis:propertyId queryName="cmis:objectTypeId" displayName="Type-Id" localName="cmis:objectTypeId" propertyDefinitionId="cmis:objectTypeId"> + <cmis:value>DocumentLevel2</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:versionSeriesCheckedOutBy" displayName="Checked Out By" localName="cmis:versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:versionSeriesCheckedOutId" displayName="Checked Out Id" localName="cmis:versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId"> + <cmis:value>working-copy</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:name" displayName="Name" localName="cmis:name" propertyDefinitionId="cmis:name"> + <cmis:value>Test Document</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="VersionedStringProp" displayName="Sample String Property" localName="VersionedStringProp" propertyDefinitionId="VersionedStringProp"/> + <cmis:propertyString queryName="cmis:contentStreamMimeType" displayName="Mime Type" localName="cmis:contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType"> + <cmis:value>text/plain</cmis:value> + </cmis:propertyString> + <cmis:propertyId queryName="cmis:versionSeriesId" displayName="Version Series Id" localName="cmis:versionSeriesId" propertyDefinitionId="cmis:versionSeriesId"> + <cmis:value>version-series</cmis:value> + </cmis:propertyId> + <cmis:propertyDateTime queryName="cmis:creationDate" displayName="Creation Date" localName="cmis:creationDate" propertyDefinitionId="cmis:creationDate"> + <cmis:value>2013-05-21T13:50:45.300Z</cmis:value> + </cmis:propertyDateTime> + <cmis:propertyString queryName="cmis:changeToken" displayName="Change Token" localName="cmis:changeToken" propertyDefinitionId="cmis:changeToken"> + <cmis:value>1369144245300</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:versionLabel" displayName="Version Label" localName="cmis:versionLabel" propertyDefinitionId="cmis:versionLabel"> + <cmis:value>V 0.2</cmis:value> + </cmis:propertyString> + <cmis:propertyBoolean queryName="cmis:isLatestVersion" displayName="Is Latest Version" localName="cmis:isLatestVersion" propertyDefinitionId="cmis:isLatestVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isVersionSeriesCheckedOut" displayName="Checked Out" localName="cmis:isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut"> + <cmis:value>true</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyString queryName="cmis:lastModifiedBy" displayName="Modified By" localName="cmis:lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:createdBy" displayName="Created By" localName="cmis:createdBy" propertyDefinitionId="cmis:createdBy"> + <cmis:value>unknown</cmis:value> + </cmis:propertyString> + <cmis:propertyString queryName="cmis:checkinComment" displayName="Checkin Comment" localName="cmis:checkinComment" propertyDefinitionId="cmis:checkinComment"/> + <cmis:propertyId queryName="cmis:objectId" displayName="Object Id" localName="cmis:objectId" propertyDefinitionId="cmis:objectId"> + <cmis:value>working-copy</cmis:value> + </cmis:propertyId> + <cmis:propertyBoolean queryName="cmis:isMajorVersion" displayName="Is Major Version" localName="cmis:isMajorVersion" propertyDefinitionId="cmis:isMajorVersion"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyBoolean queryName="cmis:isImmutable" displayName="Immutable" localName="cmis:isImmutable" propertyDefinitionId="cmis:isImmutable"> + <cmis:value>false</cmis:value> + </cmis:propertyBoolean> + <cmis:propertyId queryName="cmis:baseTypeId" displayName="Base-Type-Id" localName="cmis:baseTypeId" propertyDefinitionId="cmis:baseTypeId"> + <cmis:value>cmis:document</cmis:value> + </cmis:propertyId> + <cmis:propertyString queryName="cmis:contentStreamFileName" displayName="File Name" localName="cmis:contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName"> + <cmis:value>data.txt</cmis:value> + </cmis:propertyString> + <cmis:propertyDateTime queryName="cmis:lastModificationDate" displayName="Modification Date" localName="cmis:lastModificationDate" propertyDefinitionId="cmis:lastModificationDate"> + <cmis:value>2013-05-21T13:50:45.300Z</cmis:value> + </cmis:propertyDateTime> + </cmis:properties> + <exampleExtension:exampleExtension xmlns="http://mockup/cmis/extension" xmlns:exampleExtension="http://mockup/cmis/extension"> + <objectId xmlns:ns0="http://mockup/cmis/extension" ns0:type="DocumentLevel2">working-copy</objectId> + <name>Test Document</name> + </exampleExtension:exampleExtension> + </cmism:object> + </cmism:getObjectResponse> + </S:Body> +</S:Envelope> +--uuid:846b7f14-435a-4809-845f-b98822f936ab-- + diff --git a/qa/libcmis/test-atom.cxx b/qa/libcmis/test-atom.cxx new file mode 100644 index 0000000..f0ce29f --- /dev/null +++ b/qa/libcmis/test-atom.cxx @@ -0,0 +1,1250 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <memory> +#include <sstream> + +#define SERVER_URL string( "http://mockup/binding" ) +#define SERVER_REPOSITORY string( "mock" ) +#define SERVER_USERNAME "tester" +#define SERVER_PASSWORD "somepass" + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <libcmis/document.hxx> +#include <libcmis/session-factory.hxx> + +#include <mockup-config.h> +#include "test-helpers.hxx" +#include "atom-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +typedef std::unique_ptr<AtomPubSession> AtomPubSessionPtr; + +class AtomTest : public CppUnit::TestFixture +{ + public: + void sessionCreationTest( ); + void sessionCreationBadAuthTest( ); + void sessionCreationProxyTest( ); + void authCallbackTest( ); + void invalidSSLTest( ); + void getRepositoriesTest( ); + void getTypeTest( ); + void getUnexistantTypeTest( ); + void getTypeParentsTest( ); + void getTypeChildrenTest( ); + void getObjectTest( ); + void getDocumentTest( ); + void getDocumentRelationshipsTest( ); + void getUnexistantObjectTest( ); + void getFolderTest( ); + void getFolderBadTypeTest( ); + void getByPathValidTest( ); + void getByPathInvalidTest( ); + void getRenditionsTest( ); + void getAllowableActionsTest( ); + void getAllowableActionsNotIncludedTest( ); + void getChildrenTest( ); + void getDocumentParentsTest( ); + void getContentStreamTest( ); + void setContentStreamTest( ); + void updatePropertiesTest( ); + void updatePropertiesEmptyTest( ); + void createFolderTest( ); + void createFolderBadTypeTest( ); + void createDocumentTest( ); + void deleteDocumentTest( ); + void deleteFolderTreeTest( ); + void checkOutTest( ); + void cancelCheckOutTest( ); + void checkInTest( ); + void getAllVersionsTest( ); + void moveTest( ); + + CPPUNIT_TEST_SUITE( AtomTest ); + CPPUNIT_TEST( sessionCreationTest ); + CPPUNIT_TEST( sessionCreationBadAuthTest ); + CPPUNIT_TEST( sessionCreationProxyTest ); + CPPUNIT_TEST( authCallbackTest ); + CPPUNIT_TEST( invalidSSLTest ); + CPPUNIT_TEST( getRepositoriesTest ); + CPPUNIT_TEST( getTypeTest ); + CPPUNIT_TEST( getUnexistantTypeTest ); + CPPUNIT_TEST( getTypeParentsTest ); + CPPUNIT_TEST( getTypeChildrenTest ); + CPPUNIT_TEST( getObjectTest ); + CPPUNIT_TEST( getDocumentTest ); + CPPUNIT_TEST( getDocumentRelationshipsTest ); + CPPUNIT_TEST( getUnexistantObjectTest ); + CPPUNIT_TEST( getFolderTest ); + CPPUNIT_TEST( getFolderBadTypeTest ); + CPPUNIT_TEST( getByPathValidTest ); + CPPUNIT_TEST( getByPathInvalidTest ); + CPPUNIT_TEST( getRenditionsTest ); + CPPUNIT_TEST( getAllowableActionsTest ); + CPPUNIT_TEST( getAllowableActionsNotIncludedTest ); + CPPUNIT_TEST( getChildrenTest ); + CPPUNIT_TEST( getDocumentParentsTest ); + CPPUNIT_TEST( getContentStreamTest ); + CPPUNIT_TEST( setContentStreamTest ); + CPPUNIT_TEST( updatePropertiesTest ); + CPPUNIT_TEST( updatePropertiesEmptyTest ); + CPPUNIT_TEST( createFolderTest ); + CPPUNIT_TEST( createFolderBadTypeTest ); + CPPUNIT_TEST( createDocumentTest ); + CPPUNIT_TEST( deleteDocumentTest ); + CPPUNIT_TEST( deleteFolderTreeTest ); + CPPUNIT_TEST( checkOutTest ); + CPPUNIT_TEST( cancelCheckOutTest ); + CPPUNIT_TEST( checkInTest ); + CPPUNIT_TEST( getAllVersionsTest ); + CPPUNIT_TEST( moveTest ); + CPPUNIT_TEST_SUITE_END( ); + + AtomPubSessionPtr getTestSession( string username = string( ), string password = string( ) ); +}; + +class TestAuthProvider : public libcmis::AuthProvider +{ + bool m_fail; + + public: + TestAuthProvider( bool fail ) : m_fail( fail ) { } + + bool authenticationQuery( std::string&, std::string& password ) + { + password = SERVER_PASSWORD; + return !m_fail; + } +}; + +class TestCertValidationHandler : public libcmis::CertValidationHandler +{ + public: + + bool m_fail; + vector< string > m_chain; + + TestCertValidationHandler( bool fail ) : m_fail( fail ), m_chain() { } + + bool validateCertificate( vector< string > chain ) + { + m_chain = chain; + return !m_fail; + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( AtomTest ); + +void AtomTest::sessionCreationTest( ) +{ + // Response showing one mock repository + curl_mockup_reset( ); + curl_mockup_setResponse( DATA_DIR "/atom/workspaces.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, SERVER_USERNAME, SERVER_PASSWORD ); + + // Check for the mandatory collection URLs + CPPUNIT_ASSERT_MESSAGE( "root collection URL missing", + !session.getAtomRepository()->getCollectionUrl( Collection::Root ).empty() ); + CPPUNIT_ASSERT_MESSAGE( "types collection URL missing", + !session.getAtomRepository()->getCollectionUrl( Collection::Types ).empty() ); + CPPUNIT_ASSERT_MESSAGE( "query collection URL missing", + !session.getAtomRepository()->getCollectionUrl( Collection::Query ).empty() ); + + // The optional collection URLs are present on InMemory, so check them + CPPUNIT_ASSERT_MESSAGE( "checkedout collection URL missing", + !session.getAtomRepository()->getCollectionUrl( Collection::CheckedOut ).empty() ); + CPPUNIT_ASSERT_MESSAGE( "unfiled collection URL missing", + !session.getAtomRepository()->getCollectionUrl( Collection::Unfiled ).empty() ); + + // Check for the mandatory URI template URLs + CPPUNIT_ASSERT_MESSAGE( "objectbyid URI template URL missing", + !session.getAtomRepository()->getUriTemplate( UriTemplate::ObjectById ).empty() ); + CPPUNIT_ASSERT_MESSAGE( "objectbypath URI template URL missing", + !session.getAtomRepository()->getUriTemplate( UriTemplate::ObjectByPath ).empty() ); + CPPUNIT_ASSERT_MESSAGE( "typebyid URI template URL missing", + !session.getAtomRepository()->getUriTemplate( UriTemplate::TypeById ).empty() ); + + // The optional URI template URL is present on InMemory, so check it + CPPUNIT_ASSERT_MESSAGE( "query URI template URL missing", + !session.getAtomRepository()->getUriTemplate( UriTemplate::Query ).empty() ); + + // Check that the root id is defined + CPPUNIT_ASSERT_MESSAGE( "Root node ID is missing", + !session.getRootId().empty() ); +} + +void AtomTest::sessionCreationBadAuthTest( ) +{ + // Response showing one mock repository + curl_mockup_reset( ); + curl_mockup_setResponse( DATA_DIR "/atom/workspaces.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + try + { + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, "bad", "bad" ); + CPPUNIT_FAIL( "Exception should have been thrown" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong error type", string( "permissionDenied" ), e.getType( ) ); + } +} + +void AtomTest::sessionCreationProxyTest( ) +{ + // Response showing one mock repository + curl_mockup_reset( ); + curl_mockup_setResponse( DATA_DIR "/atom/workspaces.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + string proxy( "proxy" ); + string noProxy( "noProxy" ); + string proxyUser( "proxyUser" ); + string proxyPass( "proxyPass" ); + + libcmis::SessionFactory::setProxySettings( proxy, noProxy, proxyUser, proxyPass ); + + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, SERVER_USERNAME, SERVER_PASSWORD ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Proxy not set", proxy, string( curl_mockup_getProxy( session.m_curlHandle ) ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "NoProxy not set", noProxy, string( curl_mockup_getNoProxy( session.m_curlHandle ) ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Proxy User not set", proxyUser, string( curl_mockup_getProxyUser( session.m_curlHandle ) ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Proxy Pass not set", proxyPass, string( curl_mockup_getProxyPass( session.m_curlHandle ) ) ); + + // Reset proxy settings to default for next tests + libcmis::SessionFactory::setProxySettings( string(), string(), string(), string() ); +} + +void AtomTest::authCallbackTest( ) +{ + // Response showing one mock repository + curl_mockup_reset( ); + curl_mockup_setResponse( DATA_DIR "/atom/workspaces.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + + // Test cancelled authentication + { + libcmis::AuthProviderPtr authProvider( new TestAuthProvider( true ) ); + libcmis::SessionFactory::setAuthenticationProvider( authProvider ); + try + { + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, SERVER_USERNAME, string( ) ); + CPPUNIT_FAIL( "Should raise an exception saying the user cancelled the authentication" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception message", + string( "User cancelled authentication request" ), string( e.what() ) ); + } + } + + // Test provided authentication + { + libcmis::AuthProviderPtr authProvider( new TestAuthProvider( false ) ); + libcmis::SessionFactory::setAuthenticationProvider( authProvider ); + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, SERVER_USERNAME, string( ) ); + } +} + +void AtomTest::invalidSSLTest( ) +{ + // Response showing one mock repository + curl_mockup_reset( ); + curl_mockup_setResponse( DATA_DIR "/atom/workspaces.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + string badCert( "A really invalid SSL Certificate" ); + curl_mockup_setSSLBadCertificate( badCert.c_str() ); + + // Test validated certificate case + { + libcmis::CertValidationHandlerPtr handler( new TestCertValidationHandler( false ) ); + libcmis::SessionFactory::setCertificateValidationHandler( handler ); + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, SERVER_USERNAME, SERVER_PASSWORD ); + + TestCertValidationHandler* handler_impl = static_cast< TestCertValidationHandler* >( handler.get( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of certificates provided", size_t( 1 ), handler_impl->m_chain.size( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad certificate provided", badCert, handler_impl->m_chain.front() ); + } + + // Test cancelled validation case + { + libcmis::CertValidationHandlerPtr handler( new TestCertValidationHandler( true ) ); + libcmis::SessionFactory::setCertificateValidationHandler( handler ); + try + { + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, SERVER_USERNAME, SERVER_PASSWORD ); + CPPUNIT_FAIL( "Should raise an exception saying the user didn't validate the SSL certificate" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception message", + string( "Invalid SSL certificate" ), string( e.what() ) ); + } + } +} + +void AtomTest::getRepositoriesTest( ) +{ + // Response showing one mock repository + curl_mockup_reset( ); + curl_mockup_setResponse( DATA_DIR "/atom/workspaces.xml" ); + + AtomPubSession session( SERVER_URL, SERVER_REPOSITORY, SERVER_USERNAME, SERVER_PASSWORD ); + vector< libcmis::RepositoryPtr > actual = session.getRepositories( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of repositories", size_t( 1 ), actual.size( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong repository found", SERVER_REPOSITORY, actual.front()->getId( ) ); +} + +void AtomTest::getTypeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "cmis:folder" ); + libcmis::ObjectTypePtr actual = session->getType( expectedId ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Id for fetched type", expectedId, actual->getId( ) ); +} + +void AtomTest::getUnexistantTypeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "bad_type" ); + try + { + session->getType( expectedId ); + CPPUNIT_FAIL( "Exception should be raised: invalid ID" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong error type", string( "objectNotFound" ), e.getType() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception message", string( "No such type: bad_type" ), string( e.what() ) ); + } +} + +void AtomTest::getTypeParentsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel1", "GET", DATA_DIR "/atom/type-docLevel1.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:document", "GET", DATA_DIR "/atom/type-document.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectTypePtr actual = session->getType( "DocumentLevel2" ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Parent type", string( "DocumentLevel1" ), actual->getParentType( )->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Base type", string( "cmis:document" ), actual->getBaseType( )->getId( ) ); +} + +void AtomTest::getTypeChildrenTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel1", "GET", DATA_DIR "/atom/type-docLevel1.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:document", "GET", DATA_DIR "/atom/type-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/types", "typeId=cmis:document", "GET", DATA_DIR "/atom/typechildren-document.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectTypePtr actual = session->getType( "cmis:document" ); + vector< libcmis::ObjectTypePtr > children = actual->getChildren( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of children", size_t( 1 ), children.size( ) ); +} + +void AtomTest::getObjectTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=valid-object", "GET", DATA_DIR "/atom/valid-object.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "valid-object" ); + libcmis::ObjectPtr actual = session->getObject( expectedId ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Id for fetched object", expectedId, actual->getId( ) ); +} + +void AtomTest::getDocumentTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "test-document" ); + libcmis::ObjectPtr actual = session->getObject( expectedId ); + + // Do we have a document? + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( actual ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::DocumentPtr", + NULL != document ); + + // Test the document properties + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document ID", expectedId, document->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document name", string( "Test Document" ), document->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document type", string( "text/plain" ), document->getContentType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:document" ), document->getBaseType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !document->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !document->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModifiedBy is missing", !document->getLastModifiedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !document->getLastModificationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "ChangeToken is missing", !document->getChangeToken( ).empty( ) ); + + CPPUNIT_ASSERT_MESSAGE( "Content length is missing", 12345 == document->getContentLength( ) ); +} + +void AtomTest::getDocumentRelationshipsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document-relationships.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "test-document" ); + libcmis::ObjectPtr actual = session->getObject( expectedId ); + + // Do we have a document? + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( actual ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::DocumentPtr", + NULL != document ); + + // Test the document properties + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document ID", expectedId, document->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document name", string( "Test Document" ), document->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document type", string( "text/plain" ), document->getContentType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:document" ), document->getBaseType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !document->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !document->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModifiedBy is missing", !document->getLastModifiedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !document->getLastModificationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "ChangeToken is missing", !document->getChangeToken( ).empty( ) ); + + CPPUNIT_ASSERT_MESSAGE( "Content length is missing", 12345 == document->getContentLength( ) ); +} + +void AtomTest::getFolderTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=valid-object", "GET", DATA_DIR "/atom/valid-object.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=root-folder", "GET", DATA_DIR "/atom/root-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "valid-object" ); + libcmis::FolderPtr actual = session->getFolder( expectedId ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder ID", expectedId, actual->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder name", string( "Valid Object" ), actual->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder path", string( "/Valid Object" ), actual->getPath( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder paths", + string( "/Valid Object" ), actual->getPaths( )[0] ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Folder should have only one path", + size_t(1), actual->getPaths( ).size() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:folder" ), actual->getBaseType( ) ); + CPPUNIT_ASSERT_MESSAGE( "Missing folder parent", actual->getFolderParent( ).get( ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a root folder", !actual->isRootFolder() ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !actual->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !actual->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModifiedBy is missing", !actual->getLastModifiedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !actual->getLastModificationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "ChangeToken is missing", !actual->getChangeToken( ).empty( ) ); +} + +void AtomTest::getFolderBadTypeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::FolderPtr actual = session->getFolder( "test-document" ); + + CPPUNIT_ASSERT_MESSAGE( "returned folder should be an empty pointer", NULL == actual ); +} + +void AtomTest::getUnexistantObjectTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string id( "bad_object" ); + try + { + session->getObject( id ); + CPPUNIT_FAIL( "Exception should be raised: invalid ID" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong CMIS exception type", string( "objectNotFound" ), e.getType() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception message", string( "No such node: " ) + id, string( e.what() ) ); + } +} + +void AtomTest::getByPathValidTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/path", "path=/Valid Object", "GET", DATA_DIR "/atom/valid-object.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr actual = session->getObjectByPath( string( "/Valid Object" ) ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Id for fetched object", string( "valid-object" ), actual->getId( ) ); +} + +void AtomTest::getByPathInvalidTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + try + { + session->getObjectByPath( string( "/some/invalid/path" ) ); + CPPUNIT_FAIL( "Exception should be thrown: invalid Path" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong error type", string( "objectNotFound" ), e.getType() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception message", + string( "No node corresponding to path: /some/invalid/path" ), string( e.what() ) ); + } + +} + +void AtomTest::getRenditionsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "test-document" ); + libcmis::ObjectPtr actual = session->getObject( expectedId ); + + std::vector< libcmis::RenditionPtr > renditions = actual->getRenditions( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad renditions count", size_t( 2 ), renditions.size( ) ); + + libcmis::RenditionPtr rendition = renditions[0]; + CPPUNIT_ASSERT( rendition->isThumbnail() ); + + rendition = renditions[1]; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition mime type", string( "application/pdf" ), rendition->getMimeType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition href", string( "http://mockup/mock/renditions?id=test-document-rendition2" ), rendition->getUrl() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition length - default case", long( -1 ), rendition->getLength( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition Title", string( "Doc as PDF" ), rendition->getTitle( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition kind", string( "pdf" ), rendition->getKind( ) ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition length - filled case", long( 40385 ), renditions[0]->getLength( ) ); +} + +void AtomTest::getAllowableActionsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=valid-object", "GET", DATA_DIR "/atom/valid-object.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "valid-object" ); + libcmis::FolderPtr actual = session->getFolder( expectedId ); + + boost::shared_ptr< libcmis::AllowableActions > toCheck = actual->getAllowableActions( ); + CPPUNIT_ASSERT_MESSAGE( "ApplyACL allowable action not defined... are all the actions read?", + toCheck->isDefined( libcmis::ObjectAction::ApplyACL ) ); + + CPPUNIT_ASSERT_MESSAGE( "GetChildren allowable action should be true", + toCheck->isDefined( libcmis::ObjectAction::GetChildren ) && + toCheck->isAllowed( libcmis::ObjectAction::GetChildren ) ); +} + +void AtomTest::getAllowableActionsNotIncludedTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=valid-object", "GET", DATA_DIR "/atom/valid-object-noactions.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/allowableactions", "id=valid-object", "GET", DATA_DIR "/atom/allowable-actions.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + string expectedId( "valid-object" ); + libcmis::FolderPtr actual = session->getFolder( expectedId ); + + // In some cases (mostly when getting folder children), we may not have the allowable actions + // included in the object answer. Test that we are querying them when needed in those cases. + boost::shared_ptr< libcmis::AllowableActions > toCheck = actual->getAllowableActions( ); + CPPUNIT_ASSERT_MESSAGE( "ApplyACL allowable action not defined... are all the actions read?", + toCheck->isDefined( libcmis::ObjectAction::ApplyACL ) ); + + CPPUNIT_ASSERT_MESSAGE( "GetChildren allowable action should be true", + toCheck->isDefined( libcmis::ObjectAction::GetChildren ) && + toCheck->isAllowed( libcmis::ObjectAction::GetChildren ) ); +} + +void AtomTest::getChildrenTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/children", "id=root-folder", "GET", DATA_DIR "/atom/root-children.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=root-folder", "GET", DATA_DIR "/atom/root-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + vector< libcmis::ObjectPtr > children = session->getRootFolder()->getChildren( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of children", size_t( 5 ), children.size() ); + + int folderCount = 0; + int documentCount = 0; + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end( ); ++it ) + { + if ( NULL != boost::dynamic_pointer_cast< libcmis::Folder >( *it ) ) + ++folderCount; + else if ( NULL != boost::dynamic_pointer_cast< libcmis::Document >( *it ) ) + ++documentCount; + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of folder children", 2, folderCount ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of document children", 3, documentCount ); +} + +void AtomTest::getDocumentParentsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/parents", "id=test-document", "GET", DATA_DIR "/atom/test-document-parents.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr object = session->getObject( "test-document" ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + CPPUNIT_ASSERT_MESSAGE( "Document expected", document != NULL ); + vector< libcmis::FolderPtr > actual = document->getParents( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of parents", size_t( 2 ), actual.size() ); + + vector< string > paths = document->getPaths(); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of paths", size_t( 2 ), paths.size() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad document paths", + string( "/Parent 1/Test Document" ), paths[0] ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad document paths", + string( "/Parent 2/Test Document" ), paths[1] ); +} + +void AtomTest::getContentStreamTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + + string expectedContent( "Some content stream" ); + curl_mockup_addResponse( "http://mockup/mock/content/data.txt", "id=test-document", "GET", expectedContent.c_str( ), 0, false ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr object = session->getObject( "test-document" ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + try + { + boost::shared_ptr< istream > is = document->getContentStream( ); + ostringstream out; + out << is->rdbuf(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", expectedContent, out.str( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void AtomTest::setContentStreamTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/content/data.txt", "id=test-document", "PUT", "Updated", 0, false ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr object = session->getObject( "test-document" ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + try + { + string expectedContent( "Some content stream to set" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "name.txt" ); + document->setContentStream( os, "text/plain", filename ); + + CPPUNIT_ASSERT_MESSAGE( "Object not refreshed during setContentStream", object->getRefreshTimestamp( ) > 0 ); + + // Check the content has been properly uploaded + const char* content = curl_mockup_getRequestBody( "http://mockup/mock/content/", "id=test-document", "PUT" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void AtomTest::updatePropertiesTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "PUT", DATA_DIR "/atom/test-document-updated.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + // Values for the test + libcmis::ObjectPtr object = session->getObject( "test-document" ); + string propertyName( "cmis:name" ); + string expectedValue( "New name" ); + + // Fill the map of properties to change + PropertyPtrMap newProperties; + + libcmis::ObjectTypePtr objectType = object->getTypeDescription( ); + map< string, libcmis::PropertyTypePtr >::iterator it = objectType->getPropertiesTypes( ).find( propertyName ); + vector< string > values; + values.push_back( expectedValue ); + libcmis::PropertyPtr property( new libcmis::Property( it->second, values ) ); + newProperties[ propertyName ] = property; + + // Update the properties (method to test) + libcmis::ObjectPtr updated = object->updateProperties( newProperties ); + + // Check that the proper request has been send + // In order to avoid to check changing strings (containing a timestamp), + // get the cmisra:object tree and compare it. + string request( curl_mockup_getRequestBody( "http://mockup/mock/id", "id=test-document", "PUT" ) ); + string actualObject = test::getXmlNodeAsString( request, "/atom:entry/cmisra:object" ); + + string expectedObject = "<cmisra:object>" + "<cmis:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>New name</cmis:value>" + "</cmis:propertyString>" + "</cmis:properties>" + "</cmisra:object>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request content sent", expectedObject, actualObject ); + + // Check that the properties are updated after the call + PropertyPtrMap::iterator propIt = updated->getProperties( ).find( propertyName ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong value after refresh", expectedValue, propIt->second->getStrings().front( ) ); +} + +void AtomTest::updatePropertiesEmptyTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + // Values for the test + libcmis::ObjectPtr object = session->getObject( "test-document" ); + + // Just leave the map empty and update + PropertyPtrMap emptyProperties; + libcmis::ObjectPtr updated = object->updateProperties( emptyProperties ); + + // Check that no HTTP request was sent + int count = curl_mockup_getRequestsCount( "http://mockup/mock/id", + "id=test-document", "PUT" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "No HTTP request should have been sent", 0, count ); + + // Check that the object we got is the same than previous one + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong change token", object->getChangeToken(), updated->getChangeToken() ); +} + +void AtomTest::createFolderTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/children", "id=root-folder", "POST", DATA_DIR "/atom/create-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=root-folder", "GET", DATA_DIR "/atom/root-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::FolderPtr parent = session->getRootFolder( ); + + // Prepare the properties for the new object, object type is cmis:folder + PropertyPtrMap props; + libcmis::ObjectTypePtr type = session->getType( "cmis:folder" ); + map< string, libcmis::PropertyTypePtr > propTypes = type->getPropertiesTypes( ); + + // Set the object name + string expectedName( "create folder" ); + map< string, libcmis::PropertyTypePtr >::iterator it = propTypes.find( string( "cmis:name" ) ); + vector< string > nameValues; + nameValues.push_back( expectedName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( it->second, nameValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // set the object type + it = propTypes.find( string( "cmis:objectTypeId" ) ); + vector< string > typeValues; + typeValues.push_back( "cmis:folder" ); + libcmis::PropertyPtr typeProperty( new libcmis::Property( it->second, typeValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeProperty ) ); + + // Actually send the folder creation request + libcmis::FolderPtr created = parent->createFolder( props ); + + // Check that something came back + CPPUNIT_ASSERT_MESSAGE( "Change token shouldn't be empty: object should have been refreshed", + !created->getChangeToken( ).empty() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong name", expectedName, created->getName( ) ); + + // Check that the proper request has been sent + string request( curl_mockup_getRequestBody( "http://mockup/mock/children", "id=root-folder", "POST" ) ); + string actualObject = test::getXmlNodeAsString( request, "/atom:entry/cmisra:object" ); + + string expectedObject = "<cmisra:object>" + "<cmis:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>create folder</cmis:value>" + "</cmis:propertyString>" + "<cmis:propertyId propertyDefinitionId=\"cmis:objectTypeId\" localName=\"cmis:objectTypeId\"" + " displayName=\"Type-Id\" queryName=\"cmis:objectTypeId\">" + "<cmis:value>cmis:folder</cmis:value>" + "</cmis:propertyId>" + "</cmis:properties>" + "</cmisra:object>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request content sent", expectedObject, actualObject ); +} + +void AtomTest::createFolderBadTypeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/children", "id=root-folder", "POST", + "Not a cmis:folder derived type", 409, false ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=root-folder", "GET", DATA_DIR "/atom/root-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:document", "GET", DATA_DIR "/atom/type-document.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::FolderPtr parent = session->getRootFolder( ); + + // Prepare the properties for the new object, object type is cmis:folder + PropertyPtrMap props; + libcmis::ObjectTypePtr type = session->getType( "cmis:folder" ); + map< string, libcmis::PropertyTypePtr > propTypes = type->getPropertiesTypes( ); + + // Set the object name + string expectedName( "create folder" ); + map< string, libcmis::PropertyTypePtr >::iterator it = propTypes.find( string( "cmis:name" ) ); + vector< string > nameValues; + nameValues.push_back( expectedName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( it->second, nameValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // set the object type + it = propTypes.find( string( "cmis:objectTypeId" ) ); + vector< string > typeValues; + typeValues.push_back( "cmis:document" ); + libcmis::PropertyPtr typeProperty( new libcmis::Property( it->second, typeValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeProperty ) ); + + // Actually send the folder creation request + try + { + parent->createFolder( props ); + CPPUNIT_FAIL( "Should not succeed to return a folder" ); + } + catch ( libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong error type", string( "constraint" ), e.getType() ); + } + + // Check that the proper request has been sent + string request( curl_mockup_getRequestBody( "http://mockup/mock/children", "id=root-folder", "POST" ) ); + string actualObject = test::getXmlNodeAsString( request, "/atom:entry/cmisra:object" ); + + string expectedObject = "<cmisra:object>" + "<cmis:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>create folder</cmis:value>" + "</cmis:propertyString>" + "<cmis:propertyId propertyDefinitionId=\"cmis:objectTypeId\" localName=\"cmis:objectTypeId\"" + " displayName=\"Type-Id\" queryName=\"cmis:objectTypeId\">" + "<cmis:value>cmis:document</cmis:value>" + "</cmis:propertyId>" + "</cmis:properties>" + "</cmisra:object>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request content sent", expectedObject, actualObject ); +} + +void AtomTest::createDocumentTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/children", "id=root-folder", "POST", "Response body up to server", 201, false, + "Location: http://mockup/mock/id?id=create-document\r\n" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=create-document", "GET", DATA_DIR "/atom/create-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=root-folder", "GET", DATA_DIR "/atom/root-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:document", "GET", DATA_DIR "/atom/type-document.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::FolderPtr parent = session->getRootFolder( ); + + // Prepare the properties for the new object, object type is cmis:folder + PropertyPtrMap props; + libcmis::ObjectTypePtr type = session->getType( "cmis:document" ); + map< string, libcmis::PropertyTypePtr > propTypes = type->getPropertiesTypes( ); + + // Set the object name + string expectedName( "create document" ); + + map< string, libcmis::PropertyTypePtr >::iterator it = propTypes.find( string( "cmis:name" ) ); + vector< string > nameValues; + nameValues.push_back( expectedName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( it->second, nameValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // set the object type + it = propTypes.find( string( "cmis:objectTypeId" ) ); + vector< string > typeValues; + typeValues.push_back( "cmis:document" ); + libcmis::PropertyPtr typeProperty( new libcmis::Property( it->second, typeValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeProperty ) ); + + // Actually send the document creation request + string content = "Some content"; + boost::shared_ptr< ostream > os ( new stringstream( content ) ); + string contentType = "text/plain"; + string filename( "name.txt" ); + libcmis::DocumentPtr created = parent->createDocument( props, os, contentType, filename ); + + // Check that something came back + CPPUNIT_ASSERT_MESSAGE( "Change token shouldn't be empty: object should have been refreshed", + !created->getChangeToken( ).empty() ); + + // Check that the name is ok + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong name set", expectedName, created->getName( ) ); + + // Check that the request is the expected one + string request( curl_mockup_getRequestBody( "http://mockup/mock/children", "id=root-folder", "POST" ) ); + string actualContent = test::getXmlNodeAsString( request, "/atom:entry/cmisra:content" ); + string expectedContent = "<cmisra:content>" + "<cmisra:mediatype>text/plain</cmisra:mediatype>" + "<cmisra:base64>U29tZSBjb250ZW50</cmisra:base64>" + "</cmisra:content>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request content sent", expectedContent, actualContent ); + + string actualObject = test::getXmlNodeAsString( request, "/atom:entry/cmisra:object" ); + string expectedObject = "<cmisra:object>" + "<cmis:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>create document</cmis:value>" + "</cmis:propertyString>" + "<cmis:propertyId propertyDefinitionId=\"cmis:objectTypeId\" localName=\"cmis:objectTypeId\"" + " displayName=\"Type-Id\" queryName=\"cmis:objectTypeId\">" + "<cmis:value>cmis:document</cmis:value>" + "</cmis:propertyId>" + "</cmis:properties>" + "</cmisra:object>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request object sent", expectedObject, actualObject ); +} + +void AtomTest::deleteDocumentTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "DELETE", "", 204, false ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr object = session->getObject( "test-document" ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get() ); + + document->remove( ); + + // Test the sent request + const char* request = curl_mockup_getRequestBody( "http://mockup/mock/id", "id=test-document", "DELETE" ); + CPPUNIT_ASSERT_MESSAGE( "DELETE request not sent", request ); +} + +void AtomTest::deleteFolderTreeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=valid-object", "GET", DATA_DIR "/atom/valid-object.xml" ); + curl_mockup_addResponse( "http://mockup/mock/descendants", "id=valid-object", "DELETE", "", 204, false ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr object = session->getObject( "valid-object" ); + libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( object.get() ); + + folder->removeTree( ); + + // Test the sent request + const struct HttpRequest* request = curl_mockup_getRequest( "http://mockup/mock/descendants", "id=valid-object", "DELETE" ); + CPPUNIT_ASSERT_MESSAGE( "DELETE request not sent", request ); + curl_mockup_HttpRequest_free( request ); +} + +void AtomTest::checkOutTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/checkedout", "", "POST", DATA_DIR "/atom/working-copy.xml", 201 ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr object = session->getObject( "test-document" ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get() ); + + libcmis::DocumentPtr pwc = document->checkOut( ); + + CPPUNIT_ASSERT_MESSAGE( "Missing returned Private Working Copy", pwc.get( ) != NULL ); + + PropertyPtrMap::iterator it = pwc->getProperties( ).find( string( "cmis:isVersionSeriesCheckedOut" ) ); + vector< bool > values = it->second->getBools( ); + CPPUNIT_ASSERT_MESSAGE( "cmis:isVersionSeriesCheckedOut isn't true", values.front( ) ); +} + +void AtomTest::cancelCheckOutTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=working-copy", "GET", DATA_DIR "/atom/working-copy.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=working-copy", "DELETE", "", 204, false ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + // First get a checked out document + libcmis::ObjectPtr object = session->getObject( "working-copy" ); + libcmis::DocumentPtr pwc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + pwc->cancelCheckout( ); + + // Check that the DELETE request was sent out + const struct HttpRequest* request = curl_mockup_getRequest( "http://mockup/mock/id", "id=working-copy", "DELETE" ); + CPPUNIT_ASSERT_MESSAGE( "DELETE request not sent", request ); + curl_mockup_HttpRequest_free( request ); +} + +void AtomTest::checkInTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=working-copy", "GET", DATA_DIR "/atom/working-copy.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=working-copy", "PUT", DATA_DIR "/atom/test-document.xml", 200, true, + "Location: http://mockup/mock/id?id=valid-object" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + // First get a checked out document + libcmis::ObjectPtr object = session->getObject( "working-copy" ); + libcmis::DocumentPtr pwc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + // Do the checkin + bool isMajor = true; + string comment( "Some check-in comment" ); + PropertyPtrMap properties; + string newContent = "Some New content to check in"; + boost::shared_ptr< ostream > stream ( new stringstream( newContent ) ); + pwc->checkIn( isMajor, comment, properties, stream, "text/plain", "filename.txt" ); + + // Make sure that the expected request has been sent + const struct HttpRequest* request = curl_mockup_getRequest( "http://mockup/mock/id", "id=working-copy", "PUT" ); + CPPUNIT_ASSERT_MESSAGE( "PUT request not sent", request ); + + string actualContent = test::getXmlNodeAsString( request->body, "/atom:entry/cmisra:content" ); + string expectedContent = "<cmisra:content><cmisra:mediatype>text/plain</cmisra:mediatype><cmisra:base64>U29tZSBOZXcgY29udGVudCB0byBjaGVjayBpbg==</cmisra:base64></cmisra:content>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong content sent", expectedContent, actualContent ); + + // Still needs to test that checkin request parameters were OK + string url( request->url ); + CPPUNIT_ASSERT_MESSAGE( "Sent checkin request has wrong major parameter", url.find("major=true") != string::npos ); + CPPUNIT_ASSERT_MESSAGE( "Sent checkin request has wrong checkinComment parameter", url.find( "checkinComment=" + comment ) != string::npos ); + CPPUNIT_ASSERT_MESSAGE( "Sent checkin request has no checkin parameter", url.find("checkin=true") != string::npos ); + + curl_mockup_HttpRequest_free( request ); +} + +void AtomTest::getAllVersionsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/versions", "id=test-document", "GET", DATA_DIR "/atom/get-versions.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + // First get a document + libcmis::ObjectPtr object = session->getObject( "test-document" ); + libcmis::DocumentPtr doc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + // Get all the versions (method to check) + vector< libcmis::DocumentPtr > versions = doc->getAllVersions( ); + + // Checks + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of versions", size_t( 2 ), versions.size( ) ); +} + +void AtomTest::moveTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=DocumentLevel2", "GET", DATA_DIR "/atom/type-docLevel2.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=test-document", "GET", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_addResponse( "http://mockup/mock/parents", "id=test-document", "GET", DATA_DIR "/atom/test-document-parents.xml" ); + curl_mockup_addResponse( "http://mockup/mock/id", "id=valid-object", "GET", DATA_DIR "/atom/valid-object.xml" ); + curl_mockup_addResponse( "http://mockup/mock/type", "id=cmis:folder", "GET", DATA_DIR "/atom/type-folder.xml" ); + curl_mockup_addResponse( "http://mockup/mock/children", "id=valid-object", "POST", DATA_DIR "/atom/test-document.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + AtomPubSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD ); + + libcmis::ObjectPtr object = session->getObject( "test-document" ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get() ); + + string destFolderId = "valid-object"; + libcmis::FolderPtr src = document->getParents( ).front( ); + libcmis::FolderPtr dest = session->getFolder( destFolderId ); + + document->move( src, dest ); + + // Check that the sent request has the expected params + const struct HttpRequest* request = curl_mockup_getRequest( "http://mockup/mock/children", "id=valid-object", "POST" ); + string url( request->url ); + CPPUNIT_ASSERT_MESSAGE( "Sent move request has wrong or missing sourceFolderId", url.find("sourceFolderId=parent1") != string::npos ); + + // Check that we had the atom entry in the request body + string actualObject = test::getXmlNodeAsString( request->body, "/atom:entry/cmisra:object/cmis:properties/cmis:propertyId[@propertyDefinitionId='cmis:objectId']" ); + string expectedObject = "<cmis:propertyId propertyDefinitionId=\"cmis:objectId\" localName=\"cmis:objectId\"" + " displayName=\"Object Id\" queryName=\"cmis:objectId\">" + "<cmis:value>test-document</cmis:value>" + "</cmis:propertyId>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request object sent", expectedObject, actualObject ); + + curl_mockup_HttpRequest_free( request ); +} + +AtomPubSessionPtr AtomTest::getTestSession( string username, string password ) +{ + AtomPubSessionPtr session( new AtomPubSession( ) ); + string buf; + test::loadFromFile( DATA_DIR "/atom/workspaces.xml", buf ); + session->parseServiceDocument( buf ); + + session->m_username = username; + session->m_password = password; + + return session; +} diff --git a/qa/libcmis/test-commons.cxx b/qa/libcmis/test-commons.cxx new file mode 100644 index 0000000..df271e9 --- /dev/null +++ b/qa/libcmis/test-commons.cxx @@ -0,0 +1,223 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 SUSE <cedric@bosdonnat.fr> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <time.h> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> +#include <cppunit/ui/text/TestRunner.h> +#include <libxml/tree.h> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <libcmis/oauth2-data.hxx> +#include <libcmis/object-type.hxx> + +#include "oauth2-handler.hxx" + +using namespace libcmis; +using namespace std; + +class CommonsTest : public CppUnit::TestFixture +{ + public: + + // constructors tests + void oauth2DataCopyTest(); + void oauth2HandlerCopyTest(); + void objectTypeCopyTest(); + + // Methods that should never be called + void objectTypeNocallTest(); + + CPPUNIT_TEST_SUITE( CommonsTest ); + CPPUNIT_TEST( oauth2DataCopyTest ); + CPPUNIT_TEST( oauth2HandlerCopyTest ); + CPPUNIT_TEST( objectTypeCopyTest ); + CPPUNIT_TEST( objectTypeNocallTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +static void assertOAuth2DataEquals( const OAuth2Data& expected, const OAuth2Data& actual ) +{ + CPPUNIT_ASSERT_EQUAL( expected.m_authUrl, actual.m_authUrl ); + CPPUNIT_ASSERT_EQUAL( expected.m_tokenUrl, actual.m_tokenUrl ); + CPPUNIT_ASSERT_EQUAL( expected.m_scope, actual.m_scope ); + CPPUNIT_ASSERT_EQUAL( expected.m_redirectUri, actual.m_redirectUri ); + CPPUNIT_ASSERT_EQUAL( expected.m_clientId, actual.m_clientId ); + CPPUNIT_ASSERT_EQUAL( expected.m_clientSecret, actual.m_clientSecret ); +} + +void CommonsTest::oauth2DataCopyTest( ) +{ + OAuth2Data data( "url", "token", "scope", "redirect", + "clientid", "clientsecret" ); + { + OAuth2Data copy; + copy = data; + assertOAuth2DataEquals( data, copy ); + } + + { + OAuth2Data copy( data ); + assertOAuth2DataEquals( data, copy ); + } +} + +string DummyOAuth2Parser( HttpSession*, const string&, const string&, const string& ) +{ + return "Fake"; +} + +void CommonsTest::oauth2HandlerCopyTest( ) +{ + OAuth2DataPtr data( new OAuth2Data ( "url", "token", "scope", "redirect", + "clientid", "clientsecret" ) ); + HttpSession session( "user", "pass" ); + OAuth2Handler handler( &session, data ); + handler.m_access = "access"; + handler.m_refresh = "refresh"; + handler.m_oauth2Parser = &DummyOAuth2Parser; + + { + OAuth2Handler copy; + copy = handler; + + CPPUNIT_ASSERT_EQUAL( &session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL( data, copy.m_data ); + CPPUNIT_ASSERT_EQUAL( handler.m_access, copy.m_access ); + CPPUNIT_ASSERT_EQUAL( handler.m_refresh, copy.m_refresh ); + CPPUNIT_ASSERT_EQUAL( &DummyOAuth2Parser, copy.m_oauth2Parser ); + } + + { + OAuth2Handler copy( handler ); + + CPPUNIT_ASSERT_EQUAL( &session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL( data, copy.m_data ); + CPPUNIT_ASSERT_EQUAL( handler.m_access, copy.m_access ); + CPPUNIT_ASSERT_EQUAL( handler.m_refresh, copy.m_refresh ); + CPPUNIT_ASSERT_EQUAL( &DummyOAuth2Parser, copy.m_oauth2Parser ); + } +} + +static void assertObjectTypeEquals( const ObjectType& expected, const ObjectType& actual ) +{ + CPPUNIT_ASSERT_EQUAL( expected.getRefreshTimestamp(), actual.getRefreshTimestamp() ); + CPPUNIT_ASSERT_EQUAL( expected.getId(), actual.getId() ); + CPPUNIT_ASSERT_EQUAL( expected.getLocalName(), actual.getLocalName() ); + CPPUNIT_ASSERT_EQUAL( expected.getLocalNamespace(), actual.getLocalNamespace() ); + CPPUNIT_ASSERT_EQUAL( expected.getDisplayName(), actual.getDisplayName() ); + CPPUNIT_ASSERT_EQUAL( expected.getQueryName(), actual.getQueryName() ); + CPPUNIT_ASSERT_EQUAL( expected.getDescription(), actual.getDescription() ); + CPPUNIT_ASSERT_EQUAL( expected.getParentTypeId(), actual.getParentTypeId() ); + CPPUNIT_ASSERT_EQUAL( expected.getBaseTypeId(), actual.getBaseTypeId() ); + CPPUNIT_ASSERT_EQUAL( expected.isCreatable(), actual.isCreatable() ); + CPPUNIT_ASSERT_EQUAL( expected.isFileable(), actual.isFileable() ); + CPPUNIT_ASSERT_EQUAL( expected.isQueryable(), actual.isQueryable() ); + CPPUNIT_ASSERT_EQUAL( expected.isFulltextIndexed(), actual.isFulltextIndexed() ); + CPPUNIT_ASSERT_EQUAL( expected.isIncludedInSupertypeQuery(), actual.isIncludedInSupertypeQuery() ); + CPPUNIT_ASSERT_EQUAL( expected.isControllablePolicy(), actual.isControllablePolicy() ); + CPPUNIT_ASSERT_EQUAL( expected.isControllableACL(), actual.isControllableACL() ); + CPPUNIT_ASSERT_EQUAL( expected.isVersionable(), actual.isVersionable() ); + CPPUNIT_ASSERT_EQUAL( expected.getContentStreamAllowed(), actual.getContentStreamAllowed() ); + CPPUNIT_ASSERT_EQUAL( const_cast< ObjectType& >( expected ).getPropertiesTypes().size(), + const_cast< ObjectType& >( actual ).getPropertiesTypes().size() ); +} + +void CommonsTest::objectTypeCopyTest( ) +{ + ObjectType type; + time_t refresh = time( NULL ); + type.m_refreshTimestamp = refresh; + type.m_id = "id"; + type.m_baseTypeId = "base"; + + { + ObjectType copy; + copy = type; + assertObjectTypeEquals( type, copy ); + } + + { + ObjectType copy ( type ); + assertObjectTypeEquals( type, copy ); + } +} + +void CommonsTest::objectTypeNocallTest( ) +{ + ObjectType type; + + try + { + type.refresh(); + CPPUNIT_FAIL( "refresh() shouldn't succeed" ); + } + catch ( const Exception& e ) + { + } + + try + { + type.getParentType(); + CPPUNIT_FAIL( "getParentType() shouldn't succeed" ); + } + catch ( const Exception& e ) + { + } + + try + { + type.getBaseType(); + CPPUNIT_FAIL( "getBaseType() shouldn't succeed" ); + } + catch ( const Exception& e ) + { + } + + try + { + type.getChildren(); + CPPUNIT_FAIL( "getChildren() shouldn't succeed" ); + } + catch ( const Exception& e ) + { + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION( CommonsTest ); diff --git a/qa/libcmis/test-decoder.cxx b/qa/libcmis/test-decoder.cxx new file mode 100644 index 0000000..c7b4748 --- /dev/null +++ b/qa/libcmis/test-decoder.cxx @@ -0,0 +1,221 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> +#include <cppunit/ui/text/TestRunner.h> + +#include <libcmis/xml-utils.hxx> + +#define BASE64_ENCODING string( "base64" ) + +using namespace std; + +class DecoderTest : public CppUnit::TestFixture +{ + private: + libcmis::EncodedData* data; + FILE* stream; + + string getActual( ); + + public: + + DecoderTest( ); + DecoderTest( const DecoderTest& rCopy ); + + DecoderTest& operator=( const DecoderTest& rCopy ); + + void setUp( ); + void tearDown( ); + + void noEncodingTest(); + + void base64DecodeSimpleBlockTest( ); + void base64DecodePaddedBlockTest( ); + void base64DecodeNoEqualsPaddedBlockTest( ); + void base64DecodeSplitRunsTest( ); + + void base64EncodeSimpleBlockTest( ); + void base64EncodePaddedBlockTest( ); + void base64EncodeSplitRunsTest( ); + + void base64encodeTest( ); + + CPPUNIT_TEST_SUITE( DecoderTest ); + CPPUNIT_TEST( noEncodingTest ); + CPPUNIT_TEST( base64DecodeSimpleBlockTest ); + CPPUNIT_TEST( base64DecodePaddedBlockTest ); + CPPUNIT_TEST( base64DecodeNoEqualsPaddedBlockTest ); + CPPUNIT_TEST( base64DecodeSplitRunsTest ); + CPPUNIT_TEST( base64EncodeSimpleBlockTest ); + CPPUNIT_TEST( base64EncodePaddedBlockTest ); + CPPUNIT_TEST( base64EncodeSplitRunsTest ); + CPPUNIT_TEST( base64encodeTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +DecoderTest::DecoderTest( ) : + CppUnit::TestFixture( ), + data( NULL ), + stream( NULL ) +{ +} + +DecoderTest::DecoderTest( const DecoderTest& rCopy ) : + CppUnit::TestFixture( ), + data( rCopy.data ), + stream( rCopy.stream ) +{ +} + +DecoderTest& DecoderTest::operator=( const DecoderTest& rCopy ) +{ + if ( this != &rCopy ) + { + data = rCopy.data; + stream = rCopy.stream; + } + return *this; +} + +void DecoderTest::setUp( ) +{ + stream = tmpfile(); + data = new libcmis::EncodedData( stream ); +} + +void DecoderTest::tearDown( ) +{ + fclose( stream ); + delete data; +} + +string DecoderTest::getActual( ) +{ + string actual; + + rewind( stream ); + size_t bufSize = 100; + char buf[100]; + size_t readBytes = 0; + do { + readBytes = fread( buf, 1, bufSize, stream ); + actual += string( buf, readBytes ); + } while ( readBytes == bufSize ); + + return actual; +} + +void DecoderTest::noEncodingTest() +{ + data->decode( ( void* )"pleasure.", 1, 9 ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "pleasure." ), getActual( ) ); +} + +/* + * All the test values for the Base64 have been taken from + * the wikipedia article: http://en.wikipedia.org/wiki/Base64 + */ + +void DecoderTest::base64DecodeSimpleBlockTest( ) +{ + data->setEncoding( BASE64_ENCODING ); + string input( "cGxlYXN1cmUu" ); + data->decode( ( void* )input.c_str( ), 1, input.size() ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "pleasure." ), getActual( ) ); +} + +void DecoderTest::base64DecodePaddedBlockTest( ) +{ + data->setEncoding( BASE64_ENCODING ); + string input( "c3VyZS4=" ); + data->decode( ( void* )input.c_str( ), 1, input.size( ) ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "sure." ), getActual( ) ); +} + +void DecoderTest::base64DecodeNoEqualsPaddedBlockTest( ) +{ + data->setEncoding( BASE64_ENCODING ); + string input( "c3VyZS4" ); + data->decode( ( void* )input.c_str( ), 1, input.size( ) ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "sure." ), getActual( ) ); +} + +void DecoderTest::base64DecodeSplitRunsTest( ) +{ + data->setEncoding( BASE64_ENCODING ); + string input1( "cGxlYXN1c" ); + data->decode( ( void* )input1.c_str( ), 1, input1.size( ) ); + string input2( "mUu" ); + data->decode( ( void* )input2.c_str( ), 1, input2.size( ) ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "pleasure." ), getActual( ) ); +} + +void DecoderTest::base64EncodeSimpleBlockTest( ) +{ + data->setEncoding( BASE64_ENCODING ); + string input( "pleasure." ); + data->encode( ( void* )input.c_str(), 1, input.size() ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "cGxlYXN1cmUu" ), getActual( ) ); +} + +void DecoderTest::base64EncodePaddedBlockTest( ) +{ + data->setEncoding( BASE64_ENCODING ); + string input( "sure." ); + data->encode( ( void* )input.c_str(), 1, input.size() ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "c3VyZS4=" ), getActual( ) ); +} + +void DecoderTest::base64EncodeSplitRunsTest( ) +{ + data->setEncoding( BASE64_ENCODING ); + string input1( "plea" ); + data->encode( ( void* )input1.c_str(), 1, input1.size() ); + string input2( "sure." ); + data->encode( ( void* )input2.c_str(), 1, input2.size() ); + data->finish( ); + CPPUNIT_ASSERT_EQUAL( string( "cGxlYXN1cmUu" ), getActual( ) ); +} + +void DecoderTest::base64encodeTest( ) +{ + string actual = libcmis::base64encode( "sure." ); + CPPUNIT_ASSERT_EQUAL( string( "c3VyZS4=" ), actual ); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( DecoderTest ); diff --git a/qa/libcmis/test-factory.cxx b/qa/libcmis/test-factory.cxx new file mode 100644 index 0000000..74a6b02 --- /dev/null +++ b/qa/libcmis/test-factory.cxx @@ -0,0 +1,336 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <memory> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <libcmis/session-factory.hxx> + +#include <atom-session.hxx> +#include <ws-session.hxx> +#include <gdrive-session.hxx> +#include <onedrive-session.hxx> +#include <sharepoint-session.hxx> + +#include <mockup-config.h> +#include <test-helpers.hxx> +#include <test-mockup-helpers.hxx> + +#define BINDING_ATOM string( "http://mockup/atom" ) +#define BINDING_WS string( "http://mockup/ws" ) +#define BINDING_SHAREPOINT string ( "http://mockup/sharepoint/_api/web" ) +#define CONTEXTINFO_URL string ( "http://mockup/sharepoint/_api/contextinfo" ) +#define BINDING_BAD "http://mockup/bad" +#define BINDING_GDRIVE string ( "https://www.googleapis.com/drive/v3" ) +#define BINDING_ONEDRIVE string ( "https://graph.microsoft.com/v1.0" ) +#define SERVER_REPOSITORY string( "mock" ) +#define SERVER_USERNAME "tester" +#define SERVER_PASSWORD "somepass" + +#define OAUTH_CLIENT_ID string ( "mock-id" ) +#define OAUTH_CLIENT_SECRET string ( "mock-secret" ) +#define OAUTH_SCOPE string ( "https://scope/url" ) +#define OAUTH_REDIRECT_URI string ("redirect:uri" ) + +#define GDRIVE_AUTH_URL string ( "https://auth/url" ) +#define GDRIVE_LOGIN_URL string ("https://login/url" ) +#define GDRIVE_LOGIN_URL2 string ("https://login2/url" ) +#define GDRIVE_APPROVAL_URL string ("https://approval/url" ) +#define GDRIVE_TOKEN_URL string ( "https://token/url" ) + +#define ONEDRIVE_AUTH_URL string ( "https://auth/url" ) +#define ONEDRIVE_TOKEN_URL string ( "https://token/url" ) + +using namespace std; + +namespace +{ + void lcl_init_mockup_ws( ) + { + curl_mockup_reset( ); + curl_mockup_addResponse( BINDING_WS.c_str( ), "", "GET", + DATA_DIR "/ws/CMISWS-Service.wsdl" ); + test::addWsResponse( string( BINDING_WS + "/services/RepositoryService" ).c_str(), + DATA_DIR "/ws/repositories.http" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + } + + void lcl_init_mockup_atom( ) + { + curl_mockup_reset( ); + curl_mockup_addResponse( BINDING_ATOM.c_str( ), "", "GET", + DATA_DIR "/atom/workspaces.xml" ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + } + + void lcl_init_mockup_gdrive( ) + { + curl_mockup_reset( ); + + //login response + string loginIdentifier = string("scope=") + OAUTH_SCOPE + + string("&redirect_uri=") + OAUTH_REDIRECT_URI + + string("&response_type=code") + + string("&client_id=") + OAUTH_CLIENT_ID; + + curl_mockup_addResponse ( GDRIVE_AUTH_URL.c_str(), loginIdentifier.c_str( ), + "GET", DATA_DIR "/gdrive/login1.html", 200, true); + + //authentication email + curl_mockup_addResponse( GDRIVE_LOGIN_URL2.c_str( ), "", "POST", + DATA_DIR "/gdrive/login2.html", 200, true); + + //authentication password, + curl_mockup_addResponse( GDRIVE_LOGIN_URL.c_str( ), "", "POST", + DATA_DIR "/gdrive/approve.html", 200, true); + + //approval response + curl_mockup_addResponse( GDRIVE_APPROVAL_URL.c_str( ), "", + "POST", DATA_DIR "/gdrive/authcode.html", 200, true); + + curl_mockup_addResponse ( GDRIVE_TOKEN_URL.c_str( ), "", "POST", + DATA_DIR "/gdrive/token-response.json", 200, true ); + } + + char* authCodeFallback( const char* /*url*/, const char* /*username*/, const char* /*password*/ ) + { + char *authCode = strdup( "authCode" ); + return authCode; + } + + void lcl_init_mockup_sharepoint( ) + { + curl_mockup_reset( ); + + curl_mockup_addResponse( BINDING_SHAREPOINT.c_str( ), "", "GET", "", 401, false ); + curl_mockup_addResponse( ( BINDING_SHAREPOINT + "/currentuser" ).c_str( ), "", "GET", + DATA_DIR "/sharepoint/auth-resp.json", 200, true ); + curl_mockup_addResponse( CONTEXTINFO_URL.c_str( ), "", "POST", + DATA_DIR "/sharepoint/xdigest.json", 200, true ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + } +} + +class FactoryTest : public CppUnit::TestFixture +{ + public: + + void createSessionAtomTest( ); + void createSessionAtomBadAuthTest( ); + void createSessionWSTest( ); + void createSessionWSBadAuthTest( ); + void createSessionNoCmisTest( ); + void createSessionGDriveTest( ); + void createSessionOneDriveTest( ); + void createSessionSharePointTest( ); + void createSessionSharePointDefaultAuthTest( ); + void createSessionSharePointBadAuthTest( ); + + CPPUNIT_TEST_SUITE( FactoryTest ); + CPPUNIT_TEST( createSessionAtomTest ); + CPPUNIT_TEST( createSessionAtomBadAuthTest ); + CPPUNIT_TEST( createSessionWSTest ); + CPPUNIT_TEST( createSessionWSBadAuthTest ); + CPPUNIT_TEST( createSessionNoCmisTest ); + CPPUNIT_TEST( createSessionGDriveTest ); + CPPUNIT_TEST( createSessionOneDriveTest ); + CPPUNIT_TEST( createSessionSharePointTest ); + CPPUNIT_TEST( createSessionSharePointDefaultAuthTest ); + CPPUNIT_TEST( createSessionSharePointBadAuthTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( FactoryTest ); + +void FactoryTest::createSessionAtomTest( ) +{ + lcl_init_mockup_atom( ); + + unique_ptr< libcmis::Session > session( libcmis::SessionFactory::createSession( + BINDING_ATOM, SERVER_USERNAME, SERVER_PASSWORD, + SERVER_REPOSITORY ) ); + CPPUNIT_ASSERT_MESSAGE( "Not an AtomPubSession", + dynamic_cast< AtomPubSession* >( session.get() ) != NULL ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "More than one request for the binding", + int( 1 ), curl_mockup_getRequestsCount( BINDING_ATOM.c_str( ), "", "GET" ) ); +} + +void FactoryTest::createSessionAtomBadAuthTest( ) +{ + lcl_init_mockup_atom( ); + + try + { + libcmis::SessionFactory::createSession( + BINDING_ATOM, "Bad user", "Bad Password", + SERVER_REPOSITORY ); + CPPUNIT_FAIL( "Should throw exception" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception type", + string( "permissionDenied" ), e.getType( ) ); + } +} + +void FactoryTest::createSessionWSTest( ) +{ + lcl_init_mockup_ws( ); + + unique_ptr< libcmis::Session > session( libcmis::SessionFactory::createSession( + BINDING_WS, SERVER_USERNAME, SERVER_PASSWORD, + SERVER_REPOSITORY ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a WSSession", + dynamic_cast< WSSession* >( session.get() ) != NULL ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "More than one request for the binding", + int( 1 ), curl_mockup_getRequestsCount( BINDING_WS.c_str( ), "", "GET" ) ); +} + +void FactoryTest::createSessionWSBadAuthTest( ) +{ + lcl_init_mockup_ws( ); + + try + { + libcmis::SessionFactory::createSession( + BINDING_WS, "Bad User", "Bad Pass", + SERVER_REPOSITORY ); + CPPUNIT_FAIL( "Should throw exception" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception type", + string( "permissionDenied" ), e.getType( ) ); + } +} + +void FactoryTest::createSessionGDriveTest( ) +{ + lcl_init_mockup_gdrive( ); + + libcmis::OAuth2DataPtr oauth2Data( + new libcmis::OAuth2Data( GDRIVE_AUTH_URL, GDRIVE_TOKEN_URL, + OAUTH_SCOPE, OAUTH_REDIRECT_URI, + OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET )); + + unique_ptr< libcmis::Session > session( libcmis::SessionFactory::createSession( + BINDING_GDRIVE, SERVER_USERNAME, SERVER_PASSWORD, + SERVER_REPOSITORY, false, + oauth2Data ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a GDriveSession", + dynamic_cast< GDriveSession* >( session.get() ) != NULL ); +} + +void FactoryTest::createSessionOneDriveTest( ) +{ + lcl_init_mockup_gdrive( ); + + libcmis::OAuth2DataPtr oauth2Data( + new libcmis::OAuth2Data( ONEDRIVE_AUTH_URL, ONEDRIVE_TOKEN_URL, + OAUTH_SCOPE, OAUTH_REDIRECT_URI, + OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET )); + + libcmis::SessionFactory::setOAuth2AuthCodeProvider( authCodeFallback ); + unique_ptr< libcmis::Session > session( libcmis::SessionFactory::createSession( + BINDING_ONEDRIVE, SERVER_USERNAME, SERVER_PASSWORD, + SERVER_REPOSITORY, false, + oauth2Data ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a OneDriveSession", + dynamic_cast< OneDriveSession* >( session.get() ) != NULL ); +} + +void FactoryTest::createSessionSharePointTest( ) +{ + lcl_init_mockup_sharepoint( ); + + unique_ptr< libcmis::Session > session( libcmis::SessionFactory::createSession( + BINDING_SHAREPOINT, SERVER_USERNAME, SERVER_PASSWORD, + SERVER_REPOSITORY ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a SharePoint Session", + dynamic_cast< SharePointSession* >( session.get() ) != NULL ); +} + +void FactoryTest::createSessionSharePointDefaultAuthTest( ) +{ + curl_mockup_addResponse( BINDING_SHAREPOINT.c_str( ), "", "GET", + DATA_DIR "/sharepoint/auth-xml-resp.xml", 200, true ); + curl_mockup_addResponse( CONTEXTINFO_URL.c_str( ), "", "POST", + DATA_DIR "/sharepoint/xdigest.json", 200, true ); + + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + unique_ptr< libcmis::Session > session( libcmis::SessionFactory::createSession( + BINDING_SHAREPOINT, SERVER_USERNAME, SERVER_PASSWORD, + SERVER_REPOSITORY ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a SharePoint Session", + dynamic_cast< SharePointSession* >( session.get() ) != NULL ); +} + +void FactoryTest::createSessionSharePointBadAuthTest( ) +{ + lcl_init_mockup_sharepoint( ); + + try + { + libcmis::SessionFactory::createSession( + BINDING_ATOM, "Bad user", "Bad Password", + SERVER_REPOSITORY ); + CPPUNIT_FAIL( "Should throw exception" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception type", + string( "permissionDenied" ), e.getType( ) ); + } +} + +void FactoryTest::createSessionNoCmisTest( ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( BINDING_BAD, "", "GET", + "<p>Some non CMIS content</p>", 200, false ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + unique_ptr< libcmis::Session > session( libcmis::SessionFactory::createSession( + BINDING_BAD, SERVER_USERNAME, SERVER_PASSWORD, + SERVER_REPOSITORY ) ); + CPPUNIT_ASSERT_MESSAGE( "Session should be NULL", !session ); +} diff --git a/qa/libcmis/test-gdrive.cxx b/qa/libcmis/test-gdrive.cxx new file mode 100644 index 0000000..c89017a --- /dev/null +++ b/qa/libcmis/test-gdrive.cxx @@ -0,0 +1,1318 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <memory> +#include <string> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <libcmis/document.hxx> +#include <libcmis/session-factory.hxx> + +#include <mockup-config.h> + +#include "gdrive-session.hxx" +#include "gdrive-property.hxx" +#include "oauth2-handler.hxx" +#include "gdrive-object.hxx" + +using namespace std; +using namespace libcmis; + +static const string CLIENT_ID ( "mock-id" ); +static const string CLIENT_SECRET ( "mock-secret" ); +static const string USERNAME( "mock-user" ); +static const string PASSWORD( "mock-password" ); +static const string USERNAME2( "mock-user2" ); +static const string PASSWORD2( "mock-password2" ); +static const string LOGIN_URL ("https://login/url" ); +static const string LOGIN_URL2 ("https://login2/url" ); +static const string APPROVAL_URL ("https://approval/url" ); +static const string CHALLENGE_URL ("https://accounts.google.com/challenge/url" ); +static const string AUTH_URL ( "https://auth/url" ); +static const string TOKEN_URL ( "https://token/url" ); +static const string SCOPE ( "https://scope/url" ); +static const string REDIRECT_URI ("redirect:uri" ); +static const string BASE_URL ( "https://base/url" ); + +#define PIN "123321" + +namespace +{ + char* lcl_authCodeFallback( const char* /*url*/, const char* /*username*/, const char* /*password*/ ) + { + char *authCode = strdup( PIN ); + return authCode; + } +} + +typedef std::unique_ptr<GDriveSession> GDriveSessionPtr; + +class GDriveTest : public CppUnit::TestFixture +{ + public: + void sessionAuthenticationTest( ); + void sessionAuthenticationTestWith2FA( ); + void sessionExpiryTokenGetTest( ); + void sessionExpiryTokenPostTest( ); + void sessionExpiryTokenPutTest( ); + void sessionExpiryTokenDeleteTest( ); + void setRepositoryTest( ); + void getRepositoriesTest( ); + void getTypeTest( ); + void getObjectTest( ); + void getObjectByPathRootTest( ); + void getObjectByPathTest( ); + void getObjectByPathMissingTest( ); + void getDocumentTest( ); + void getFolderTest( ); + void getDocumentParentsTest( ); + void getContentStreamTest( ); + void setContentStreamTest( ); + void setContentStreamGdocTest( ); + void getChildrenTest( ); + void getDocumentAllowableActionsTest( ); + void getFolderAllowableActionsTest( ); + void checkOutTest( ); + void checkInTest( ); + void deleteTest( ); + void moveTest( ); + void createDocumentTest( ); + void createFolderTest( ); + void updatePropertiesTest( ); + void propertyCopyTest( ); + void removeTreeTest( ); + void getContentStreamWithRenditionsTest( ); + void getRefreshTokenTest( ); + void getThumbnailUrlTest( ); + void getAllVersionsTest( ); + + CPPUNIT_TEST_SUITE( GDriveTest ); + CPPUNIT_TEST( sessionAuthenticationTest ); + CPPUNIT_TEST( sessionAuthenticationTestWith2FA ); + CPPUNIT_TEST( sessionExpiryTokenGetTest ); + CPPUNIT_TEST( sessionExpiryTokenPutTest ); + CPPUNIT_TEST( sessionExpiryTokenPostTest ); + CPPUNIT_TEST( sessionExpiryTokenDeleteTest ); + CPPUNIT_TEST( setRepositoryTest ); + CPPUNIT_TEST( getRepositoriesTest ); + CPPUNIT_TEST( getTypeTest ); + CPPUNIT_TEST( getObjectTest ); + CPPUNIT_TEST( getObjectByPathRootTest ); + CPPUNIT_TEST( getObjectByPathTest ); + CPPUNIT_TEST( getObjectByPathMissingTest ); + CPPUNIT_TEST( getDocumentTest ); + CPPUNIT_TEST( getFolderTest ); + CPPUNIT_TEST( getDocumentParentsTest ); + CPPUNIT_TEST( getContentStreamTest ); + CPPUNIT_TEST( setContentStreamTest ); + CPPUNIT_TEST( setContentStreamGdocTest ); + CPPUNIT_TEST( getChildrenTest ); + CPPUNIT_TEST( getDocumentAllowableActionsTest ); + CPPUNIT_TEST( getFolderAllowableActionsTest ); + CPPUNIT_TEST( checkOutTest ); + CPPUNIT_TEST( checkInTest ); + CPPUNIT_TEST( deleteTest ); + CPPUNIT_TEST( moveTest ); + CPPUNIT_TEST( createDocumentTest ); + CPPUNIT_TEST( createFolderTest ); + CPPUNIT_TEST( updatePropertiesTest ); + CPPUNIT_TEST( propertyCopyTest ); + CPPUNIT_TEST( removeTreeTest ); + CPPUNIT_TEST( getContentStreamWithRenditionsTest ); + CPPUNIT_TEST( getRefreshTokenTest ); + CPPUNIT_TEST( getThumbnailUrlTest ); + CPPUNIT_TEST( getAllVersionsTest ); + CPPUNIT_TEST_SUITE_END( ); + + private: + GDriveSessionPtr getTestSession( string username, string password, bool with2FA = false ); +}; + +GDriveSessionPtr GDriveTest::getTestSession( string username, string password, bool with2FA ) +{ + libcmis::OAuth2DataPtr oauth2( + new libcmis::OAuth2Data( AUTH_URL, TOKEN_URL, SCOPE, + REDIRECT_URI, CLIENT_ID, CLIENT_SECRET )); + curl_mockup_reset( ); + string empty; + //login response + string loginIdentifier = string("scope=") + SCOPE + + string("&redirect_uri=") + REDIRECT_URI + + string("&response_type=code") + + string("&client_id=") + CLIENT_ID; + + curl_mockup_addResponse ( AUTH_URL.c_str(), loginIdentifier.c_str( ), + "GET", DATA_DIR "/gdrive/login1.html", 200, true); + + //authentication email + curl_mockup_addResponse( LOGIN_URL2.c_str( ), empty.c_str( ), "POST", + DATA_DIR "/gdrive/login2.html", 200, true); + + //challenge - pin code + if( with2FA == true ) + { + curl_mockup_addResponse( LOGIN_URL.c_str( ), empty.c_str( ), "POST", + DATA_DIR "/gdrive/challenge.html", 200, true); + + //approval response + curl_mockup_addResponse( CHALLENGE_URL.c_str( ), empty.c_str( ), + "POST", DATA_DIR "/gdrive/approve.html", 200, true); + } + else + { + //authentication password, + curl_mockup_addResponse( LOGIN_URL.c_str( ), empty.c_str( ), "POST", + DATA_DIR "/gdrive/approve.html", 200, true); + } + + //approval response + curl_mockup_addResponse( APPROVAL_URL.c_str( ), empty.c_str( ), + "POST", DATA_DIR "/gdrive/authcode.html", 200, true); + + curl_mockup_addResponse ( TOKEN_URL.c_str( ), empty.c_str( ), "POST", + DATA_DIR "/gdrive/token-response.json", 200, true ); + + return GDriveSessionPtr( new GDriveSession( BASE_URL, username, password, oauth2, false ) ); +} + +void GDriveTest::sessionAuthenticationTest( ) +{ + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string empty; + + // Check authentication request for email + string authRequestEmail( curl_mockup_getRequestBody( LOGIN_URL2.c_str(), empty.c_str( ), + "POST" ) ); + string expectedAuthRequestEmail = + string ( "Page=PasswordSeparationSignIn&continue=redirectLink&scope=Scope&service=lso&GALX=cookie" + "&Email=") + USERNAME; + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong authentication request for Email", + expectedAuthRequestEmail, authRequestEmail ); + + // Check authentication request for password + string authRequestPassword( curl_mockup_getRequestBody( LOGIN_URL.c_str(), empty.c_str( ), + "POST" ) ); + string expectedAuthRequestPassword = + string ( "continue=redirectLink&scope=Scope&service=lso&GALX=cookie" + "&Passwd=") + PASSWORD; + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong authentication request for Password", + expectedAuthRequestPassword, authRequestPassword ); + + // Check code request + string codeRequest( curl_mockup_getRequestBody( APPROVAL_URL.c_str(), + empty.c_str( ), "POST" ) ); + string expectedCodeRequest = string( "state_wrapper=stateWrapper&submit_access=true" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong approval request", + expectedCodeRequest, codeRequest); + + // Check token request + string expectedTokenRequest = + string( "code=AuthCode") + + string( "&client_id=") + CLIENT_ID + + string( "&redirect_uri=") + REDIRECT_URI + + string( "&scope=") + SCOPE + + string( "&grant_type=authorization_code" ); + + string tokenRequest( curl_mockup_getRequestBody( TOKEN_URL.c_str(), empty.c_str( ), + "POST" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong token request", + expectedTokenRequest, tokenRequest ); + + // Check token + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Wrong access token", + string ( "mock-access-token" ), + session->m_oauth2Handler->getAccessToken( )); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Wrong refresh token", + string ("mock-refresh-token"), + session->m_oauth2Handler->getRefreshToken( )); +} + +void GDriveTest::sessionAuthenticationTestWith2FA( ) +{ + libcmis::SessionFactory::setOAuth2AuthCodeProvider( lcl_authCodeFallback ); + + GDriveSessionPtr session = getTestSession( USERNAME2, PASSWORD2, true ); + string empty; + + // Check authentication request for email + string authRequestEmail( curl_mockup_getRequestBody( LOGIN_URL2.c_str(), empty.c_str( ), + "POST" ) ); + string expectedAuthRequestEmail = + string ( "Page=PasswordSeparationSignIn&continue=redirectLink&scope=Scope&service=lso&GALX=cookie" + "&Email=") + USERNAME2; + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong authentication request for Email", + expectedAuthRequestEmail, authRequestEmail ); + + // Check authentication request for password + string authRequestPassword( curl_mockup_getRequestBody( LOGIN_URL.c_str(), empty.c_str( ), + "POST" ) ); + string expectedAuthRequestPassword = + string ( "continue=redirectLink&scope=Scope&service=lso&GALX=cookie" + "&Passwd=") + PASSWORD2; + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong authentication request for Password", + expectedAuthRequestPassword, authRequestPassword ); + + // Check request for pin code + string authRequestPin( curl_mockup_getRequestBody( CHALLENGE_URL.c_str(), empty.c_str( ), + "POST" ) ); + string expectedAuthRequestPin = + string ( "continue=redirectLink&scope=Scope&service=lso&GALX=cookie" + "&Pin=") + PIN; + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong authentication request for Pin", + expectedAuthRequestPin, authRequestPin ); + + // Check code request + string codeRequest( curl_mockup_getRequestBody( APPROVAL_URL.c_str(), + empty.c_str( ), "POST" ) ); + string expectedCodeRequest = string( "state_wrapper=stateWrapper&submit_access=true" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong approval request", + expectedCodeRequest, codeRequest); + + // Check token request + string expectedTokenRequest = + string( "code=AuthCode") + + string( "&client_id=") + CLIENT_ID + + string( "&redirect_uri=") + REDIRECT_URI + + string( "&scope=") + SCOPE + + string( "&grant_type=authorization_code" ); + + string tokenRequest( curl_mockup_getRequestBody( TOKEN_URL.c_str(), empty.c_str( ), + "POST" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong token request", + expectedTokenRequest, tokenRequest ); + + // Check token + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Wrong access token", + string ( "mock-access-token" ), + session->m_oauth2Handler->getAccessToken( )); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Wrong refresh token", + string ("mock-refresh-token"), + session->m_oauth2Handler->getRefreshToken( )); +} + +void GDriveTest::sessionExpiryTokenGetTest( ) +{ + // Access_token will expire after expires_in seconds, + // We need to use the refresh key to get a new one. + + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + curl_mockup_reset( ); + static const string objectId("aFileId"); + string url = BASE_URL + "/files/" + objectId; + + // 401 response, token is expired + curl_mockup_addResponse( url.c_str( ),"", "GET", "", 401, false ); + + curl_mockup_addResponse( TOKEN_URL.c_str(), "", + "POST", DATA_DIR "/gdrive/refresh_response.json", 200, true); + try + { + // GET expires, need to refresh then GET again + libcmis::ObjectPtr obj = session->getObject( objectId ); + } + catch ( ... ) + { + if ( session->getHttpStatus( ) == 401 ) + { + // Check if access token is refreshed + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "wrong access token", + string ( "new-access-token" ), + session->m_oauth2Handler->getAccessToken( ) ); + } + } +} + +void GDriveTest::sessionExpiryTokenPostTest( ) +{ + // Access_token will expire after expires_in seconds, + // We need to use the refresh key to get a new one. + + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + curl_mockup_reset( ); + static const string folderId("aFileId"); + const string folderUrl = BASE_URL + "/files/" + folderId; + const string metaUrl = BASE_URL + "/files"; + + curl_mockup_addResponse( TOKEN_URL.c_str(), "", + "POST", DATA_DIR "/gdrive/refresh_response.json", 200, true); + + curl_mockup_addResponse( folderUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true ); + + // 401 response, token is expired + // refresh and then POST again + curl_mockup_addResponse( metaUrl.c_str( ), "", + "POST", "", 401, false ); + libcmis::FolderPtr parent = session->getFolder( folderId ); + + try + { + PropertyPtrMap properties; + // POST expires, need to refresh then POST again + parent->createFolder( properties ); + } + catch ( ... ) + { + if ( session->getHttpStatus( ) == 401 ) + { + // Check if access token is refreshed + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "wrong access token", + string ( "new-access-token" ), + session->m_oauth2Handler->getAccessToken( ) ); + } + } +} + +void GDriveTest::sessionExpiryTokenDeleteTest( ) +{ + // Access_token will expire after expires_in seconds, + // We need to use the refresh key to get a new one. + + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + curl_mockup_reset( ); + static const string objectId("aFileId"); + string url = BASE_URL + "/files/" + objectId; + + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document2.json", 200, true); + + curl_mockup_addResponse( TOKEN_URL.c_str(), "", + "POST", DATA_DIR "/gdrive/refresh_response.json", 200, true); + // 401 response, token is expired + curl_mockup_addResponse( url.c_str( ),"", "DELETE", "", 401, false); + + libcmis::ObjectPtr obj = session->getObject( objectId ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + try + { + // DELETE expires, need to refresh then DELETE again + object->remove( ); + } + catch ( ... ) + { + if ( session->getHttpStatus( ) == 401 ) + { + // Check if access token is refreshed + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "wrong access token", + string ( "new-access-token" ), + session->m_oauth2Handler->getAccessToken( ) ); + const struct HttpRequest* deleteRequest = curl_mockup_getRequest( url.c_str( ), "", "DELETE" ); + CPPUNIT_ASSERT_MESSAGE( "Delete request not sent", deleteRequest ); + curl_mockup_HttpRequest_free( deleteRequest ); + } + } + +} + +void GDriveTest::sessionExpiryTokenPutTest( ) +{ + // Access_token will expire after expires_in seconds, + // We need to use the refresh key to get a new one. + + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + curl_mockup_reset( ); + static const string objectId("aFileId"); + string url = BASE_URL + "/files/" + objectId; + + curl_mockup_addResponse( TOKEN_URL.c_str(), "", + "POST", DATA_DIR "/gdrive/refresh_response.json", 200, true); + + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true ); + + // 401 response, token is expired + curl_mockup_addResponse( url.c_str( ),"", "PUT", "", 401, false ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + try + { + // PUT expires, need to refresh then PUT again + object->updateProperties( object->getProperties( ) ); + } + catch ( ... ) + { + if ( session->getHttpStatus( ) == 401 ) + { + // Check if access token is refreshed + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "wrong access token", + string ( "new-access-token" ), + session->m_oauth2Handler->getAccessToken( ) ); + } + } +} + +void GDriveTest::setRepositoryTest( ) +{ + GDriveSession session; + CPPUNIT_ASSERT_MESSAGE( "Should never fail", session.setRepository( "foobar" ) ); +} + +void GDriveTest::getDocumentTest( ) +{ + curl_mockup_reset( ); + static const string objectId ("aFileId"); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/files/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + + libcmis::ObjectPtr obj = session->getObject( objectId ); + + // Check if we got the document object. + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( obj ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::DocumentPtr", + NULL != document ); + + // Test the document properties + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document ID", objectId, document->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document name", + string( "GDrive File" ), + document->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document type", + string( "application/vnd.google-apps.form" ), + document->getContentType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:document" ), document->getBaseType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong type", string( "cmis:document" ), document->getType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !document->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !document->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModifiedBy is missing", !document->getLastModifiedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !document->getLastModificationDate( ).is_not_a_date_time() ); + + CPPUNIT_ASSERT_MESSAGE( "Content length is incorrect", 123 == document->getContentLength( ) ); +} + +void GDriveTest::getFolderTest( ) +{ + curl_mockup_reset( ); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + static const string folderId( "aFolderId" ); + static const string parentId( "parentID" ); + string url = BASE_URL + "/files/" + folderId; + string parentUrl = BASE_URL + "/files/" + parentId; + + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true); + curl_mockup_addResponse( parentUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true); + // Check if we got the Folder object. + libcmis::FolderPtr folder = session->getFolder( folderId ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::FolderPtr", + NULL != folder ); + + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder ID", folderId, folder->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder name", string( "testFolder" ), folder->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:folder" ), folder->getBaseType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong type", string( "cmis:folder" ), folder->getType( ) ); + CPPUNIT_ASSERT_MESSAGE( "Missing folder parent", folder->getFolderParent( ).get( ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a root folder", !folder->isRootFolder() ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !folder->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !folder->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModifiedBy is missing", !folder->getLastModifiedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !folder->getLastModificationDate( ).is_not_a_date_time() ); +} + +void GDriveTest::getDocumentParentsTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + static const string documentId( "aFileId" ); + static const string parentId( "aFolderId" ); + static const string parentId2( "aNewFolderId" ); + string url = BASE_URL + "/files/" + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + + string parent1Url = BASE_URL + "/files/" + parentId; + string parent2Url = BASE_URL + "/files/" + parentId2; + curl_mockup_addResponse( parent1Url.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true); + curl_mockup_addResponse( parent2Url.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder2.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( "aFileId" ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + CPPUNIT_ASSERT_MESSAGE( "Document expected", document != NULL ); + + vector< libcmis::FolderPtr > parents= document->getParents( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of parents", size_t( 2 ), parents.size() ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong parent Id", parentId, parents[0]->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong parent Id", parentId2, parents[1]->getId( ) ); +} + +void GDriveTest::getContentStreamTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + static const string documentId( "aFileId" ); + string url = BASE_URL + "/files/" + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + string expectedContent( "Test content stream" ); + string downloadUrl = "https://downloadLink"; + curl_mockup_addResponse( downloadUrl.c_str( ), "", "GET", expectedContent.c_str( ), 0, false ); + + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + try + { + boost::shared_ptr< istream > is = document->getContentStream( ); + ostringstream out; + out << is->rdbuf(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", expectedContent, out.str( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void GDriveTest::setContentStreamTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + const string documentId( "aFileId" ); + const string uploadBaseUrl = "https://www.googleapis.com/upload/drive/v2/files/"; + + string url = BASE_URL + "/files/" + documentId; + string putUrl = uploadBaseUrl + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document2.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + curl_mockup_addResponse( url.c_str( ), "", + "PUT", DATA_DIR "/gdrive/document2.json", 200, true); + curl_mockup_addResponse( putUrl.c_str( ), "", "PUT", "Updated", 0, false ); + try + { + string expectedContent( "Test set content stream" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "aFileName" ); + document->setContentStream( os, "text/plain", filename ); + + CPPUNIT_ASSERT_MESSAGE( "Object not refreshed during setContentStream", object->getRefreshTimestamp( ) > 0 ); + + // Check if metadata has been properly uploaded + const char* meta = curl_mockup_getRequestBody( url.c_str( ), "", "PUT" ); + string expectedMeta = "{\n \"title\": \"aFileName\"\n}\n"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad meta uploaded", expectedMeta, string( meta ) ); + // Check the content has been properly uploaded + const char* content = curl_mockup_getRequestBody( putUrl.c_str( ), "", "PUT" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void GDriveTest::setContentStreamGdocTest( ) +{ + + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + const string documentId( "aFileId" ); + const string uploadBaseUrl = "https://www.googleapis.com/upload/drive/v2/files/"; + + string url = BASE_URL + "/files/" + documentId; + string putUrl = uploadBaseUrl + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + curl_mockup_addResponse( url.c_str( ), "convert=true", + "PUT", DATA_DIR "/gdrive/document.json", 200, true); + curl_mockup_addResponse( putUrl.c_str( ), "convert=true", "PUT", "Updated", 0, false ); + try + { + string expectedContent( "Test set content stream" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "aFileName" ); + document->setContentStream( os, "text/plain", filename ); + + CPPUNIT_ASSERT_MESSAGE( "Object not refreshed during setContentStream", object->getRefreshTimestamp( ) > 0 ); + + // Check the content has been properly uploaded + const char* content = curl_mockup_getRequestBody( putUrl.c_str( ), "", "PUT" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void GDriveTest::getChildrenTest( ) +{ + curl_mockup_reset( ); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + static const string folderId ("aFolderId"); + string url = BASE_URL + "/files/" + folderId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true); + + string urlChildFolder = BASE_URL + "/files/" + string ("aChildFolder"); + curl_mockup_addResponse( urlChildFolder.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true); + + string urlChildDocument = BASE_URL + "/files/" + string ("aChildDocument"); + curl_mockup_addResponse( urlChildDocument.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + + libcmis::ObjectPtr obj = session->getObject( folderId ); + + // Check if we got the Folder object. + libcmis::FolderPtr folder = boost::dynamic_pointer_cast< libcmis::Folder >( obj ); + + CPPUNIT_ASSERT_MESSAGE( "Folder expected", folder != NULL ); + + + string query = "q=\"" + folderId + "\"+in+parents+and+trashed+=+false"; + string childrenUrl = BASE_URL + "/files"; + curl_mockup_addResponse( childrenUrl.c_str( ), query.c_str( ), + "GET", DATA_DIR "/gdrive/folder_children.json", 200, true); + + vector< libcmis::ObjectPtr > children= folder->getChildren( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of children", size_t( 2 ), children.size() ); + + int folderCount = 0; + int documentCount = 0; + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end( ); ++it ) + { + if ( NULL != boost::dynamic_pointer_cast< libcmis::Folder >( *it ) ) + ++folderCount; + else if ( NULL != boost::dynamic_pointer_cast< libcmis::Document >( *it ) ) + ++documentCount; + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of folder children", 1, folderCount ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of document children", 1, documentCount ); +} + +void GDriveTest::getTypeTest( ) +{ + curl_mockup_reset( ); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + string expectedId( "cmis:document" ); + libcmis::ObjectTypePtr actual = session->getType( expectedId ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Id for fetched type", expectedId, actual->getId( ) ); +} + +void GDriveTest::getRepositoriesTest( ) +{ + curl_mockup_reset( ); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + vector< libcmis::RepositoryPtr > actual = session->getRepositories( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of repositories", size_t( 1 ), + actual.size( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong repository found", + string ( "GoogleDrive" ), + actual.front()->getId( ) ); +} + +void GDriveTest::getObjectTest() +{ + static const string objectId ("aFileId"); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/files/" + objectId; + curl_mockup_addResponse ( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/gdoc-file.json", 200, true); + libcmis::ObjectPtr object = session->getObject( objectId ); + boost::shared_ptr<GDriveObject> obj = boost::dynamic_pointer_cast + <GDriveObject>(object); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Object Id", objectId, + obj->getId( ) ); +} + +void GDriveTest::getObjectByPathRootTest() +{ + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string rootUrl = BASE_URL + "/files/root"; + curl_mockup_addResponse ( rootUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true); + + libcmis::ObjectPtr object = session->getObjectByPath( "/" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Object Id", + string("testFolder"), object->getName( ) ); +} + +void GDriveTest::getObjectByPathTest() +{ + // Mockup setup + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string rootChildUrl = BASE_URL + "/files/root/children/"; + curl_mockup_addResponse ( rootChildUrl.c_str( ), "q=title+=+'GDrive File'", + "GET", DATA_DIR "/gdrive/root_child_search.json", 200, true ); + + string urlRootChildDoc = BASE_URL + "/files/aRootChildId2"; + curl_mockup_addResponse( urlRootChildDoc.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true ); + + // Tested method + libcmis::ObjectPtr object = session->getObjectByPath( "/GDrive File" ); + + // Check the result + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Object", + string("GDrive File"), object->getName( ) ); +} + +void GDriveTest::getObjectByPathMissingTest() +{ + // Mockup setup + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string rootChildUrl = BASE_URL + "/files/root/children/"; + curl_mockup_addResponse ( rootChildUrl.c_str( ), "q=title+=+'GDrive File'", + "GET", DATA_DIR "/gdrive/root_child_missing.json", 200, true ); + + // Tested method + try + { + libcmis::ObjectPtr object = session->getObjectByPath( "/GDrive File" ); + CPPUNIT_FAIL( "objectNotFound exception expected" ); + } + catch ( libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("Bad exception type", + string( "objectNotFound" ), e.getType( ) ); + } +} + +void GDriveTest::getDocumentAllowableActionsTest( ) +{ + curl_mockup_reset( ); + static const string objectId ("aFileId"); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/files/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + + libcmis::ObjectPtr obj = session->getObject( objectId ); + + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( obj ); + + boost::shared_ptr< libcmis::AllowableActions > actions = document->getAllowableActions( ); + + CPPUNIT_ASSERT_MESSAGE( "GetContentStream allowable action should be true", + actions->isDefined( libcmis::ObjectAction::GetContentStream ) && + actions->isAllowed( libcmis::ObjectAction::GetContentStream ) ); + CPPUNIT_ASSERT_MESSAGE( "CreateDocument allowable action should be false", + actions->isDefined( libcmis::ObjectAction::CreateDocument ) && + !actions->isAllowed( libcmis::ObjectAction::CreateDocument ) ); +} + +void GDriveTest::getFolderAllowableActionsTest( ) +{ + curl_mockup_reset( ); + static const string folderId ("aFolderId"); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/files/" + folderId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true); + + libcmis::FolderPtr folder = session->getFolder( folderId ); + + boost::shared_ptr< libcmis::AllowableActions > actions = folder->getAllowableActions( ); + + CPPUNIT_ASSERT_MESSAGE( "CreateDocument allowable action should be true", + actions->isDefined( libcmis::ObjectAction::CreateDocument ) && + actions->isAllowed( libcmis::ObjectAction::CreateDocument ) ); + + CPPUNIT_ASSERT_MESSAGE( "GetContentStream allowable action should be false", + actions->isDefined( libcmis::ObjectAction::GetContentStream ) && + !actions->isAllowed( libcmis::ObjectAction::GetContentStream ) ); +} + +void GDriveTest::checkOutTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + static const string documentId( "aFileId" ); + string url = BASE_URL + "/files/" + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + libcmis::DocumentPtr checkout = document->checkOut( ); + CPPUNIT_ASSERT_MESSAGE( "CheckOut failed", NULL != checkout ); +} + +void GDriveTest::checkInTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + const string documentId( "aFileId" ); + const string uploadBaseUrl = "https://www.googleapis.com/upload/drive/v2/files/"; + + string url = BASE_URL + "/files/" + documentId; + string putUrl = uploadBaseUrl + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document2.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + curl_mockup_addResponse( url.c_str( ), "", + "PUT", DATA_DIR "/gdrive/document2.json", 200, true); + curl_mockup_addResponse( putUrl.c_str( ), "", "PUT", "Updated", 0, false ); + + string expectedContent( "content stream" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "aFileName" ); + + const PropertyPtrMap& properties = document->getProperties( ); + libcmis::DocumentPtr checkIn = document->checkIn( true, "", properties, os, "text/plain", filename); + CPPUNIT_ASSERT_MESSAGE( "CheckIn failed", NULL != checkIn ); +} + +void GDriveTest::deleteTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + const string objectId( "aFileId" ); + + string url = BASE_URL + "/files/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document2.json", 200, true); + curl_mockup_addResponse( url.c_str( ),"", "DELETE", "", 204, false); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + object->remove( ); + const struct HttpRequest* deleteRequest = curl_mockup_getRequest( url.c_str( ), "", "DELETE" ); + CPPUNIT_ASSERT_MESSAGE( "Delete request not sent", deleteRequest ); + curl_mockup_HttpRequest_free( deleteRequest ); +} + +void GDriveTest::moveTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string objectId( "aFileId" ); + const string sourceId( "aFolderId" ); + const string desId( "aNewFolderId" ); + + string url = BASE_URL + "/files/" + objectId; + string sourceUrl = BASE_URL + "/files/" + sourceId; + string desUrl = BASE_URL + "/files/" + desId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document2.json", 200, true ); + curl_mockup_addResponse( url.c_str( ), "", + "PUT", DATA_DIR "/gdrive/document2.json", 200, true ); + curl_mockup_addResponse( sourceUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true ); + curl_mockup_addResponse( desUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder2.json", 200, true ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + libcmis::FolderPtr source = session->getFolder( sourceId ); + libcmis::FolderPtr destination = session->getFolder( desId ); + + object->move( source, destination ); + const char* moveRequest = curl_mockup_getRequestBody( url.c_str( ), "", "PUT" ); + Json parentJson = Json::parse( string( moveRequest ) ); + string newParentId = parentJson["parents"].getList().front()["id"].toString( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad new parent folder", + desId, newParentId); +} + +void GDriveTest::createDocumentTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string documentId( "aFileId" ); + const string folderId( "aFolderId" ); + + const string folderUrl = BASE_URL + "/files/" + folderId; + const string metaUrl = BASE_URL + "/files/"; + const string uploadBaseUrl = "https://www.googleapis.com/upload/drive/v2/files/"; + string uploadUrl = uploadBaseUrl + documentId; + string documentUrl = metaUrl + documentId; + + curl_mockup_addResponse( folderUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true ); + curl_mockup_addResponse( metaUrl.c_str( ), "", + "POST", DATA_DIR "/gdrive/document.json", 200, true ); + curl_mockup_addResponse( uploadUrl.c_str( ), "", + "PUT", "updated", 0, false ); + curl_mockup_addResponse( documentUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/document2.json", 200, true); + + libcmis::FolderPtr parent = session->getFolder( folderId ); + + try + { + string expectedContent( "Test set content stream" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "aFileName" ); + PropertyPtrMap properties; + + // function to test + parent->createDocument( properties, os, "text/plain", filename ); + + const char* createRequest = curl_mockup_getRequestBody( metaUrl.c_str( ), "", "POST" ); + Json request = Json::parse( string( createRequest ) ); + string sentParentId = request["parents"].getList( ).front( )["id"].toString( ); + string sentFilename = request["title"].toString( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad parents sent", folderId, sentParentId ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad filename sent", filename, sentFilename ); + + const char* content = curl_mockup_getRequestBody( uploadUrl.c_str( ), "", "PUT" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void GDriveTest::createFolderTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string folderId( "aFolderId" ); + + const string folderUrl = BASE_URL + "/files/" + folderId; + const string metaUrl = BASE_URL + "/files/"; + + curl_mockup_addResponse( folderUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true ); + curl_mockup_addResponse( metaUrl.c_str( ), "", + "POST", DATA_DIR "/gdrive/folder2.json", 200, true ); + libcmis::FolderPtr parent = session->getFolder( folderId ); + try + { + PropertyPtrMap properties; + + // function to test + parent->createFolder( properties ); + + const char* createRequest = curl_mockup_getRequestBody( metaUrl.c_str( ), "", "POST" ); + Json request = Json::parse( string( createRequest ) ); + + string sentParentId = request["parents"].getList( ).front( )["id"].toString( ); + string sentMimeType = request["mimeType"].toString( ); + string expectedMimeType( "application/vnd.google-apps.folder" ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad parents sent", folderId, sentParentId ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad mimeType", expectedMimeType, sentMimeType ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void GDriveTest::removeTreeTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string folderId( "aFolderId" ); + + const string folderUrl = BASE_URL + "/files/" + folderId; + const string trashUrl = folderUrl + "/trash"; + + curl_mockup_addResponse( folderUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/folder.json", 200, true ); + curl_mockup_addResponse( trashUrl.c_str( ), "", + "POST", "", 200, false ); + libcmis::FolderPtr folder = session->getFolder( folderId ); + + // just make sure it doesn't crash + folder->removeTree( ); +} + +void GDriveTest::getContentStreamWithRenditionsTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + static const string documentId( "aFileId" ); + string url = BASE_URL + "/files/" + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + // pdf stream + string pdfContent( "pdf Content stream" ); + string pdfUrl = "pdflink"; + curl_mockup_addResponse( pdfUrl.c_str( ), "", "GET", pdfContent.c_str( ), 0, false ); + + try + { + boost::shared_ptr< istream > is = document->getContentStream( "application/pdf" ); + ostringstream out; + out << is->rdbuf(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", pdfContent, out.str( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } + + // ODF stream + string odfContent( "open document Content stream" ); + string odfUrl = "https://downloadLink"; + curl_mockup_addResponse( odfUrl.c_str( ), "", "GET", odfContent.c_str( ), 0, false ); + try + { + boost::shared_ptr< istream > is = document->getContentStream( "application/x-vnd.oasis.opendocument.spreadsheet" ); + ostringstream out; + out << is->rdbuf(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", odfContent, out.str( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } + + // MS stream + string msContent( "office document Content stream" ); + string msUrl = "xlslink"; + curl_mockup_addResponse( msUrl.c_str( ), "", "GET", msContent.c_str( ), 0, false ); + try + { + boost::shared_ptr< istream > is = document->getContentStream( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ); + ostringstream out; + out << is->rdbuf(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", msContent, out.str( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void GDriveTest::updatePropertiesTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string documentId( "aFileId" ); + const string documentUrl = BASE_URL + "/files/" + documentId; + curl_mockup_addResponse( documentUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true ); + curl_mockup_addResponse( documentUrl.c_str( ), "", + "PUT", DATA_DIR "/gdrive/document-updated.json", 200, true ); + + + // Values for the test + string propertyName( "cmis:name" ); + string expectedValue( "New Title" ); + libcmis::ObjectPtr object = session->getObject( documentId ); + + // Fill the map of properties to change + PropertyPtrMap newProperties; + + libcmis::ObjectTypePtr objectType = object->getTypeDescription( ); + map< string, libcmis::PropertyTypePtr >::iterator it = objectType->getPropertiesTypes( ).find( propertyName ); + vector< string > values; + values.push_back( expectedValue ); + libcmis::PropertyPtr property( new libcmis::Property( it->second, values ) ); + newProperties[ propertyName ] = property; + + // Update the properties (method to test) + object->updateProperties( newProperties ); + + // Check that the sent request is OK + const char* updateRequest = curl_mockup_getRequestBody( documentUrl.c_str( ), "", "PUT" ); + + // Check the sent properties + Json json = Json::parse( string( updateRequest ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Only the updated properties should be sent", + size_t( 1 ), json.getObjects().size( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "title key not present", + expectedValue, json["title"].toString( ) ); + + // Check that the object is updated + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Object not updated", + expectedValue, object->getName() ); +} + +void GDriveTest::propertyCopyTest( ) +{ + string name = "cmis:name"; + string value = "some value"; + + GDriveProperty property( name, Json( value.c_str() ) ); + { + GDriveProperty copy = property; + + CPPUNIT_ASSERT_EQUAL( name, copy.getPropertyType()->getId() ); + CPPUNIT_ASSERT_EQUAL( value, copy.getStrings()[0] ); + } + + { + GDriveProperty copy; + copy = property; + + CPPUNIT_ASSERT_EQUAL( name, copy.getPropertyType()->getId() ); + CPPUNIT_ASSERT_EQUAL( value, copy.getStrings()[0] ); + } +} + +void GDriveTest::getRefreshTokenTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Refresh token does not match", + string ("mock-refresh-token"), + session->getRefreshToken( ) ); +} + +void GDriveTest::getThumbnailUrlTest( ) +{ + curl_mockup_reset( ); + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string documentId( "aFileId" ); + + const string documentUrl = BASE_URL + "/files/" + documentId; + + curl_mockup_addResponse( documentUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true ); + + libcmis::ObjectPtr document = session->getObject( documentId ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Thumbnail URL does not match", + string ("https://aThumbnailLink"), + document->getThumbnailUrl( ) ); + +} + +void GDriveTest::getAllVersionsTest( ) +{ + curl_mockup_reset( ); + static const string objectId ("aFileId"); + + GDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/files/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/gdrive/document.json", 200, true); + string revisionUrl = url + "/revisions"; + curl_mockup_addResponse( revisionUrl.c_str( ), "", + "GET", DATA_DIR "/gdrive/allVersions.json", 200, true); + + libcmis::ObjectPtr obj = session->getObject( objectId ); + + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( obj ); + + // Method to test + vector< libcmis::DocumentPtr > versions = document->getAllVersions( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of versions", size_t( 3 ), versions.size( ) ); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( GDriveTest ); + diff --git a/qa/libcmis/test-helpers.cxx b/qa/libcmis/test-helpers.cxx new file mode 100644 index 0000000..0ef2452 --- /dev/null +++ b/qa/libcmis/test-helpers.cxx @@ -0,0 +1,187 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libxml/parser.h> +#include <libxml/tree.h> + +#include <libcmis/xml-utils.hxx> + +#include "test-helpers.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace test +{ + + XmlNodeRef::XmlNodeRef( xmlNodePtr node, boost::shared_ptr< xmlDoc > doc ) + : m_node( node ) + , m_doc( doc ) + { + } + + XmlNodeRef::XmlNodeRef( const XmlNodeRef& other ) + : m_node( other.m_node ) + , m_doc( other.m_doc ) + { + } + + XmlNodeRef& XmlNodeRef::operator=( const XmlNodeRef& other ) + { + m_node = other.m_node; + m_doc = other.m_doc; + return *this; + } + + XmlNodeRef::operator xmlNodePtr( ) const + { + return m_node; + } + + XmlNodeRef getXmlNode( string str ) + { + xmlNodePtr node = NULL; + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( str.c_str( ), str.size( ), "tester", NULL, 0 ), xmlFreeDoc ); + if ( bool( doc ) ) + node = xmlDocGetRootElement( doc.get() ); + + return XmlNodeRef( node, doc ); + } + + const char* getXmlns( ) + { + return "xmlns:cmis=\"http://docs.oasis-open.org/ns/cmis/core/200908/\" xmlns:cmisra=\"http://docs.oasis-open.org/ns/cmis/restatom/200908/\" "; + } + + string writeXml( boost::shared_ptr< libcmis::XmlSerializable > serializable ) + { + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + serializable->toXml( writer ); + xmlTextWriterEndDocument( writer ); + + string str( ( const char * )xmlBufferContent( buf ) ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + return str; + } + + string getXmlNodeAsString( const string& xmlDoc, const string& xpath ) + { + string result; + xmlDocPtr doc = xmlReadMemory( xmlDoc.c_str(), xmlDoc.size(), "", NULL, 0 ); + + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + libcmis::registerCmisWSNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( xpath.c_str() ), xpathCtx ); + + if ( xpathObj != NULL ) + { + int nbResults = 0; + if ( xpathObj->nodesetval ) + nbResults = xpathObj->nodesetval->nodeNr; + + for ( int i = 0; i < nbResults; ++i ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + xmlBufferPtr buf = xmlBufferCreate( ); + xmlNodeDump( buf, doc, node, 0, 0 ); + result += string( ( char * )xmlBufferContent( buf ) ); + xmlBufferFree( buf ); + } + } + xmlXPathFreeObject( xpathObj); + } + xmlXPathFreeContext( xpathCtx ); + } + else + throw libcmis::Exception( "Failed to parse service document" ); + + xmlFreeDoc( doc ); + + return result; + } + + libcmis::DocumentPtr createVersionableDocument( libcmis::Session* session, string docName ) + { + libcmis::FolderPtr parent = session->getRootFolder( ); + + // Prepare the properties for the new object, object type is cmis:folder + PropertyPtrMap props; + libcmis::ObjectTypePtr type = session->getType( "VersionableType" ); + map< string, libcmis::PropertyTypePtr > propTypes = type->getPropertiesTypes( ); + + // Set the object name + map< string, libcmis::PropertyTypePtr >::iterator it = propTypes.find( string( "cmis:name" ) ); + vector< string > nameValues; + nameValues.push_back( docName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( it->second, nameValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // set the object type + it = propTypes.find( string( "cmis:objectTypeId" ) ); + vector< string > typeValues; + typeValues.push_back( "VersionableType" ); + libcmis::PropertyPtr typeProperty( new libcmis::Property( it->second, typeValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeProperty ) ); + + // Actually send the document creation request + string contentStr = "Some content"; + boost::shared_ptr< ostream > os ( new stringstream( contentStr ) ); + string contentType = "text/plain"; + string filename( "name.txt" ); + + return parent->createDocument( props, os, contentType, filename ); + } + + void loadFromFile( const char* path, string& buf ) + { + ifstream in( path ); + + in.seekg( 0, ios::end ); + int length = in.tellg( ); + in.seekg( 0, ios::beg ); + + char* buffer = new char[length]; + in.read( buffer, length ); + in.close( ); + + buf = string( buffer, length ); + delete[] buffer; + } +} diff --git a/qa/libcmis/test-helpers.hxx b/qa/libcmis/test-helpers.hxx new file mode 100644 index 0000000..9e02357 --- /dev/null +++ b/qa/libcmis/test-helpers.hxx @@ -0,0 +1,63 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <string> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include <libcmis/document.hxx> +#include <libcmis/session.hxx> +#include <libcmis/xmlserializable.hxx> + +namespace test +{ + class XmlNodeRef + { + public: + XmlNodeRef( xmlNodePtr node, boost::shared_ptr< xmlDoc > doc ); + XmlNodeRef( const XmlNodeRef& other ); + XmlNodeRef& operator=( const XmlNodeRef& other ); + + operator xmlNodePtr( ) const; + + private: + xmlNodePtr m_node; + boost::shared_ptr< xmlDoc > m_doc; + }; + + // Test helper functions for parser and writer tests + XmlNodeRef getXmlNode( std::string str ); + const char* getXmlns( ); + std::string writeXml( boost::shared_ptr< libcmis::XmlSerializable > serializable ); + + std::string getXmlNodeAsString( const std::string& xmlDoc, const std::string& xpath ); + + libcmis::DocumentPtr createVersionableDocument( libcmis::Session* session, std::string docName ); + void loadFromFile( const char* path, std::string& buf ); +} diff --git a/qa/libcmis/test-jsonutils.cxx b/qa/libcmis/test-jsonutils.cxx new file mode 100644 index 0000000..4a9fd49 --- /dev/null +++ b/qa/libcmis/test-jsonutils.cxx @@ -0,0 +1,182 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <string> +#include <fstream> +#include <cerrno> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <libcmis/property.hxx> +#include <libcmis/property-type.hxx> + +#include "json-utils.hxx" + +using namespace std; +using namespace libcmis; + +class JsonTest : public CppUnit::TestFixture +{ + public: + void parseTest( ); + void parseTypeTest( ); + void createFromPropertyTest( ); + void createFromPropertiesTest( ); + void badKeyTest( ); + void addTest( ); + + CPPUNIT_TEST_SUITE( JsonTest ); + CPPUNIT_TEST( parseTest ); + CPPUNIT_TEST( parseTypeTest ); + CPPUNIT_TEST( createFromPropertyTest ); + CPPUNIT_TEST( createFromPropertiesTest ); + CPPUNIT_TEST( badKeyTest ); + CPPUNIT_TEST( addTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +string getFileContents( const char *filename) +{ + std::ifstream in( filename, std::ios::in | std::ios::binary ); + if (in) + { + std::string contents; + in.seekg( 0, std::ios::end ); + contents.resize(in.tellg( ) ); + in.seekg( 0, std::ios::beg ); + in.read( &contents[0], contents.size( ) ); + in.close( ); + return contents; + } + throw ( errno ); +} + +Json parseFile( string fileName ) +{ + Json json = Json::parse( getFileContents( fileName.c_str( ) ) ); + return json; +} + +void JsonTest::parseTest( ) +{ + Json json = parseFile( DATA_DIR "/gdrive/jsontest-good.json" ); + string kind = json["kind"].toString( ); + string id = json["id"].toString( ); + string mimeType = json["mimeType"].toString( ); + string createdDate = json["createdDate"].toString( ); + string intTest = json["intTest"].toString( ); + string doubleTest = json["doubleTest"].toString( ); + string editable = json["editable"].toString( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong kind", string( "drive#file" ), kind ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id", string( "aFileId"), id ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong mimeType", string( "application/vnd.google-apps.form"), mimeType ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong createdDate", string( "2010-04-28T14:53:23.141Z"), createdDate ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong intTest", string("-123"), intTest ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong doubleTest", string("-123.456"), doubleTest ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong editable", string( "true"), editable ); +} + +void JsonTest::parseTypeTest( ) +{ + Json json = parseFile( DATA_DIR "/gdrive/jsontest-good.json" ); + Json::Type stringType = json["kind"].getDataType( ); + Json::Type boolType = json["editable"].getDataType( ); + Json::Type intType = json["intTest"].getDataType( ); + Json::Type doubleType = json["doubleTest"].getDataType( ); + Json::Type dateTimeType = json["createdDate"].getDataType( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong string type", Json::json_string, stringType ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong bool type", Json::json_bool, boolType ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong int type", Json::json_int, intType ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong double type", Json::json_double, doubleType ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong datetime type", Json::json_datetime, dateTimeType ); +} + +void JsonTest::createFromPropertyTest( ) +{ + vector< string > values; + string expected("Value 1" ); + values.push_back( expected ); + + PropertyTypePtr propertyType( new PropertyType( ) ); + + PropertyPtr property( new Property( propertyType, values ) ); + + Json json( property ); + + CPPUNIT_ASSERT_EQUAL( expected, json.toString( ) ); +} + +void JsonTest::createFromPropertiesTest( ) +{ + vector< string > values; + string expected( "value" ); + values.push_back( "value" ); + + PropertyTypePtr propertyType( new PropertyType( ) ); + + PropertyPtr property( new libcmis::Property( propertyType, values ) ); + + PropertyPtrMap properties; + properties[ "key" ] = property; + + Json json( properties ); + + CPPUNIT_ASSERT_EQUAL( expected, json["key"].toString( ) ); +} + +void JsonTest::badKeyTest( ) +{ + Json json = parseFile( DATA_DIR "/gdrive/jsontest-good.json" ); + // just make sure it doesn't crash here + string notExist = json["nonExistedKey"].toString( ); + CPPUNIT_ASSERT_EQUAL( string( ), notExist); +} + +void JsonTest::addTest( ) +{ + Json json = parseFile( DATA_DIR "/gdrive/jsontest-good.json" ); + Json addJson("added"); + json.add( "new", addJson); + CPPUNIT_ASSERT_EQUAL( addJson.toString( ), json["new"].toString( ) ); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( JsonTest ); diff --git a/qa/libcmis/test-main.cxx b/qa/libcmis/test-main.cxx new file mode 100644 index 0000000..23555e9 --- /dev/null +++ b/qa/libcmis/test-main.cxx @@ -0,0 +1,39 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <cppunit/ui/text/TestRunner.h> + +int main( int, char** ) +{ + CppUnit::TextUi::TestRunner runner; + CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + runner.addTest( registry.makeTest() ); + bool wasSuccess = runner.run( "", false ); + return !wasSuccess; +} diff --git a/qa/libcmis/test-mockup-helpers.cxx b/qa/libcmis/test-mockup-helpers.cxx new file mode 100644 index 0000000..40c3605 --- /dev/null +++ b/qa/libcmis/test-mockup-helpers.cxx @@ -0,0 +1,50 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <test-helpers.hxx> +#include <mockup-config.h> + +using namespace std; + +namespace test +{ + void addWsResponse( const char* url, const char* filename, + const char* bodyMatch = 0 ) + { + string outBuf; + loadFromFile( filename, outBuf ); + + string emptyLine = ( "\n\n" ); + size_t pos = outBuf.find( emptyLine ); + string headers = outBuf.substr( 0, pos ); + string body = outBuf.substr( pos + emptyLine.size() ); + + curl_mockup_addResponse( url, "", "POST", body.c_str(), 0, false, + headers.c_str(), bodyMatch ); + } +} diff --git a/qa/libcmis/test-mockup-helpers.hxx b/qa/libcmis/test-mockup-helpers.hxx new file mode 100644 index 0000000..75e854a --- /dev/null +++ b/qa/libcmis/test-mockup-helpers.hxx @@ -0,0 +1,33 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +namespace test +{ + void addWsResponse( const char* url, const char* filename, + const char* bodyMatch = 0 ); +} diff --git a/qa/libcmis/test-onedrive.cxx b/qa/libcmis/test-onedrive.cxx new file mode 100644 index 0000000..b15edd1 --- /dev/null +++ b/qa/libcmis/test-onedrive.cxx @@ -0,0 +1,781 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <string> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <mockup-config.h> + +#include <fstream> + +#include <libcmis/document.hxx> + +#include "onedrive-object.hxx" +#include "onedrive-property.hxx" +#include "onedrive-session.hxx" +#include "oauth2-handler.hxx" + +using namespace std; +using namespace libcmis; + +static const string CLIENT_ID ( "mock-id" ); +static const string CLIENT_SECRET ( "mock-secret" ); +static const string USERNAME( "mock-user" ); +static const string PASSWORD( "mock-password" ); +static const string LOGIN_URL ("https://login/url" ); +static const string LOGIN_URL2 ("https://login2/url" ); +static const string APPROVAL_URL ("https://approval/url" ); +static const string AUTH_URL ( "https://auth/url" ); +static const string TOKEN_URL ( "https://token/url" ); +static const string SCOPE ( "https://scope/url" ); +static const string REDIRECT_URI ("redirect:uri" ); +static const string BASE_URL ( "https://base/url" ); + +typedef std::unique_ptr<OneDriveSession> OneDriveSessionPtr; + +class OneDriveTest : public CppUnit::TestFixture +{ + public: + void sessionAuthenticationTest( ); + void sessionExpiryTokenGetTest( ); + void getRepositoriesTest( ); + void getObjectTypeDocumentTest( ); + void getObjectTypeFolderTest( ); + void getObjectTest( ); + void filePropertyTest( ); + void deleteTest( ); + void updatePropertiesTest( ); + void getFileAllowableActionsTest( ); + void getFolderAllowableActionsTest( ); + void getFolderTest( ); + void getChildrenTest( ); + void moveTest( ); + void getDocumentTest( ); + void getDocumentParentTest( ); + void getContentStreamTest( ); + void setContentStreamTest( ); + void createDocumentTest( ); + void getObjectByPathTest( ); + + CPPUNIT_TEST_SUITE( OneDriveTest ); + CPPUNIT_TEST( sessionAuthenticationTest ); + CPPUNIT_TEST( sessionExpiryTokenGetTest ); + CPPUNIT_TEST( getRepositoriesTest ); + CPPUNIT_TEST( getObjectTypeDocumentTest ); + CPPUNIT_TEST( getObjectTypeFolderTest ); + CPPUNIT_TEST( getObjectTest ); + CPPUNIT_TEST( filePropertyTest ); + CPPUNIT_TEST( deleteTest ); + CPPUNIT_TEST( updatePropertiesTest ); + CPPUNIT_TEST( getFileAllowableActionsTest ); + CPPUNIT_TEST( getFolderAllowableActionsTest ); + CPPUNIT_TEST( getFolderTest ); + CPPUNIT_TEST( getChildrenTest ); + CPPUNIT_TEST( moveTest ); + CPPUNIT_TEST( getDocumentTest ); + CPPUNIT_TEST( getDocumentParentTest ); + CPPUNIT_TEST( getContentStreamTest ); + CPPUNIT_TEST( setContentStreamTest ); + CPPUNIT_TEST( createDocumentTest ); + CPPUNIT_TEST( getObjectByPathTest ); + CPPUNIT_TEST_SUITE_END( ); + + private: + OneDriveSessionPtr getTestSession( string username, string password ); +}; + +OneDriveSessionPtr OneDriveTest::getTestSession( string username, string password ) +{ + libcmis::OAuth2DataPtr oauth2( + new libcmis::OAuth2Data( AUTH_URL, TOKEN_URL, SCOPE, + REDIRECT_URI, CLIENT_ID, CLIENT_SECRET )); + curl_mockup_reset( ); + string empty; + // login, authentication & approval are done manually at the moment, so I'll + // temporarily borrow them from gdrive + //login response + string loginIdentifier = string("scope=") + SCOPE + + string("&redirect_uri=") + REDIRECT_URI + + string("&response_type=code") + + string("&client_id=") + CLIENT_ID; + + curl_mockup_addResponse ( AUTH_URL.c_str(), loginIdentifier.c_str( ), + "GET", DATA_DIR "/gdrive/login1.html", 200, true); + + //authentication email + curl_mockup_addResponse( LOGIN_URL2.c_str( ), empty.c_str( ), "POST", + DATA_DIR "/gdrive/login2.html", 200, true); + + //authentication password + curl_mockup_addResponse( LOGIN_URL.c_str( ), empty.c_str( ), "POST", + DATA_DIR "/gdrive/approve.html", 200, true); + + //approval response + curl_mockup_addResponse( APPROVAL_URL.c_str( ), empty.c_str( ), + "POST", DATA_DIR "/gdrive/authcode.html", 200, true); + + + // token response + curl_mockup_addResponse ( TOKEN_URL.c_str( ), empty.c_str( ), "POST", + DATA_DIR "/onedrive/token-response.json", 200, true ); + + return OneDriveSessionPtr( new OneDriveSession( BASE_URL, username, password, oauth2, false ) ); +} + +void OneDriveTest::sessionAuthenticationTest( ) +{ + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string empty; + + // Check token request + string expectedTokenRequest = + string( "code=AuthCode") + + string( "&client_id=") + CLIENT_ID + + string( "&client_secret=") + CLIENT_SECRET + + string( "&redirect_uri=") + REDIRECT_URI + + string( "&grant_type=authorization_code" ); + + string tokenRequest( curl_mockup_getRequestBody( TOKEN_URL.c_str(), empty.c_str( ), + "POST" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong token request", + expectedTokenRequest, tokenRequest ); + + // Check token + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Wrong access token", + string ( "mock-access-token" ), + session->m_oauth2Handler->getAccessToken( )); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Wrong refresh token", + string ("mock-refresh-token"), + session->m_oauth2Handler->getRefreshToken( )); +} + +void OneDriveTest::sessionExpiryTokenGetTest( ) +{ + // Access_token will expire after expires_in seconds, + // We need to use the refresh key to get a new one. + + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + curl_mockup_reset( ); + static const string objectId("aFileId"); + string url = BASE_URL + "/me/skydrive/files/" + objectId; + + // 401 response, token is expired + curl_mockup_addResponse( url.c_str( ),"", "GET", "", 401, false ); + + curl_mockup_addResponse( TOKEN_URL.c_str(), "", + "POST", DATA_DIR "/onedrive/refresh-response.json", 200, true); + try + { + // GET expires, need to refresh then GET again + libcmis::ObjectPtr obj = session->getObject( objectId ); + } + catch ( ... ) + { + if ( session->getHttpStatus( ) == 401 ) + { + // Check if access token is refreshed + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "wrong access token", + string ( "new-access-token" ), + session->m_oauth2Handler->getAccessToken( ) ); + } + } +} + +void OneDriveTest::getRepositoriesTest( ) +{ + curl_mockup_reset( ); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + vector< libcmis::RepositoryPtr > actual = session->getRepositories( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of repositories", size_t( 1 ), + actual.size( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong repository found", + string ( "OneDrive" ), + actual.front()->getId( ) ); +} + +void OneDriveTest::getObjectTypeDocumentTest() +{ + curl_mockup_reset( ); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + libcmis::ObjectTypePtr actual = session->getType("cmis:document"); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong type ID", string("cmis:document"), + actual->getId() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong type queryName", + string("cmis:document"), + actual->getQueryName() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong parent", string(""), + actual->getParentTypeId() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string("cmis:document"), + actual->getBaseTypeId() ); + + CPPUNIT_ASSERT( actual->getParentType().get() == NULL ); + CPPUNIT_ASSERT_EQUAL( string( "cmis:document" ), + actual->getBaseType()->getId() ); + + CPPUNIT_ASSERT( actual->isCreatable() ); + CPPUNIT_ASSERT( !actual->isVersionable() ); + CPPUNIT_ASSERT( actual->isFileable() ); + CPPUNIT_ASSERT( actual->isQueryable() ); + CPPUNIT_ASSERT( actual->isFulltextIndexed() ); + CPPUNIT_ASSERT_EQUAL( libcmis::ObjectType::Allowed, + actual->getContentStreamAllowed() ); + + map< string, libcmis::PropertyTypePtr > props = actual->getPropertiesTypes(); + + CPPUNIT_ASSERT_MESSAGE( "Missing property cmis:name", + props.find("cmis:name") != props.end() ); + + CPPUNIT_ASSERT_MESSAGE( "Missing property cmis:name", + props.find("cmis:name") != props.end() ); + CPPUNIT_ASSERT_MESSAGE( "Missing property cmis:contentStreamFileName", + props.find("cmis:contentStreamFileName") != props.end() ); + CPPUNIT_ASSERT_MESSAGE( "Missing property cmis:contentStreamLength", + props.find("cmis:contentStreamLength") != props.end() ); +} + +void OneDriveTest::getObjectTypeFolderTest() +{ + curl_mockup_reset( ); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + libcmis::ObjectTypePtr actual = session->getType("cmis:folder"); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong type ID", string("cmis:folder"), + actual->getId() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong type queryName", + string("cmis:folder"), + actual->getQueryName() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong parent", string(""), + actual->getParentTypeId() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string("cmis:folder"), + actual->getBaseTypeId() ); + + CPPUNIT_ASSERT( actual->getParentType().get() == NULL ); + CPPUNIT_ASSERT_EQUAL( string( "cmis:folder" ), + actual->getBaseType()->getId() ); + + CPPUNIT_ASSERT( actual->isCreatable() ); + CPPUNIT_ASSERT( !actual->isVersionable() ); + CPPUNIT_ASSERT( actual->isFileable() ); + CPPUNIT_ASSERT( actual->isQueryable() ); + CPPUNIT_ASSERT( !actual->isFulltextIndexed() ); + CPPUNIT_ASSERT_EQUAL( libcmis::ObjectType::NotAllowed, + actual->getContentStreamAllowed() ); + + map< string, libcmis::PropertyTypePtr > props = actual->getPropertiesTypes(); + + CPPUNIT_ASSERT_MESSAGE( "Missing property cmis:name", + props.find("cmis:name") != props.end() ); + CPPUNIT_ASSERT_MESSAGE( "Property cmis:contentStreamFileName shouldn't be set", + props.find("cmis:contentStreamFileName") == props.end() ); + CPPUNIT_ASSERT_MESSAGE( "Property cmis:contentStreamLength shouldn't be set", + props.find("cmis:contentStreamLength") == props.end() ); +} + +void OneDriveTest::getObjectTest() +{ + static const string objectId ("aFileId"); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/" + objectId; + curl_mockup_addResponse ( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + libcmis::ObjectPtr object = session->getObject( objectId ); + boost::shared_ptr<OneDriveObject> obj = boost::dynamic_pointer_cast + <OneDriveObject>( object ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Object Id", objectId, + obj->getId( ) ); +} + +void OneDriveTest::filePropertyTest( ) +{ + curl_mockup_reset( ); + static const string objectId ("aFileId"); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + + libcmis::ObjectPtr obj = session->getObject( objectId ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong creation date", + string ( "2014-06-09T08:24:04+0000" ), + obj->getStringProperty( "cmis:creationDate" ) ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong object id", + string ( "aFileId" ), + obj->getStringProperty( "cmis:objectId" ) ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong author", + string ( "onedriveUser" ), + obj->getStringProperty( "cmis:createdBy" ) ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong file name", + string ( "OneDriveFile" ), + obj->getStringProperty( "cmis:contentStreamFileName" ) ); +} + +void OneDriveTest::deleteTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + const string objectId( "aFileId" ); + + string url = BASE_URL + "/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + curl_mockup_addResponse( url.c_str( ),"", "DELETE", "", 204, false); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + object->remove( ); + const struct HttpRequest* deleteRequest = curl_mockup_getRequest( url.c_str( ), "", "DELETE" ); + CPPUNIT_ASSERT_MESSAGE( "Delete request not sent", deleteRequest ); + curl_mockup_HttpRequest_free( deleteRequest ); +} + +void OneDriveTest::updatePropertiesTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string objectId( "aFileId" ); + const string newObjectId( "aNewFileId" ); + + const string objectUrl = BASE_URL + "/" + objectId; + const string newObjectUrl = BASE_URL + "/" + newObjectId; + + curl_mockup_addResponse( objectUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true ); + curl_mockup_addResponse( newObjectUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/updated-file.json", 200, true ); + curl_mockup_addResponse( objectUrl.c_str( ), "", + "PUT", DATA_DIR "/onedrive/updated-file.json", 200, true ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + libcmis::ObjectPtr newObject = session->getObject( newObjectId ); + + object->updateProperties( newObject->getProperties( ) ); + + const char* updateRequest = curl_mockup_getRequestBody( objectUrl.c_str( ), "", "PUT" ); + + Json json = Json::parse( string( updateRequest ) ); + string name = json["name"].toString( ); + string description = json["description"].toString( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Name key not converted", + string( "New File Name"), + name ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Description key not converted", + string( "new description"), + description ); +} + +void OneDriveTest::getFileAllowableActionsTest( ) +{ + curl_mockup_reset( ); + static const string objectId ("aFileId"); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + boost::shared_ptr< libcmis::AllowableActions > actions = object->getAllowableActions( ); + + CPPUNIT_ASSERT_MESSAGE( "GetContentStream allowable action should be true", + actions->isDefined( libcmis::ObjectAction::GetContentStream ) && + actions->isAllowed( libcmis::ObjectAction::GetContentStream ) ); + CPPUNIT_ASSERT_MESSAGE( "CreateDocument allowable action should be false", + actions->isDefined( libcmis::ObjectAction::CreateDocument ) && + !actions->isAllowed( libcmis::ObjectAction::CreateDocument ) ); +} + +void OneDriveTest::getFolderAllowableActionsTest( ) +{ + curl_mockup_reset( ); + static const string objectId ("aFolderId"); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/folder.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + boost::shared_ptr< libcmis::AllowableActions > actions = object->getAllowableActions( ); + + CPPUNIT_ASSERT_MESSAGE( "CreateDocument allowable action should be true", + actions->isDefined( libcmis::ObjectAction::CreateDocument ) && + actions->isAllowed( libcmis::ObjectAction::CreateDocument ) ); + + CPPUNIT_ASSERT_MESSAGE( "GetContentStream allowable action should be false", + actions->isDefined( libcmis::ObjectAction::GetContentStream ) && + !actions->isAllowed( libcmis::ObjectAction::GetContentStream ) ); +} + +void OneDriveTest::getFolderTest( ) +{ + curl_mockup_reset( ); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + static const string folderId( "aFolderId" ); + static const string parentId( "aParentId" ); + string url = BASE_URL + "/" + folderId; + string parentUrl = BASE_URL + "/" + parentId; + + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/folder.json", 200, true); + + curl_mockup_addResponse( parentUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/parent-folder.json", 200, true); + + libcmis::FolderPtr folder = session->getFolder( folderId ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::FolderPtr", + NULL != folder ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder ID", folderId, folder->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder name", string( "OneDrive Folder" ), folder->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:folder" ), folder->getBaseType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "Missing folder parent", folder->getFolderParent( ).get( ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a root folder", !folder->isRootFolder() ); + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !folder->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !folder->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !folder->getLastModificationDate( ).is_not_a_date_time() ); +} + +void OneDriveTest::getChildrenTest( ) +{ + curl_mockup_reset( ); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + static const string folderId ("aFolderId"); + string url = BASE_URL + "/" + folderId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/folder.json", 200, true); + + libcmis::ObjectPtr obj = session->getObject( folderId ); + + libcmis::FolderPtr folder = session->getFolder( folderId ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::FolderPtr", + NULL != folder ); + + string childrenUrl = BASE_URL + "/" + folderId + "/files"; + curl_mockup_addResponse( childrenUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/folder-listed.json", 200, true); + + vector< libcmis::ObjectPtr > children= folder->getChildren( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of children", size_t( 2 ), children.size() ); + + int folderCount = 0; + int fileCount = 0; + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end( ); ++it ) + { + if ( NULL != boost::dynamic_pointer_cast< libcmis::Folder >( *it ) ) + ++folderCount; + else + ++fileCount; + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of folder children", 1, folderCount ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of file children", 1, fileCount ); +} + +void OneDriveTest::moveTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string objectId( "aFileId" ); + const string sourceId( "aFolderId" ); + const string desId( "aParentId" ); + + string url = BASE_URL + "/" + objectId; + string sourceUrl = BASE_URL + "/" + sourceId; + string desUrl = BASE_URL + "/" + desId; + + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true ); + curl_mockup_addResponse( url.c_str( ), "method=MOVE", + "POST", DATA_DIR "/onedrive/file.json", 200, true ); + curl_mockup_addResponse( desUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/parent-folder.json", 200, true ); + curl_mockup_addResponse( sourceUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/folder.json", 200, true ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + libcmis::FolderPtr source = session->getFolder( sourceId ); + libcmis::FolderPtr destination = session->getFolder( desId ); + + object->move( source, destination ); + const char* moveRequest = curl_mockup_getRequestBody( url.c_str( ), "method=MOVE", "POST" ); + Json parentJson = Json::parse( string( moveRequest ) ); + string newParentId = parentJson["destination"].toString( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad new parent folder", desId, newParentId); +} + +void OneDriveTest::getDocumentTest( ) +{ + curl_mockup_reset( ); + static const string objectId ("aFileId"); + + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string url = BASE_URL + "/" + objectId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + + libcmis::ObjectPtr obj = session->getObject( objectId ); + + // Check if we got the document object. + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( obj ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::DocumentPtr", + NULL != document ); + + // Test the document properties + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document ID", objectId, document->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document name", + string( "OneDriveFile" ), + document->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:document" ), document->getBaseType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !document->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !document->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !document->getLastModificationDate( ).is_not_a_date_time() ); + + CPPUNIT_ASSERT_MESSAGE( "Content length is incorrect", 42 == document->getContentLength( ) ); +} + +void OneDriveTest::getDocumentParentTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + static const string documentId( "aFileId" ); + static const string parentId( "aParentId" ); + string url = BASE_URL + "/" + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + + string parentUrl = BASE_URL + "/" + parentId; + curl_mockup_addResponse( parentUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/parent-folder.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( "aFileId" ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + CPPUNIT_ASSERT_MESSAGE( "Document expected", document != NULL ); + + vector< libcmis::FolderPtr > parents= document->getParents( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of parents", size_t( 1 ), parents.size() ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong parent Id", parentId, parents[0]->getId( ) ); +} + +void OneDriveTest::getContentStreamTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + static const string documentId( "aFileId" ); + string url = BASE_URL + "/" + documentId; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + string expectedContent( "Test content stream" ); + string downloadUrl = "sourceUrl"; + curl_mockup_addResponse( downloadUrl.c_str( ), "", "GET", expectedContent.c_str( ), 0, false ); + + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + try + { + boost::shared_ptr< istream > is = document->getContentStream( ); + ostringstream out; + out << is->rdbuf(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", expectedContent, out.str( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void OneDriveTest::setContentStreamTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + const string documentId( "aFileId" ); + + string url = BASE_URL + "/" + documentId; + string putUrl = BASE_URL + "/aParentId/files/OneDriveFile"; + curl_mockup_addResponse( url.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( documentId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + curl_mockup_addResponse( url.c_str( ), "", + "PUT", DATA_DIR "/onedrive/file.json", 200, true); + curl_mockup_addResponse( putUrl.c_str( ), "overwrite=true", "PUT", "Updated", 0, false ); + try + { + string expectedContent( "Test set content stream" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "aFileName" ); + document->setContentStream( os, "text/plain", filename ); + + CPPUNIT_ASSERT_MESSAGE( "Object not refreshed during setContentStream", object->getRefreshTimestamp( ) > 0 ); + + // Check if metadata has been properly uploaded + const char* meta = curl_mockup_getRequestBody( url.c_str( ), "", "PUT" ); + string expectedMeta = "{\n \"name\": \"aFileName\"\n}\n"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad meta uploaded", expectedMeta, string( meta ) ); + // Check the content has been properly uploaded + const char* content = curl_mockup_getRequestBody( putUrl.c_str( ), "", "PUT" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void OneDriveTest::createDocumentTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string documentId( "aFileId" ); + const string folderId( "aParentId" ); + const string filename( "aFileName" ); + + const string folderUrl = BASE_URL + "/" + folderId; + const string uploadUrl = folderUrl + "/files/" + filename; + const string documentUrl = BASE_URL + "/" + documentId; + + curl_mockup_addResponse( folderUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/parent-folder.json", 200, true ); + curl_mockup_addResponse( uploadUrl.c_str( ), "", + "PUT", DATA_DIR "/onedrive/new-file.json", 200, true ); + curl_mockup_addResponse( documentUrl.c_str( ), "", + "PUT", DATA_DIR "/onedrive/file.json", 200, true ); + curl_mockup_addResponse( documentUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/file.json", 200, true ); + + libcmis::FolderPtr parent = session->getFolder( folderId ); + try + { + string expectedContent( "Test set content stream" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + PropertyPtrMap properties; + + parent->createDocument( properties, os, "text/plain", filename ); + + curl_mockup_getRequestBody( documentUrl.c_str( ), "", "PUT" ); + const char* content = curl_mockup_getRequestBody( uploadUrl.c_str( ), "", "PUT" ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what( ); + CPPUNIT_FAIL( msg.c_str( ) ); + } +} + +void OneDriveTest::getObjectByPathTest( ) +{ + curl_mockup_reset( ); + OneDriveSessionPtr session = getTestSession( USERNAME, PASSWORD ); + const string documentId( "rightFile" ); + const string wrongDocumentId( "wrongFile" ); + const string folderAId( "folderA" ); // root + const string folderBId( "folderB" ); + const string folderCId( "folderC" ); + const string path( "/Folder B/Folder C/OneDriveFile" ); + + const string documentUrl = BASE_URL + "/" + documentId; + const string wrongDocumentUrl = BASE_URL + "/" + wrongDocumentId; + const string folderAUrl = BASE_URL + "/" + folderAId; + const string folderBUrl = BASE_URL + "/" + folderBId; + const string folderCUrl = BASE_URL + "/" + folderCId; + const string searchUrl = BASE_URL + "/me/skydrive/search"; + + curl_mockup_addResponse( documentUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/searched-file.json", 200, true ); + curl_mockup_addResponse( wrongDocumentUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/searched-wrong-file.json", 200, true ); + curl_mockup_addResponse( searchUrl.c_str( ), "q=OneDriveFile", + "GET", DATA_DIR "/onedrive/search-result.json", 200, true ); + curl_mockup_addResponse( folderAUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/folderA.json", 200, true ); + curl_mockup_addResponse( folderBUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/folderB.json", 200, true ); + curl_mockup_addResponse( folderCUrl.c_str( ), "", + "GET", DATA_DIR "/onedrive/folderC.json", 200, true ); + + libcmis::ObjectPtr object = session->getObjectByPath( path ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong objectFetched", documentId, object->getId( ) ); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( OneDriveTest ); diff --git a/qa/libcmis/test-sharepoint.cxx b/qa/libcmis/test-sharepoint.cxx new file mode 100644 index 0000000..91a2367 --- /dev/null +++ b/qa/libcmis/test-sharepoint.cxx @@ -0,0 +1,733 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#include <string> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <mockup-config.h> + +#include <fstream> +#include "test-helpers.hxx" +#include "sharepoint-document.hxx" +#include "sharepoint-object.hxx" +#include "sharepoint-property.hxx" +#include "sharepoint-session.hxx" + +using namespace std; +using namespace libcmis; + +static const string USERNAME( "mock-user" ); +static const string PASSWORD( "mock-password" ); +static const string BASE_URL( "http://base/_api/Web" ); +static const string CONTEXTINFO_URL( "http://base/_api/contextinfo" ); + +typedef std::unique_ptr<SharePointSession> SharePointSessionPtr; + +class SharePointTest : public CppUnit::TestFixture +{ + public: + void setRepositoryTest( ); + void getRepositoriesTest( ); + void getObjectTest( ); + void propertiesTest( ); + void deleteTest( ); + void xdigestExpiredTest( ); + void getFileAllowableActionsTest( ); + void getFolderAllowableActionsTest( ); + void getDocumentTest( ); + void getContentStreamTest( ); + void setContentStreamTest( ); + void checkOutTest( ); + void checkInTest( ); + void getAllVersionsTest( ); + void getFolderTest( ); + void getChildrenTest( ); + void createFolderTest( ); + void createDocumentTest( ); + void moveTest( ); + void getObjectByPathTest( ); + + void propertyCopyTest( ); + void objectCopyTest( ); + + CPPUNIT_TEST_SUITE( SharePointTest ); + CPPUNIT_TEST( setRepositoryTest ); + CPPUNIT_TEST( getRepositoriesTest ); + CPPUNIT_TEST( getObjectTest ); + CPPUNIT_TEST( propertiesTest ); + CPPUNIT_TEST( deleteTest ); + CPPUNIT_TEST( xdigestExpiredTest ); + CPPUNIT_TEST( getFileAllowableActionsTest ); + CPPUNIT_TEST( getFolderAllowableActionsTest ); + CPPUNIT_TEST( getDocumentTest ); + CPPUNIT_TEST( getContentStreamTest ); + CPPUNIT_TEST( setContentStreamTest ); + CPPUNIT_TEST( checkOutTest ); + CPPUNIT_TEST( checkInTest ); + CPPUNIT_TEST( getAllVersionsTest ); + CPPUNIT_TEST( getFolderTest ); + CPPUNIT_TEST( getChildrenTest ); + CPPUNIT_TEST( createFolderTest ); + CPPUNIT_TEST( createDocumentTest ); + CPPUNIT_TEST( moveTest ); + CPPUNIT_TEST( getObjectByPathTest ); + CPPUNIT_TEST( propertyCopyTest ); + CPPUNIT_TEST( objectCopyTest ); + CPPUNIT_TEST_SUITE_END( ); + + private: + SharePointSessionPtr getTestSession( string username, string password ); +}; + +SharePointSessionPtr SharePointTest::getTestSession( string username, string password ) +{ + curl_mockup_reset( ); + curl_mockup_addResponse( BASE_URL.c_str( ), "", "GET", "", 401, false ); + curl_mockup_addResponse( ( BASE_URL + "/currentuser" ).c_str( ), "", "GET", + DATA_DIR "/sharepoint/auth-resp.json", 200, true ); + curl_mockup_addResponse( CONTEXTINFO_URL.c_str( ), "", "POST", + DATA_DIR "/sharepoint/xdigest.json", 200, true ); + + return SharePointSessionPtr( new SharePointSession( BASE_URL, username, password, false ) ); +} + +void SharePointTest::setRepositoryTest( ) +{ + curl_mockup_reset( ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + CPPUNIT_ASSERT_MESSAGE( "setRepository should never fail", session->setRepository( "Anything" )); +} + +void SharePointTest::getRepositoriesTest( ) +{ + curl_mockup_reset( ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + vector< libcmis::RepositoryPtr > actual = session->getRepositories( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of repositories", size_t( 1 ), + actual.size( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong repository found", + string ( "SharePoint" ), + actual.front()->getId( ) ); +} + +void SharePointTest::getObjectTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + boost::shared_ptr<SharePointObject> obj = boost::dynamic_pointer_cast + <SharePointObject>( object ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Object Id", objectId, + obj->getId( ) ); +} + +void SharePointTest::propertiesTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong creation date", + string ( "2014-07-08T09:29:29Z" ), + object->getStringProperty( "cmis:creationDate" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong object id", + string ( "http://base/_api/Web/aFileId" ), + object->getStringProperty( "cmis:objectId" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong author", + string ( "aUserId" ), + object->getStringProperty( "cmis:createdBy" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong file name", + string ( "SharePointFile" ), + object->getStringProperty( "cmis:contentStreamFileName" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong checkin comment", + string ( "aCheckinComment" ), + object->getStringProperty( "cmis:checkinComment" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong version", + string ( "1.0" ), + object->getStringProperty( "cmis:versionLabel" ) ); +} + +void SharePointTest::deleteTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + curl_mockup_addResponse( objectId.c_str( ),"", "DELETE", "", 204, false); + + libcmis::ObjectPtr object = session->getObject( objectId ); + + object->remove( ); + const struct HttpRequest* deleteRequest = curl_mockup_getRequest( objectId.c_str( ), "", "DELETE" ); + CPPUNIT_ASSERT_MESSAGE( "Delete request not sent", deleteRequest ); + curl_mockup_HttpRequest_free( deleteRequest ); +} + +void SharePointTest::xdigestExpiredTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + curl_mockup_addResponse( objectId.c_str( ),"", "DELETE", "", 401, false); + curl_mockup_addResponse( CONTEXTINFO_URL.c_str( ), "", "POST", + DATA_DIR "/sharepoint/new-xdigest.json", 200, true ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + try + { + object->remove( ); + } + catch ( ... ) + { + if ( session->getHttpStatus( ) == 401 ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "wrong xdigest code", + string ( "new-xdigest-code" ), + session->m_digestCode ); + } + } +} + +void SharePointTest::getFileAllowableActionsTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + boost::shared_ptr< libcmis::AllowableActions > actions = object->getAllowableActions( ); + + CPPUNIT_ASSERT_MESSAGE( "GetContentStream allowable action should be true", + actions->isDefined( libcmis::ObjectAction::GetContentStream ) && + actions->isAllowed( libcmis::ObjectAction::GetContentStream ) ); + CPPUNIT_ASSERT_MESSAGE( "CreateDocument allowable action should be false", + actions->isDefined( libcmis::ObjectAction::CreateDocument ) && + !actions->isAllowed( libcmis::ObjectAction::CreateDocument ) ); +} + +void SharePointTest::getFolderAllowableActionsTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFolderId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string folderPropUrl = objectId + "/Properties"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder.json", 200, true); + curl_mockup_addResponse ( folderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + boost::shared_ptr< libcmis::AllowableActions > actions = object->getAllowableActions( ); + + CPPUNIT_ASSERT_MESSAGE( "CreateDocument allowable action should be true", + actions->isDefined( libcmis::ObjectAction::CreateDocument ) && + actions->isAllowed( libcmis::ObjectAction::CreateDocument ) ); + + CPPUNIT_ASSERT_MESSAGE( "GetContentStream allowable action should be false", + actions->isDefined( libcmis::ObjectAction::GetContentStream ) && + !actions->isAllowed( libcmis::ObjectAction::GetContentStream ) ); +} + +void SharePointTest::getDocumentTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + // Check if we got the document object. + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::DocumentPtr", + NULL != document ); + + // Test the document properties + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document ID", objectId, document->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document name", + string( "SharePointFile" ), + document->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:document" ), document->getBaseType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !document->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !document->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !document->getLastModificationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "Content length is incorrect", 18045 == document->getContentLength( ) ); +} + +void SharePointTest::getContentStreamTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + string expectedContent( "Test content stream" ); + string downloadUrl = objectId + "/%24value"; + + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + curl_mockup_addResponse( downloadUrl.c_str( ), "", "GET", expectedContent.c_str( ), 0, false ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + try + { + boost::shared_ptr< istream > is = document->getContentStream( ); + ostringstream out; + out << is->rdbuf(); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", expectedContent, out.str( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void SharePointTest::setContentStreamTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + string expectedContent( "Test content stream" ); + string putUrl = objectId + "/%24value"; + + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + curl_mockup_addResponse( putUrl.c_str( ), "", "PUT", "Updated", 0, false ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + try + { + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "aFileName" ); + document->setContentStream( os, "text/plain", filename ); + + CPPUNIT_ASSERT_MESSAGE( "Object not refreshed during setContentStream", object->getRefreshTimestamp( ) > 0 ); + // Check the content has been properly uploaded + const char* content = curl_mockup_getRequestBody( putUrl.c_str( ), "", "PUT" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void SharePointTest::checkOutTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + static const string authorUrl = objectId + "/Author"; + static const string checkOutUrl = objectId + "/checkout"; + static const string cancelCheckOutUrl = objectId + "/undocheckout"; + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + curl_mockup_addResponse( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true ); + curl_mockup_addResponse( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true ); + curl_mockup_addResponse( checkOutUrl.c_str( ), "", + "POST", DATA_DIR "/sharepoint/file.json", 200, true ); + curl_mockup_addResponse( cancelCheckOutUrl.c_str( ), "", + "POST", DATA_DIR "/sharepoint/file.json", 200, true ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + libcmis::DocumentPtr checkedOutDocument = document->checkOut( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong checkedOut document", + objectId, + checkedOutDocument->getId( ) ); + + checkedOutDocument->cancelCheckout( ); +} + +void SharePointTest::checkInTest( ) +{ + static const string objectId( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + string expectedContent( "Test content stream" ); + string putUrl = objectId + "/%24value"; + string checkInUrl = objectId + "/checkin(comment='checkin_comment',checkintype=1)"; + + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + curl_mockup_addResponse( putUrl.c_str( ), "", "PUT", "Updated", 0, false ); + curl_mockup_addResponse( checkInUrl.c_str( ), "", + "POST", DATA_DIR "/sharepoint/file.json", 200, true ); + + libcmis::ObjectPtr object = session->getObject( objectId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + PropertyPtrMap properties; + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string fileName( "aFileName" ); + string checkInComment( "checkin_comment" ); + + libcmis::DocumentPtr checkedInDocument; + checkedInDocument = document->checkIn( true, checkInComment, properties, os, "", fileName ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong checkedIn document", objectId, checkedInDocument->getId( ) ); +} + +void SharePointTest::getAllVersionsTest( ) +{ + static const string objectId( "http://base/_api/Web/aFileId" ); + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + string versionsUrl = objectId + "/Versions"; + string objectV1Url = versionsUrl +"(1)"; + string objectV2Url = versionsUrl +"(2)"; + string objectV1Id = objectId + "-v1"; + + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + curl_mockup_addResponse ( versionsUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/versions.json", 200, true); + curl_mockup_addResponse ( objectV1Url.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( objectV2Url.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file-v1.json", 200, true); + + libcmis::ObjectPtr object = session->getObject( objectId ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + vector< libcmis::DocumentPtr > allVersions = document->getAllVersions( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong version of the document - 1", + objectId, allVersions[1]->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong version of the document - 2", + objectV1Id, allVersions[2]->getId( ) ); +} + +void SharePointTest::getFolderTest( ) +{ + static const string folderId( "http://base/_api/Web/aFolderId" ); + static const string parentId( "http://base/_api/Web/rootFolderId" ); + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + string parentUrl = folderId + "/ParentFolder"; + string folderPropUrl = folderId + "/Properties"; + string parentFolderPropUrl = parentId + "/Properties"; + curl_mockup_addResponse( folderId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder.json", 200, true ); + curl_mockup_addResponse( folderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true ); + curl_mockup_addResponse( parentFolderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true ); + curl_mockup_addResponse( parentUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/root-folder.json", 200, true ); + curl_mockup_addResponse( parentId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/root-folder.json", 200, true ); + + libcmis::FolderPtr folder = session->getFolder( folderId ); + + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::FolderPtr", + NULL != folder ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder ID", folderId, folder->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder name", string( "SharePointFolder" ), folder->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:folder" ), folder->getBaseType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "Missing folder parent", folder->getFolderParent( ).get( ) ); + CPPUNIT_ASSERT_MESSAGE( "Not a root folder", !folder->isRootFolder() ); +} + +void SharePointTest::getChildrenTest( ) +{ + static const string folderId( "http://base/_api/Web/aFolderId" ); + static const string authorUrl( "http://base/_api/Web/aFileId/Author" ); + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + string filesUrl = folderId + "/Files"; + string foldersUrl = folderId + "/Folders"; + string folderPropUrl = folderId + "/Properties"; + curl_mockup_addResponse( folderId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder.json", 200, true ); + curl_mockup_addResponse( folderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true ); + curl_mockup_addResponse( filesUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/children-files.json", 200, true ); + curl_mockup_addResponse( foldersUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/children-folders.json", 200, true ); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + + libcmis::FolderPtr folder = session->getFolder( folderId ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::FolderPtr", + NULL != folder ); + + vector< libcmis::ObjectPtr > children= folder->getChildren( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of children", size_t( 2 ), children.size() ); + + int folderCount = 0; + int fileCount = 0; + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end( ); ++it ) + { + if ( NULL != boost::dynamic_pointer_cast< libcmis::Folder >( *it ) ) + ++folderCount; + else { + ++fileCount; + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( *it ); + vector< libcmis::FolderPtr > parents= document->getParents( ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of parents", size_t( 1 ), parents.size() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong parent Id", folderId, parents[0]->getId( ) ); + } + + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of folder children", 1, folderCount ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of file children", 1, fileCount ); +} + +void SharePointTest::createFolderTest( ) +{ + static const string folderId( "http://base/_api/Web/aFolderId" ); + static const string parentId( "http://base/_api/Web/rootFolderId" ); + static const string newFolderUrl ( "http://base/_api/Web/folders/add('/SharePointFolder')" ); + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + string folderPropUrl = folderId + "/Properties"; + string parentFolderPropUrl = parentId + "/Properties"; + curl_mockup_addResponse( folderId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder.json", 200, true ); + curl_mockup_addResponse( folderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true ); + curl_mockup_addResponse( parentFolderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true ); + curl_mockup_addResponse( parentId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/root-folder.json", 200, true ); + curl_mockup_addResponse( newFolderUrl.c_str( ), "", + "POST", DATA_DIR "/sharepoint/folder.json", 200, true ); + + libcmis::FolderPtr folder = session->getFolder( parentId ); + PropertyPtrMap properties = session->getFolder( folderId )->getProperties( ); + + libcmis::FolderPtr newFolder = folder->createFolder( properties ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "New folder not created", folderId, newFolder->getId( ) ); +} + +void SharePointTest::createDocumentTest( ) +{ + static const string folderId( "http://base/_api/Web/aFolderId" ); + static const string fileId( "http://base/_api/Web/aFileId" ); + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + + string folderPropUrl = folderId + "/Properties"; + string newDocUrl = folderId + "/files/add(overwrite=true,url='NewDoc')"; + string authorUrl = fileId + "/Author"; + curl_mockup_addResponse( folderId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder.json", 200, true ); + curl_mockup_addResponse( folderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true ); + curl_mockup_addResponse( newDocUrl.c_str( ), "", + "POST", DATA_DIR "/sharepoint/file.json", 200, true ); + curl_mockup_addResponse( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true ); + + libcmis::FolderPtr folder = session->getFolder( folderId ); + try + { + string expectedContent( "Test set content stream" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + PropertyPtrMap properties; + string fileName = "NewDoc"; + + libcmis::DocumentPtr document = folder->createDocument( properties, os, + "text/plain", fileName ); + + const char* content = curl_mockup_getRequestBody( newDocUrl.c_str( ), "", "POST" ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad content uploaded", expectedContent, string( content ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad document id", fileId, document->getId( ) ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what( ); + CPPUNIT_FAIL( msg.c_str( ) ); + } +} + +void SharePointTest::moveTest( ) +{ + static const string fileId ( "http://base/_api/Web/aFileId" ); + static const string folderId( "http://base/_api/Web/aFolderId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = fileId + "/Author"; + string folderPropUrl = folderId + "/Properties"; + string moveUrl = fileId + "/moveto(newurl='/SharePointFolder/SharePointFile',flags=1)"; + curl_mockup_addResponse ( fileId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + curl_mockup_addResponse ( folderId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder.json", 200, true); + curl_mockup_addResponse( folderPropUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/folder-properties.json", 200, true ); + curl_mockup_addResponse( moveUrl.c_str( ), "", + "POST", DATA_DIR "/sharepoint/file.json", 200, true ); + + libcmis::ObjectPtr document = session->getObject( fileId ); + libcmis::FolderPtr folder = session->getFolder( folderId ); + + document->move( folder, folder ); + // nothing to assert, making the right reqeusts should be enough +} + +void SharePointTest::getObjectByPathTest( ) +{ + static const string folderUrl( "http://base/_api/Web/getFolderByServerRelativeUrl('/SharePointFile')" ); + static const string fileUrl( "http://base/_api/Web/getFileByServerRelativeUrl('/SharePointFile')" ); + static string authorUrl( "http://base/_api/Web/aFileId/Author" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + curl_mockup_addResponse( fileUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse( folderUrl.c_str( ), "", + "GET", "", 400, true); + curl_mockup_addResponse( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + + libcmis::ObjectPtr object = session->getObjectByPath( "/SharePointFile" ); + // Check if we got the document object. + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::DocumentPtr", + NULL != document ); +} + +void SharePointTest::propertyCopyTest( ) +{ + SharePointProperty property("Author", + "\"__deferred\":{" + " \"uri\":\"http://base/_api/Web/aFileId/Author\"" + "}"); + + { + SharePointProperty copy; + copy = property; + + CPPUNIT_ASSERT_EQUAL( property.m_propertyType->m_id, copy.m_propertyType->m_id ); + CPPUNIT_ASSERT_EQUAL( property.m_strValues[0], copy.m_strValues[0]); + } + + { + SharePointProperty copy( property ); + + CPPUNIT_ASSERT_EQUAL( property.m_propertyType->m_id, copy.m_propertyType->m_id ); + CPPUNIT_ASSERT_EQUAL( property.m_strValues[0], copy.m_strValues[0]); + } +} + +void SharePointTest::objectCopyTest( ) +{ + static const string objectId ( "http://base/_api/Web/aFileId" ); + + SharePointSessionPtr session = getTestSession( USERNAME, PASSWORD ); + string authorUrl = objectId + "/Author"; + curl_mockup_addResponse ( objectId.c_str( ), "", + "GET", DATA_DIR "/sharepoint/file.json", 200, true); + curl_mockup_addResponse ( authorUrl.c_str( ), "", + "GET", DATA_DIR "/sharepoint/author.json", 200, true); + + boost::shared_ptr< SharePointObject > object = boost::dynamic_pointer_cast< SharePointObject >(session->getObject( objectId ) ); + + { + SharePointObject copy( *object ); + CPPUNIT_ASSERT_EQUAL( object->m_refreshTimestamp, copy.m_refreshTimestamp ); + } + + { + SharePointObject copy( session.get( ) ); + copy = *object; + CPPUNIT_ASSERT_EQUAL( object->m_refreshTimestamp, copy.m_refreshTimestamp ); + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION( SharePointTest ); diff --git a/qa/libcmis/test-soap.cxx b/qa/libcmis/test-soap.cxx new file mode 100644 index 0000000..e401971 --- /dev/null +++ b/qa/libcmis/test-soap.cxx @@ -0,0 +1,563 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> +#include <cppunit/ui/text/TestRunner.h> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include "ws-relatedmultipart.hxx" +#include "ws-requests.hxx" +#include "ws-soap.hxx" +#include "test-helpers.hxx" + +using namespace std; + +class SoapTest : public CppUnit::TestFixture +{ + private: + map< string, SoapResponseCreator > getTestMapping( ); + map< string, string > getTestNamespaces( ); + map< string, SoapFaultDetailCreator > getTestDetailMapping( ); + + public: + + // Copy tests + void soapResponseFactoryCopyTest(); + + // Soap Responses tests + + void createResponseTest( ); + void parseFaultDetailEmptyTest( ); + void parseFaultDetailUnknownTest( ); + void parseFaultDetailValidTest( ); + void createFaultDefaultTest( ); + void parseResponseTest( ); + void parseResponseXmlTest( ); + void parseResponseFaultTest( ); + + // RelatedMultipart tests + + void serializeMultipartSimpleTest( ); + void serializeMultipartComplexTest( ); + void parseMultipartTest( ); + void getStreamFromNodeXopTest( ); + void getStreamFromNodeBase64Test( ); + + // CMISM utilities tests + void writeCmismStreamTest( ); + + CPPUNIT_TEST_SUITE( SoapTest ); + CPPUNIT_TEST( soapResponseFactoryCopyTest ); + + CPPUNIT_TEST( createResponseTest ); + CPPUNIT_TEST( parseFaultDetailEmptyTest ); + CPPUNIT_TEST( parseFaultDetailUnknownTest ); + CPPUNIT_TEST( parseFaultDetailValidTest ); + CPPUNIT_TEST( parseResponseTest ); + CPPUNIT_TEST( parseResponseXmlTest ); + CPPUNIT_TEST( parseResponseFaultTest ); + + CPPUNIT_TEST( serializeMultipartSimpleTest ); + CPPUNIT_TEST( serializeMultipartComplexTest ); + CPPUNIT_TEST( parseMultipartTest ); + CPPUNIT_TEST( getStreamFromNodeXopTest ); + CPPUNIT_TEST( getStreamFromNodeBase64Test ); + + CPPUNIT_TEST( writeCmismStreamTest ); + + CPPUNIT_TEST_SUITE_END( ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( SoapTest ); + +/** Dummy response class to use for testing + */ +class TestResponse : public SoapResponse +{ + private: + TestResponse( ) { }; + + public: + + static SoapResponsePtr create( xmlNodePtr, RelatedMultipart&, SoapSession* ) + { + SoapResponsePtr resp ( new TestResponse( ) ); + return resp; + } +}; + +class TestFaultDetail : public SoapFaultDetail +{ + private: + TestFaultDetail( ) : SoapFaultDetail( ) { }; + + public: + ~TestFaultDetail( ) noexcept { }; + + static SoapFaultDetailPtr create( xmlNodePtr ) + { + return SoapFaultDetailPtr( new TestFaultDetail( ) ); + } +}; + +map< string, SoapResponseCreator > SoapTest::getTestMapping( ) +{ + map< string, SoapResponseCreator > mapping; + mapping[ "{test-ns-url}testResponse" ] = &TestResponse::create; + return mapping; +} + +map< string, SoapFaultDetailCreator > SoapTest::getTestDetailMapping( ) +{ + map< string, SoapFaultDetailCreator > mapping; + mapping[ "{test-ns-url}testFault" ] = &TestFaultDetail::create; + return mapping; +} + +map< string, string > SoapTest::getTestNamespaces( ) +{ + map< string, string > namespaces; + namespaces[ "test" ] = "test-ns-url"; + return namespaces; +} + +void SoapTest::soapResponseFactoryCopyTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + { + SoapResponseFactory copy; + copy = factory; + + CPPUNIT_ASSERT_EQUAL( factory.m_mapping.size(), copy.m_mapping.size() ); + CPPUNIT_ASSERT_EQUAL( factory.m_namespaces.size(), copy.m_namespaces.size() ); + CPPUNIT_ASSERT_EQUAL( factory.m_detailMapping.size(), copy.m_detailMapping.size() ); + CPPUNIT_ASSERT_EQUAL( factory.m_session, copy.m_session ); + } + + { + SoapResponseFactory copy( factory ); + + CPPUNIT_ASSERT_EQUAL( factory.m_mapping.size(), copy.m_mapping.size() ); + CPPUNIT_ASSERT_EQUAL( factory.m_namespaces.size(), copy.m_namespaces.size() ); + CPPUNIT_ASSERT_EQUAL( factory.m_detailMapping.size(), copy.m_detailMapping.size() ); + CPPUNIT_ASSERT_EQUAL( factory.m_session, copy.m_session ); + } +} + +void SoapTest::createResponseTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + string xml = "<n1:testResponse xmlns:n1=\"test-ns-url\"/>"; + RelatedMultipart multipart; // Multipart won't be used in that test + + SoapResponsePtr actual = factory.createResponse( test::getXmlNode( xml ), multipart ); + CPPUNIT_ASSERT_MESSAGE( "Wrong response created", dynamic_cast< TestResponse* >( actual.get( ) ) != NULL ); +} + +void SoapTest::parseFaultDetailEmptyTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + string xml = "<detail/>"; + + vector< SoapFaultDetailPtr > actual = factory.parseFaultDetail( test::getXmlNode( xml ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shouldn't have any detail", size_t( 0 ), actual.size() ); +} + +void SoapTest::parseFaultDetailUnknownTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + string xml = "<detail><unknown-detail/></detail>"; + + vector< SoapFaultDetailPtr > actual = factory.parseFaultDetail( test::getXmlNode( xml ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shouldn't have ignored unknonw details", size_t( 0 ), actual.size() ); +} +void SoapTest::parseFaultDetailValidTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + string xml = "<detail><n1:testFault xmlns:n1=\"test-ns-url\"/></detail>"; + + vector< SoapFaultDetailPtr > actual = factory.parseFaultDetail( test::getXmlNode( xml ) ); + CPPUNIT_ASSERT_MESSAGE( "Wrong fault detail created", + dynamic_cast< TestFaultDetail* >( actual.front( ).get( ) ) != NULL ); +} + +void SoapTest::parseResponseTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + string xml = "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Body>" + "<test:testResponse xmlns:test=\"test-ns-url\"/>" + "<test:testResponse xmlns:test=\"test-ns-url\"/>" + "</S:Body></S:Envelope>"; + string name( "name" ); + string type( "application/xop+xml" ); + RelatedPartPtr requestPart( new RelatedPart( name, type, xml ) ); + + RelatedMultipart multipart; + string cid = multipart.addPart( requestPart ); + string startInfo( "text/xml" ); + multipart.setStart( cid, startInfo ); + + vector< SoapResponsePtr > actual = factory.parseResponse( multipart ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of responses", size_t( 2 ), actual.size( ) ); +} + +void SoapTest::parseResponseXmlTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + string xml = "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Body>" + "<test:testResponse xmlns:test=\"test-ns-url\"/>" + "<test:testResponse xmlns:test=\"test-ns-url\"/>" + "</S:Body></S:Envelope>"; + + vector< SoapResponsePtr > actual = factory.parseResponse( xml ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of responses", size_t( 2 ), actual.size( ) ); +} + +void SoapTest::parseResponseFaultTest( ) +{ + SoapResponseFactory factory; + factory.setMapping( getTestMapping() ); + factory.setNamespaces( getTestNamespaces( ) ); + factory.setDetailMapping( getTestDetailMapping( ) ); + + string xml = "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"" + " xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"" + " xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">" + " <S:Body><S:Fault>" + " <faultcode xsi:type=\"xsd:string\">S:Client</faultcode>" + " <faultstring xsi:type=\"xsd:string\">Some Error Message</faultstring>" + " <detail><n1:testFault xmlns:n1=\"test-ns-url\"/></detail>" + " </S:Fault></S:Body>" + "</S:Envelope>"; + + string name( "name" ); + string type( "application/xop+xml" ); + RelatedPartPtr requestPart( new RelatedPart( name, type, xml ) ); + + RelatedMultipart multipart; + string cid = multipart.addPart( requestPart ); + string startInfo( "text/xml" ); + multipart.setStart( cid, startInfo ); + + try + { + factory.parseResponse( multipart ); + CPPUNIT_FAIL( "Should have thrown the SoapFault" ); + } + catch ( const SoapFault& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong detail string", string( "Some Error Message" ), e.getFaultstring() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong detail string", string( "Client" ), e.getFaultcode() ); + CPPUNIT_ASSERT_MESSAGE( "Wrong fault detail created", + dynamic_cast< TestFaultDetail* >( e.getDetail( ).front( ).get( ) ) != NULL ); + } +} + +void SoapTest::serializeMultipartSimpleTest( ) +{ + string partName = "data"; + string partType = "text/plain"; + string partContent = "Some content"; + string startInfo = "some info"; + + + RelatedMultipart multipart; + RelatedPartPtr part( new RelatedPart( partName, partType, partContent ) ); + string cid = multipart.addPart( part ); + multipart.setStart( cid, startInfo ); + + boost::shared_ptr< istringstream > actual = multipart.toStream( ); + + string boundary = multipart.getBoundary( ); + string expected = "\r\n--" + boundary + "\r\n" + + "Content-Id: <" + cid + ">\r\n" + + "Content-Type: " + partType + "\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + partContent + + "\r\n--" + boundary + "--\r\n"; + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong body", expected, actual->str() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong content type", + "multipart/related;start=\"" + cid + "\";type=\"" + partType + "\";boundary=\"" + boundary + "\";start-info=\"" + startInfo + "\"", + multipart.getContentType() ); +} + +void SoapTest::serializeMultipartComplexTest( ) +{ + string rootName = "root"; + string rootType = "text/plain"; + string rootContent = "Some content"; + + string part2Name = "part2"; + string part2Type = "application/octet-stream"; + string part2Content = "Some content 2"; + + string startInfo = "some info"; + + + RelatedMultipart multipart; + RelatedPartPtr rootPart( new RelatedPart( rootName, rootType, rootContent ) ); + string rootCid = multipart.addPart( rootPart ); + + RelatedPartPtr part2( new RelatedPart( part2Name, part2Type, part2Content ) ); + string part2Cid = multipart.addPart( part2 ); + + multipart.setStart( rootCid, startInfo ); + + boost::shared_ptr< istringstream > actual = multipart.toStream( ); + + string boundary = multipart.getBoundary( ); + string expected = "\r\n--" + boundary + "\r\n" + + "Content-Id: <" + rootCid + ">\r\n" + + "Content-Type: " + rootType + "\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + rootContent + + "\r\n--" + boundary + "\r\n" + + "Content-Id: <" + part2Cid + ">\r\n" + + "Content-Type: " + part2Type + "\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + part2Content + + "\r\n--" + boundary + "--\r\n"; + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong body", expected, actual->str() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong content type", + "multipart/related;start=\"" + rootCid + "\";type=\"" + rootType + "\";boundary=\"" + boundary + "\";start-info=\"" + startInfo + "\"", + multipart.getContentType() ); +} + +void SoapTest::parseMultipartTest( ) +{ + string rootCid = "root-cid"; + string rootType = "text/plain"; + string rootContent = "Some content"; + + string part2Cid = "part2-cid"; + string part2Type = "application/octet-stream"; + string part2Content = "Some content 2\r\nwith windows-style line endings\r\n"; + + string startInfo = "some info"; + + string boundary = "------------ABCDEF-Boundary"; + string body = "\r\n--" + boundary + "\r\n" + + "Content-Id: <" + rootCid + ">\r\n" + + "Content-Type: " + rootType + "\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + rootContent + + "\r\n--" + boundary + "\r\n" + + // Voluntarily make a case-sensitivity error to test the SharePoint case + "Content-ID: <" + part2Cid + ">\r\n" + + "Content-Type: " + part2Type + "\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + part2Content + + "\r\n--" + boundary + "--\r\n"; + + // Added a space before one of the items as it may happen + string contentType = "multipart/related; start=\"" + rootCid + "\";type=\"" + rootType + "\";" + + "boundary=\"" + boundary + "\";start-info=\"" + startInfo + "\""; + + RelatedMultipart multipart( body, contentType ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong start Content id", rootCid, multipart.getStartId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong start info", startInfo, multipart.getStartInfo( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong boundary", boundary, multipart.getBoundary( ) ); + + vector< string > cids = multipart.getIds( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of parts parsed", size_t( 2 ), cids.size( ) ); + + RelatedPartPtr actualRoot = multipart.getPart( rootCid ); + CPPUNIT_ASSERT_MESSAGE( "No part corresponding to root cid", actualRoot.get( ) != NULL ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong root part content type", rootType, actualRoot->getContentType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong root part content", rootContent, actualRoot->getContent( ) ); + + RelatedPartPtr actualPart2 = multipart.getPart( part2Cid ); + CPPUNIT_ASSERT_MESSAGE( "No part corresponding to part2 cid", actualPart2.get( ) != NULL ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong part2 part content type", part2Type, actualPart2->getContentType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong part2 part content", part2Content, actualPart2->getContent( ) ); +} + +void SoapTest::getStreamFromNodeXopTest( ) +{ + // Create the test multipart + string dataCid = "http://data-cid"; + string dataCidEncoded = "http%3A%2F%2Fdata-cid"; + string dataContent = "Some transfered content"; + + string boundary = "------------ABCDEF-Boundary"; + string body = "\r\n--" + boundary + "\r\n" + + "Content-Id: <root-cid>\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + "Who cares? we assume, this has been properly extracted in this test" + + "\r\n--" + boundary + "\r\n" + + "Content-Id: " + dataCid + "\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + dataContent + + "\r\n--" + boundary + "--\r\n"; + + string contentType = string( "multipart/related;start=\"root-cid\";type=\"text/plain\";" ) + + "boundary=\"" + boundary + "\";start-info=\"info\""; + + RelatedMultipart multipart( body, contentType ); + + // Create test node + stringstream buf; + buf << "<stream>" + << " <xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" href=\"cid:" << dataCidEncoded << "\"/>" + << "</stream>"; + test::XmlNodeRef node = test::getXmlNode( buf.str( ) ); + + // Run the tested method + boost::shared_ptr< istream > stream = getStreamFromNode( node, multipart ); + + // Checks + stringstream out; + out << stream->rdbuf( ); + CPPUNIT_ASSERT_EQUAL( dataContent, out.str( ) ); +} + +void SoapTest::getStreamFromNodeBase64Test( ) +{ + // Create the test multipart + string boundary = "------------ABCDEF-Boundary"; + string body = "\r\n--" + boundary + "\r\n" + + "Content-Id: <root-cid>\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + "Who cares? we assume, this has been properly extracted in this test" + + "\r\n--" + boundary + "--\r\n"; + + string contentType = string( "multipart/related;start=\"root-cid\";type=\"text/plain\";" ) + + "boundary=\"" + boundary + "\";start-info=\"info\""; + + RelatedMultipart multipart( body, contentType ); + + // Create test node + string dataContent = "U29tZSB0cmFuc2ZlcmVkIGNvbnRlbnQ="; + string expectedContent = "Some transfered content"; + + stringstream buf; + buf << "<stream>" << dataContent << "</stream>"; + test::XmlNodeRef node = test::getXmlNode( buf.str( ) ); + + // Run the tested method + boost::shared_ptr< istream > stream = getStreamFromNode( node, multipart ); + + // Checks + stringstream out; + out << stream->rdbuf( ); + CPPUNIT_ASSERT_EQUAL( expectedContent, out.str( ) ); +} + +void SoapTest::writeCmismStreamTest( ) +{ + // Initialize the writer + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + // Test writeCmismStream + RelatedMultipart multipart; + string contentType( "text/plain" ); + string content( "Expected content" ); + string filename( "name.txt" ); + boost::shared_ptr< ostream > os( new stringstream( content ) ); + writeCmismStream( writer, multipart, os, contentType, filename ); + + // Close the writer and check the results + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + + vector< string > ids = multipart.getIds( ); + CPPUNIT_ASSERT_EQUAL( size_t( 1 ), ids.size( ) ); + string partId = ids.front( ); + + RelatedPartPtr part = multipart.getPart( partId ); + CPPUNIT_ASSERT_MESSAGE( "Missing stream related part", part.get( ) != NULL ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content not properly attached", content, part->getContent( ) ); + + stringstream expectedXml; + expectedXml << "<?xml version=\"1.0\"?>\n" + << "<cmism:length>" << content.size( ) << "</cmism:length>" + << "<cmism:mimeType>" << contentType << "</cmism:mimeType>" + << "<cmism:filename>" << filename << "</cmism:filename>" + << "<cmism:stream>" + << "<xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" href=\"cid:" << partId << "\"/>" + << "</cmism:stream>\n"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Envelope part isn't correct", expectedXml.str( ), str ); + + // Free it all + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); +} diff --git a/qa/libcmis/test-ws.cxx b/qa/libcmis/test-ws.cxx new file mode 100644 index 0000000..6857e63 --- /dev/null +++ b/qa/libcmis/test-ws.cxx @@ -0,0 +1,1505 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <memory> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#define protected public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <ws-session.hxx> +#include <ws-object-type.hxx> + +#include <mockup-config.h> +#include <test-helpers.hxx> +#include <test-mockup-helpers.hxx> + +#define SERVER_URL string( "http://mockup/ws" ) +#define SERVER_REPOSITORY string( "mock" ) +#define SERVER_USERNAME "tester" +#define SERVER_PASSWORD "somepass" + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace +{ + string lcl_getStreamAsString( boost::shared_ptr< istream > is ) + { + is->seekg( 0, ios::end ); + long size = is->tellg( ); + is->seekg( 0, ios::beg ); + + char* buf = new char[ size ]; + is->read( buf, size ); + string content( buf, size ); + delete[ ] buf; + + return content; + } + + string lcl_getCmisRequestXml( string url, const char* bodyMatch = NULL ) + { + const struct HttpRequest* request = curl_mockup_getRequest( url.c_str(), "", "POST", bodyMatch ); + char* contentType = curl_mockup_HttpRequest_getHeader( request, "Content-Type" ); + RelatedMultipart multipart( request->body, string( contentType ) ); + RelatedPartPtr part = multipart.getPart( multipart.getStartId() ); + string xml = part->getContent( ); + curl_mockup_HttpRequest_free( request ); + free( contentType ); + + string requestStr = test::getXmlNodeAsString( xml, "/soap-env:Envelope/soap-env:Body/child::*" ); + + // Obfuscate the xop:Include ids + string xopSearch = "<xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" href=\"cid:"; + size_t pos = requestStr.find( xopSearch ); + if ( pos != string::npos ) + { + pos = pos + xopSearch.size(); + size_t endPos = requestStr.find( "\"", pos ); + requestStr = requestStr.replace( pos, + endPos - pos, + "obfuscated" ); + } + return requestStr; + } + + string lcl_getExpectedNs( ) + { + string ns = " xmlns:cmis=\"http://docs.oasis-open.org/ns/cmis/core/200908/\"" + " xmlns:cmism=\"http://docs.oasis-open.org/ns/cmis/messaging/200908/\""; + return ns; + } +} + +typedef std::unique_ptr<WSSession> WSSessionPtr; + +class WSTest : public CppUnit::TestFixture +{ + public: + + void getRepositoriesTest( ); + void getRepositoryInfosTest( ); + void getRepositoryInfosBadTest( ); + + void getTypeTest( ); + void getTypeRefreshTest( ); + void objectTypeCopyTest( ); + void getUnexistantTypeTest( ); + void getTypeParentsTest( ); + void getTypeChildrenTest( ); + + void getObjectTest( ); + void getDocumentTest( ); + void getFolderTest( ); + void getByPathValidTest( ); + void getByPathInvalidTest( ); + void getDocumentParentsTest( ); + void getChildrenTest( ); + void getContentStreamTest( ); + void setContentStreamTest( ); + void getRenditionsTest( ); + void updatePropertiesTest( ); + void updatePropertiesEmptyTest( ); + void createFolderTest( ); + void createFolderBadTypeTest( ); + void createDocumentTest( ); + void deleteDocumentTest( ); + void deleteFolderTreeTest( ); + void moveTest( ); + void addSecondaryTypeTest( ); + + void checkOutTest( ); + void cancelCheckOutTest( ); + void checkInTest( ); + void getAllVersionsTest( ); + + void navigationServiceCopyTest(); + void repositoryServiceCopyTest(); + void objectServiceCopyTest(); + void versioningServiceCopyTest(); + + CPPUNIT_TEST_SUITE( WSTest ); + CPPUNIT_TEST( getRepositoriesTest ); + CPPUNIT_TEST( getRepositoryInfosTest ); + CPPUNIT_TEST( getRepositoryInfosBadTest ); + CPPUNIT_TEST( getTypeTest ); + CPPUNIT_TEST( getTypeRefreshTest ); + CPPUNIT_TEST( objectTypeCopyTest ); + CPPUNIT_TEST( getUnexistantTypeTest ); + CPPUNIT_TEST( getTypeParentsTest ); + CPPUNIT_TEST( getTypeChildrenTest ); + CPPUNIT_TEST( getObjectTest ); + CPPUNIT_TEST( getDocumentTest ); + CPPUNIT_TEST( getFolderTest ); + CPPUNIT_TEST( getByPathValidTest ); + CPPUNIT_TEST( getByPathInvalidTest ); + CPPUNIT_TEST( getDocumentParentsTest ); + CPPUNIT_TEST( getChildrenTest ); + CPPUNIT_TEST( getContentStreamTest ); + CPPUNIT_TEST( setContentStreamTest ); + CPPUNIT_TEST( getRenditionsTest ); + CPPUNIT_TEST( updatePropertiesTest ); + CPPUNIT_TEST( updatePropertiesEmptyTest ); + CPPUNIT_TEST( createFolderTest ); + CPPUNIT_TEST( createFolderBadTypeTest ); + CPPUNIT_TEST( createDocumentTest ); + CPPUNIT_TEST( deleteDocumentTest ); + CPPUNIT_TEST( deleteFolderTreeTest ); + CPPUNIT_TEST( moveTest ); + CPPUNIT_TEST( addSecondaryTypeTest ); + CPPUNIT_TEST( checkOutTest ); + CPPUNIT_TEST( cancelCheckOutTest ); + CPPUNIT_TEST( checkInTest ); + CPPUNIT_TEST( getAllVersionsTest ); + CPPUNIT_TEST( navigationServiceCopyTest ); + CPPUNIT_TEST( repositoryServiceCopyTest ); + CPPUNIT_TEST( objectServiceCopyTest ); + CPPUNIT_TEST( versioningServiceCopyTest ); + CPPUNIT_TEST_SUITE_END( ); + + libcmis::RepositoryPtr getTestRepository( ); + WSSessionPtr getTestSession( string username, string password, bool noRepos = false ); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION( WSTest ); + +void WSTest::getRepositoriesTest() +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/repositories.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + map< string, string > actual = session->getRepositoryService().getRepositories( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of repositories", size_t( 1 ), actual.size( ) ); + + // Test the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/RepositoryService" ); + string expectedRequest = "<cmism:getRepositories" + lcl_getExpectedNs() + "/>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getRepositoryInfosTest() +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/repository-infos.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + string validId = "mock"; + libcmis::RepositoryPtr actual = session->getRepositoryService().getRepositoryInfo( validId ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Root folder is wrong", string( "root-folder" ), actual->getRootId( ) ); + + // Test the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/RepositoryService" ); + string expectedRequest = "<cmism:getRepositoryInfo" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>" + validId + "</cmism:repositoryId>" + "</cmism:getRepositoryInfo>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getRepositoryInfosBadTest() +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/repository-infos-bad.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + string badId = "bad"; + try + { + session->getRepositoryService().getRepositoryInfo( badId ); + } + catch( const libcmis::Exception& e ) + { + // Test the caught exception + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong exception type", string( "invalidArgument" ), e.getType( ) ); + + // Test the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/RepositoryService" ); + string expectedRequest = "<cmism:getRepositoryInfo" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>" + badId + "</cmism:repositoryId>" + "</cmism:getRepositoryInfo>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); + } + +} + +void WSTest::getTypeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + string id( "cmis:folder" ); + libcmis::ObjectTypePtr actual = session->getType( id ); + + // Check the returned type + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id", id, actual->getId( ) ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/RepositoryService" ); + string expectedRequest = "<cmism:getTypeDefinition" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:typeId>" + id + "</cmism:typeId>" + "</cmism:getTypeDefinition>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getTypeRefreshTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", + DATA_DIR "/ws/type-docLevel2.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + string id( "DocumentLevel2" ); + libcmis::ObjectTypePtr actual = session->getType( id ); + + // Check the returned type + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id", id, actual->getId( ) ); + + test::addWsResponse( "http://mockup/ws/services/RepositoryService", + DATA_DIR "/ws/type-docLevel1.http" ); + + + // Do the refresh + actual->refresh(); + + // Check the refreshed object + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id", string( "DocumentLevel1" ), + actual->getId( ) ); +} + +void WSTest::objectTypeCopyTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + string id( "cmis:folder" ); + libcmis::ObjectTypePtr expected = session->getType( id ); + WSObjectType* type = dynamic_cast< WSObjectType* >( expected.get( ) ); + + { + WSObjectType copy( *type ); + + CPPUNIT_ASSERT_EQUAL( type->getId( ), copy.getId( ) ); + CPPUNIT_ASSERT_EQUAL( type->m_session, copy.m_session ); + } + + { + WSObjectType copy; + copy = *type; + + CPPUNIT_ASSERT_EQUAL( type->getId( ), copy.getId( ) ); + CPPUNIT_ASSERT_EQUAL( type->m_session, copy.m_session ); + } +} + +void WSTest::getUnexistantTypeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-bad.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + string id( "bad_type" ); + try + { + session->getType( id ); + } + catch ( const libcmis::Exception& e ) + { + // Check the caught exception + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong error type", string( "objectNotFound" ), e.getType() ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/RepositoryService" ); + string expectedRequest = "<cmism:getTypeDefinition" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:typeId>" + id + "</cmism:typeId>" + "</cmism:getTypeDefinition>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); + } +} + +void WSTest::getTypeParentsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", + DATA_DIR "/ws/type-docLevel2.http", + "<cmism:typeId>DocumentLevel2</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", + DATA_DIR "/ws/type-docLevel1.http", + "<cmism:typeId>DocumentLevel1</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", + DATA_DIR "/ws/type-document.http", + "<cmism:typeId>cmis:document</cmism:typeId>" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "DocumentLevel2"; + libcmis::ObjectTypePtr actual = session->getType( id ); + + // Check the resulting type + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Parent type Id", string( "DocumentLevel1" ), + actual->getParentTypeId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Base type Id", string( "cmis:document" ), + actual->getBaseTypeId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Parent type", string( "DocumentLevel1" ), + actual->getParentType()->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Base type", string( "cmis:document" ), + actual->getBaseType()->getId( ) ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/RepositoryService" ); + string expectedRequest = "<cmism:getTypeDefinition" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:typeId>" + id + "</cmism:typeId>" + "</cmism:getTypeDefinition>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getTypeChildrenTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", + DATA_DIR "/ws/typechildren-document.http", + "<cmism:getTypeChildren "); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", + DATA_DIR "/ws/type-document.http", + "<cmism:typeId>cmis:document</cmism:typeId>" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "cmis:document"; + libcmis::ObjectTypePtr actual = session->getType( id ); + vector< libcmis::ObjectTypePtr > children = actual->getChildren(); + + // Check the actual children returned + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of children", size_t( 1 ), children.size( ) ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/RepositoryService", + "<cmism:getTypeChildren " ); + string expectedRequest = "<cmism:getTypeChildren" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:typeId>" + id + "</cmism:typeId>" + "<cmism:includePropertyDefinitions>true</cmism:includePropertyDefinitions>" + "</cmism:getTypeChildren>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getObjectTest( ) +{ + // Setup the mockup + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/valid-object.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // Run the tested method + string expectedId( "valid-object" ); + libcmis::ObjectPtr actual = session->getObject( expectedId ); + + // Check the returned object + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Id for fetched object", + expectedId, actual->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong type for fetched object", + string( "cmis:folder" ), actual->getType() ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService" ); + string expectedRequest = "<cmism:getObject" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + expectedId + "</cmism:objectId>" + "<cmism:includeAllowableActions>true</cmism:includeAllowableActions>" + "<cmism:renditionFilter>*</cmism:renditionFilter>" + "</cmism:getObject>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getDocumentTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string expectedId( "test-document" ); + libcmis::ObjectPtr actual = session->getObject( expectedId ); + + // Do we have a document? + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( actual ); + CPPUNIT_ASSERT_MESSAGE( "Fetched object should be an instance of libcmis::DocumentPtr", + NULL != document ); + + // Check the document properties + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document ID", expectedId, document->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document name", string( "Test Document" ), document->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong document type", string( "text/plain" ), document->getContentType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:document" ), document->getBaseType( ) ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !document->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !document->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModifiedBy is missing", !document->getLastModifiedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !document->getLastModificationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "ChangeToken is missing", !document->getChangeToken( ).empty( ) ); + + CPPUNIT_ASSERT_MESSAGE( "Content length is missing", 12345 == document->getContentLength( ) ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService" ); + string expectedRequest = "<cmism:getObject" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + expectedId + "</cmism:objectId>" + "<cmism:includeAllowableActions>true</cmism:includeAllowableActions>" + "<cmism:renditionFilter>*</cmism:renditionFilter>" + "</cmism:getObject>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getFolderTest( ) +{ + // Setup the mockup + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/valid-object.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // Run the method under test + string expectedId( "valid-object" ); + libcmis::FolderPtr actual = session->getFolder( expectedId ); + + // Check the returned folder + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder ID", expectedId, actual->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder name", string( "Valid Object" ), actual->getName( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong folder path", string( "/Valid Object" ), actual->getPath( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong base type", string( "cmis:folder" ), actual->getBaseType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Missing folder parent ID", + string( "root-folder" ), actual->getParentId() ); + CPPUNIT_ASSERT_MESSAGE( "Not a root folder", !actual->isRootFolder() ); + + CPPUNIT_ASSERT_MESSAGE( "CreatedBy is missing", !actual->getCreatedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "CreationDate is missing", !actual->getCreationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "LastModifiedBy is missing", !actual->getLastModifiedBy( ).empty( ) ); + CPPUNIT_ASSERT_MESSAGE( "LastModificationDate is missing", !actual->getLastModificationDate( ).is_not_a_date_time() ); + CPPUNIT_ASSERT_MESSAGE( "ChangeToken is missing", !actual->getChangeToken( ).empty( ) ); + + // No need to check the request: we do the same one in another test +} + +void WSTest::getByPathValidTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/valid-object.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string path = "/Valid Object"; + libcmis::ObjectPtr actual = session->getObjectByPath( path ); + + // Check the returned object + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong Id for fetched object", string( "valid-object" ), actual->getId( ) ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService" ); + string expectedRequest = "<cmism:getObjectByPath" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:path>" + path + "</cmism:path>" + "<cmism:includeAllowableActions>true</cmism:includeAllowableActions>" + "<cmism:renditionFilter>*</cmism:renditionFilter>" + "</cmism:getObjectByPath>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getByPathInvalidTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/getbypath-bad.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string path = "/some/invalid/path"; + try + { + session->getObjectByPath( path ); + CPPUNIT_FAIL( "Exception should be thrown: invalid Path" ); + } + catch ( const libcmis::Exception& e ) + { + // Check the caught exception + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong error type", string( "objectNotFound" ), e.getType() ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService" ); + string expectedRequest = "<cmism:getObjectByPath" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:path>" + path + "</cmism:path>" + "<cmism:includeAllowableActions>true</cmism:includeAllowableActions>" + "<cmism:renditionFilter>*</cmism:renditionFilter>" + "</cmism:getObjectByPath>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); + } + +} + +void WSTest::getDocumentParentsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + test::addWsResponse( "http://mockup/ws/services/NavigationService", DATA_DIR "/ws/test-document-parents.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "test-document"; + vector< libcmis::FolderPtr > actual = session->getNavigationService(). + getObjectParents( session->m_repositoryId, + id ); + + // Check the actual parents + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad number of parents", size_t( 2 ), actual.size() ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/NavigationService" ); + string expectedRequest = "<cmism:getObjectParents" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "<cmism:includeAllowableActions>true</cmism:includeAllowableActions>" + "<cmism:renditionFilter>*</cmism:renditionFilter>" + "</cmism:getObjectParents>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getChildrenTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http", "<cmism:typeId>cmis:folder</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http", "<cmism:typeId>DocumentLevel2</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/NavigationService", DATA_DIR "/ws/root-children.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + + string id = "root-folder"; + vector< libcmis::ObjectPtr > children = session->getNavigationService(). + getChildren( session->m_repositoryId, + id ); + + // Check the returned children + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of children", size_t( 5 ), children.size() ); + + int folderCount = 0; + int documentCount = 0; + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end( ); ++it ) + { + if ( NULL != boost::dynamic_pointer_cast< libcmis::Folder >( *it ) ) + ++folderCount; + else if ( NULL != boost::dynamic_pointer_cast< libcmis::Document >( *it ) ) + ++documentCount; + } + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of folder children", 2, folderCount ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of document children", 3, documentCount ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/NavigationService" ); + string expectedRequest = "<cmism:getChildren" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:folderId>" + id + "</cmism:folderId>" + "<cmism:includeAllowableActions>true</cmism:includeAllowableActions>" + "<cmism:renditionFilter>*</cmism:renditionFilter>" + "</cmism:getChildren>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getContentStreamTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/get-content-stream.http", "<cmism:getContentStream " ); + + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + boost::shared_ptr< istream > is = document->getContentStream( ); + + // Check the fetched content + string actualContent = lcl_getStreamAsString( is ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Content stream doesn't match", + string( "Some content stream" ), actualContent ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:getContentStream " ); + string expectedRequest = "<cmism:getContentStream" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "</cmism:getContentStream>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::setContentStreamTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/set-content-stream.http", "<cmism:setContentStream " ); + curl_mockup_addResponse( "http://mockup/mock/content/data.txt", "id=test-document", "PUT", "Updated", 0, false ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::DocumentPtr document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + try + { + string oldChangeToken = object->getChangeToken( ); + string expectedContent( "Some content stream to set" ); + boost::shared_ptr< ostream > os ( new stringstream ( expectedContent ) ); + string filename( "name.txt" ); + string contentType( "text/plain" ); + document->setContentStream( os, contentType, filename, true ); + + CPPUNIT_ASSERT_MESSAGE( "Object not refreshed during setContentStream", object->getRefreshTimestamp( ) > 0 ); + // We do not check the change token as we are lazy + // That would require to write another answer file for the refresh + + // Check the sent request + ostringstream converter; + converter << expectedContent.size( ); + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:setContentStream " ); + string expectedRequest = "<cmism:setContentStream" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "<cmism:overwriteFlag>true</cmism:overwriteFlag>" + "<cmism:changeToken>" + oldChangeToken + "</cmism:changeToken>" + "<cmism:contentStream>" + "<cmism:length>" + converter.str() + "</cmism:length>" + "<cmism:mimeType>" + contentType + "</cmism:mimeType>" + "<cmism:filename>" + filename + "</cmism:filename>" + "<cmism:stream>" + "<xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" " + "href=\"cid:obfuscated\"/>" + "</cmism:stream>" + "</cmism:contentStream>" + "</cmism:setContentStream>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); + } + catch ( const libcmis::Exception& e ) + { + string msg = "Unexpected exception: "; + msg += e.what(); + CPPUNIT_FAIL( msg.c_str() ); + } +} + +void WSTest::getRenditionsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/get-renditions.http", "<cmism:getRenditions " ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string expectedId( "test-document" ); + libcmis::ObjectPtr actual = session->getObject( expectedId ); + + std::vector< libcmis::RenditionPtr > renditions = actual->getRenditions( ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad renditions count", size_t( 2 ), renditions.size( ) ); + + libcmis::RenditionPtr rendition = renditions[1]; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition mime type", string( "application/pdf" ), rendition->getMimeType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition stream id", string( "test-document-rendition2" ), rendition->getStreamId() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition length - default case", long( -1 ), rendition->getLength( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition Title", string( "Doc as PDF" ), rendition->getTitle( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition kind", string( "pdf" ), rendition->getKind( ) ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad rendition length - filled case", long( 40385 ), renditions[0]->getLength( ) ); +} + +void WSTest::updatePropertiesTest( ) +{ + curl_mockup_reset( ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/update-properties.http", "<cmism:updateProperties " ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // Values for the test + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + string propertyName( "cmis:name" ); + string expectedValue( "New name" ); + + // Fill the map of properties to change + PropertyPtrMap newProperties; + + libcmis::ObjectTypePtr objectType = object->getTypeDescription( ); + map< string, libcmis::PropertyTypePtr >::iterator it = objectType->getPropertiesTypes( ).find( propertyName ); + vector< string > values; + values.push_back( expectedValue ); + libcmis::PropertyPtr property( new libcmis::Property( it->second, values ) ); + newProperties[ propertyName ] = property; + + // Change the object response to provide the updated values + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document-updated.http", "<cmism:getObject " ); + + // Update the properties (method to test) + libcmis::ObjectPtr updated = object->updateProperties( newProperties ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:updateProperties " ); + string expectedRequest = "<cmism:updateProperties" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "<cmism:changeToken>some-change-token</cmism:changeToken>" + "<cmism:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>New name</cmis:value>" + "</cmis:propertyString>" + "</cmism:properties>" + "</cmism:updateProperties>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); + + // Check that the properties are updated after the call + PropertyPtrMap::iterator propIt = updated->getProperties( ).find( propertyName ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong value after refresh", expectedValue, propIt->second->getStrings().front( ) ); +} + +void WSTest::updatePropertiesEmptyTest( ) +{ + curl_mockup_reset( ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // Values for the test + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + + // Just leave the map empty and update + PropertyPtrMap emptyProperties; + libcmis::ObjectPtr updated = object->updateProperties( emptyProperties ); + + // Check that no HTTP request was sent + int count = curl_mockup_getRequestsCount( "http://mockup/ws/services/ObjectService", + "", "POST", "<cmism:updateProperties" ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "No HTTP request should have been sent", 0, count ); + + // Check that the object we got is the same than previous one + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong change token", object->getChangeToken(), updated->getChangeToken() ); +} + +void WSTest::createFolderTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/root-folder.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/create-folder.http", "<cmism:createFolder " ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + libcmis::FolderPtr parent = session->getRootFolder( ); + + // Prepare the properties for the new object, object type is cmis:folder + PropertyPtrMap props; + libcmis::ObjectTypePtr type = session->getType( "cmis:folder" ); + map< string, libcmis::PropertyTypePtr > propTypes = type->getPropertiesTypes( ); + + // Set the object name + string expectedName( "create folder" ); + map< string, libcmis::PropertyTypePtr >::iterator it = propTypes.find( string( "cmis:name" ) ); + vector< string > nameValues; + nameValues.push_back( expectedName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( it->second, nameValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // set the object type + it = propTypes.find( string( "cmis:objectTypeId" ) ); + vector< string > typeValues; + typeValues.push_back( "cmis:folder" ); + libcmis::PropertyPtr typeProperty( new libcmis::Property( it->second, typeValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeProperty ) ); + + // Set the mockup to send the updated folder now that we had the parent + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/created-folder.http", "<cmism:getObject " ); + + // Actually send the folder creation request + libcmis::FolderPtr created = parent->createFolder( props ); + + // Check that something came back + CPPUNIT_ASSERT_MESSAGE( "Change token shouldn't be empty: object should have been refreshed", + !created->getChangeToken( ).empty() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong name", expectedName, created->getName( ) ); + + // Check that the proper request has been sent + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:createFolder " ); + string expectedRequest = "<cmism:createFolder" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>create folder</cmis:value>" + "</cmis:propertyString>" + "<cmis:propertyId propertyDefinitionId=\"cmis:objectTypeId\" localName=\"cmis:objectTypeId\"" + " displayName=\"Type-Id\" queryName=\"cmis:objectTypeId\">" + "<cmis:value>cmis:folder</cmis:value>" + "</cmis:propertyId>" + "</cmism:properties>" + "<cmism:folderId>root-folder</cmism:folderId>" + "</cmism:createFolder>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::createFolderBadTypeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/root-folder.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/create-folder-bad-type.http", "<cmism:createFolder " ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + libcmis::FolderPtr parent = session->getRootFolder( ); + + // Prepare the properties for the new object, object type is cmis:folder + PropertyPtrMap props; + libcmis::ObjectTypePtr type = session->getType( "cmis:folder" ); + map< string, libcmis::PropertyTypePtr > propTypes = type->getPropertiesTypes( ); + + // Set the object name + string expectedName( "create folder" ); + map< string, libcmis::PropertyTypePtr >::iterator it = propTypes.find( string( "cmis:name" ) ); + vector< string > nameValues; + nameValues.push_back( expectedName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( it->second, nameValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // set the object type + it = propTypes.find( string( "cmis:objectTypeId" ) ); + vector< string > typeValues; + typeValues.push_back( "cmis:document" ); + libcmis::PropertyPtr typeProperty( new libcmis::Property( it->second, typeValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeProperty ) ); + + // Actually send the folder creation request + try + { + libcmis::FolderPtr created = parent->createFolder( props ); + CPPUNIT_FAIL( "Should not succeed to return a folder" ); + } + catch ( libcmis::Exception& e ) + { + // Check the caught exception + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong error type", string( "constraint" ), e.getType() ); + + // Check that the proper request has been sent + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:createFolder " ); + string expectedRequest = "<cmism:createFolder" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>create folder</cmis:value>" + "</cmis:propertyString>" + "<cmis:propertyId propertyDefinitionId=\"cmis:objectTypeId\" localName=\"cmis:objectTypeId\"" + " displayName=\"Type-Id\" queryName=\"cmis:objectTypeId\">" + "<cmis:value>cmis:document</cmis:value>" + "</cmis:propertyId>" + "</cmism:properties>" + "<cmism:folderId>root-folder</cmism:folderId>" + "</cmism:createFolder>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); + } +} + +void WSTest::createDocumentTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/create-document.http", "<cmism:createDocument " ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/root-folder.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + libcmis::FolderPtr parent = session->getRootFolder( ); + + // Make the mockup know about cmis:document now + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-document.http" ); + + // Prepare the properties for the new object, object type is cmis:folder + PropertyPtrMap props; + libcmis::ObjectTypePtr type = session->getType( "cmis:document" ); + map< string, libcmis::PropertyTypePtr > propTypes = type->getPropertiesTypes( ); + + // Set the object name + string expectedName( "create document" ); + + map< string, libcmis::PropertyTypePtr >::iterator it = propTypes.find( string( "cmis:name" ) ); + vector< string > nameValues; + nameValues.push_back( expectedName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( it->second, nameValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // set the object type + it = propTypes.find( string( "cmis:objectTypeId" ) ); + vector< string > typeValues; + typeValues.push_back( "cmis:document" ); + libcmis::PropertyPtr typeProperty( new libcmis::Property( it->second, typeValues ) ); + props.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeProperty ) ); + + // Make the mockup able to send the response to update the object + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/created-document.http", "<cmism:getObject " ); + + // Actually send the document creation request + string content = "Some content"; + boost::shared_ptr< ostream > os ( new stringstream( content ) ); + string contentType = "text/plain"; + string filename( "name.txt" ); + libcmis::DocumentPtr created = parent->createDocument( props, os, contentType, filename ); + + // Check that something came back + CPPUNIT_ASSERT_MESSAGE( "Change token shouldn't be empty: object should have been refreshed", + !created->getChangeToken( ).empty() ); + + // Check that the name is ok + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong name set", expectedName, created->getName( ) ); + + // Check that the sent request is the expected one + ostringstream converter; + converter << content.size( ); + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:createDocument " ); + string expectedRequest = "<cmism:createDocument" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:name\" localName=\"cmis:name\" " + "displayName=\"Name\" queryName=\"cmis:name\">" + "<cmis:value>create document</cmis:value>" + "</cmis:propertyString>" + "<cmis:propertyId propertyDefinitionId=\"cmis:objectTypeId\" localName=\"cmis:objectTypeId\"" + " displayName=\"Type-Id\" queryName=\"cmis:objectTypeId\">" + "<cmis:value>cmis:document</cmis:value>" + "</cmis:propertyId>" + "</cmism:properties>" + "<cmism:folderId>root-folder</cmism:folderId>" + "<cmism:contentStream>" + "<cmism:length>" + converter.str( ) + "</cmism:length>" + "<cmism:mimeType>" + contentType + "</cmism:mimeType>" + "<cmism:filename>" + filename + "</cmism:filename>" + "<cmism:stream>" + "<xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" " + "href=\"cid:obfuscated\"/>" + "</cmism:stream>" + "</cmism:contentStream>" + "</cmism:createDocument>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::deleteDocumentTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/delete-object.http", "<cmism:deleteObject " ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get() ); + + // Run the tested method. Here we delete the object with all its versions + document->remove( true ); + + // Check that the proper request has been sent + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:deleteObject " ); + string expectedRequest = "<cmism:deleteObject" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "<cmism:allVersions>true</cmism:allVersions>" + "</cmism:deleteObject>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::deleteFolderTreeTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/valid-object.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/delete-tree.http", "<cmism:deleteTree " ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "valid-object"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( object.get() ); + + vector<string> failed = folder->removeTree( true, libcmis::UnfileObjects::Delete, false ); + + // Check that we had the failed ids + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong ids for non-deleted objects", + string( "bad-delete" ), failed[0] ); + + // Test the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:deleteTree " ); + string expectedRequest = "<cmism:deleteTree" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:folderId>valid-object</cmism:folderId>" + "<cmism:allVersions>true</cmism:allVersions>" + "<cmism:unfileObjects>delete</cmism:unfileObjects>" + "<cmism:continueOnFailure>false</cmism:continueOnFailure>" + "</cmism:deleteTree>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::moveTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-folder.http", "<cmism:typeId>cmis:folder</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http", "<cmism:typeId>DocumentLevel2</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/NavigationService", DATA_DIR "/ws/test-document-parents.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/move-object.http", "<cmism:moveObject " ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get() ); + + string destFolderId = "valid-object"; + libcmis::FolderPtr src = document->getParents( ).front( ); + + // Tell the mockup about the destination folder + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/valid-object.http", "<cmism:getObject " ); + libcmis::FolderPtr dest = session->getFolder( destFolderId ); + + document->move( src, dest ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:moveObject " ); + string expectedRequest = "<cmism:moveObject" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "<cmism:targetFolderId>" + destFolderId + "</cmism:targetFolderId>" + "<cmism:sourceFolderId>" + src->getId( ) + "</cmism:sourceFolderId>" + "</cmism:moveObject>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::addSecondaryTypeTest( ) +{ + curl_mockup_reset( ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/secondary-type.http", + "<cmism:typeId>secondary-type</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2-secondary.http", + "<cmism:typeId>DocumentLevel2</cmism:typeId>" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:getObject " ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/update-properties.http", "<cmism:updateProperties " ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // Values for the test + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + string secondaryType = "secondary-type"; + string propertyName = "secondary-prop"; + string expectedValue = "some-value"; + + // Fill the map of properties to change + PropertyPtrMap newProperties; + + libcmis::ObjectTypePtr objectType = session->getType( secondaryType ); + map< string, libcmis::PropertyTypePtr >::iterator it = objectType->getPropertiesTypes( ).find( propertyName ); + vector< string > values; + values.push_back( expectedValue ); + libcmis::PropertyPtr property( new libcmis::Property( it->second, values ) ); + newProperties[ propertyName ] = property; + + // Change the object response to provide the updated values + test::addWsResponse( "http://mockup/ws/services/ObjectService", + DATA_DIR "/ws/test-document-add-secondary.http", + "<cmism:getObject " ); + + // add the secondary type (method to test) + libcmis::ObjectPtr updated = object->addSecondaryType( secondaryType, newProperties ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/ObjectService", "<cmism:updateProperties " ); + string expectedRequest = "<cmism:updateProperties" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "<cmism:changeToken>some-change-token</cmism:changeToken>" + "<cmism:properties>" + "<cmis:propertyString propertyDefinitionId=\"cmis:secondaryObjectTypeIds\"" + " localName=\"cmis:secondaryObjectTypeIds\"" + " displayName=\"cmis:secondaryObjectTypeIds\"" + " queryName=\"cmis:secondaryObjectTypeIds\">" + "<cmis:value>" + secondaryType + "</cmis:value>" + "</cmis:propertyString>" + "<cmis:propertyString propertyDefinitionId=\"" + propertyName + "\"" + " localName=\"" + propertyName + "\"" + " displayName=\"" + propertyName + "\"" + " queryName=\"" + propertyName + "\">" + "<cmis:value>" + expectedValue + "</cmis:value>" + "</cmis:propertyString>" + "</cmism:properties>" + "</cmism:updateProperties>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); + + // Check that the properties are updated after the call + PropertyPtrMap::iterator propIt = updated->getProperties( ).find( propertyName ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong value after refresh", expectedValue, propIt->second->getStrings().front( ) ); +} + +void WSTest::checkOutTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http", "<cmism:objectId>test-document</cmism:objectId>" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/working-copy.http", "<cmism:objectId>working-copy</cmism:objectId>" ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/VersioningService", DATA_DIR "/ws/checkout.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get() ); + + libcmis::DocumentPtr pwc = document->checkOut( ); + + // Check that we have a PWC + CPPUNIT_ASSERT_MESSAGE( "Missing returned Private Working Copy", pwc.get( ) != NULL ); + + PropertyPtrMap::iterator it = pwc->getProperties( ).find( string( "cmis:isVersionSeriesCheckedOut" ) ); + vector< bool > values = it->second->getBools( ); + CPPUNIT_ASSERT_MESSAGE( "cmis:isVersionSeriesCheckedOut isn't true", values.front( ) ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/VersioningService" ); + string expectedRequest = "<cmism:checkOut" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "</cmism:checkOut>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::cancelCheckOutTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/working-copy.http" ); + test::addWsResponse( "http://mockup/ws/services/VersioningService", DATA_DIR "/ws/cancel-checkout.http" ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // First get a checked out document + string id = "working-copy"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::DocumentPtr pwc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + pwc->cancelCheckout( ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/VersioningService" ); + string expectedRequest = "<cmism:cancelCheckOut" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "</cmism:cancelCheckOut>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::checkInTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/working-copy.http", "<cmism:objectId>working-copy</cmism:objectId>" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/checked-in.http", "<cmism:objectId>test-document</cmism:objectId>" ); + test::addWsResponse( "http://mockup/ws/services/VersioningService", DATA_DIR "/ws/checkin.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // First get a checked out document + string id = "working-copy"; + libcmis::ObjectPtr object = session->getObject( id ); + libcmis::DocumentPtr pwc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + // Do the checkin + bool isMajor = true; + string comment( "Some check-in comment" ); + PropertyPtrMap properties; + string newContent = "Some New content to check in"; + boost::shared_ptr< ostream > stream ( new stringstream( newContent ) ); + string contentType = "text/plain"; + string filename = "filename.txt"; + libcmis::DocumentPtr updated = pwc->checkIn( isMajor, comment, properties, + stream, contentType, filename ); + + // Check that we had the new version + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong filename: probably not the new version", + filename, updated->getContentFilename() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong commit comment: not the new version", + comment, updated->getStringProperty( "cmis:checkinComment" ) ); + + // Check the sent request + ostringstream converter; + converter << newContent.size( ); + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/VersioningService" ); + string expectedRequest = "<cmism:checkIn" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + id + "</cmism:objectId>" + "<cmism:major>true</cmism:major>" + "<cmism:properties/>" + "<cmism:contentStream>" + "<cmism:length>" + converter.str() + "</cmism:length>" + "<cmism:mimeType>" + contentType + "</cmism:mimeType>" + "<cmism:filename>" + filename + "</cmism:filename>" + "<cmism:stream>" + "<xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\" " + "href=\"cid:obfuscated\"/>" + "</cmism:stream>" + "</cmism:contentStream>" + "<cmism:checkinComment>" + comment + "</cmism:checkinComment>" + "</cmism:checkIn>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::getAllVersionsTest( ) +{ + curl_mockup_reset( ); + curl_mockup_setCredentials( SERVER_USERNAME, SERVER_PASSWORD ); + test::addWsResponse( "http://mockup/ws/services/RepositoryService", DATA_DIR "/ws/type-docLevel2.http" ); + test::addWsResponse( "http://mockup/ws/services/ObjectService", DATA_DIR "/ws/test-document.http" ); + test::addWsResponse( "http://mockup/ws/services/VersioningService", DATA_DIR "/ws/get-versions.http" ); + + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + + // First get a document + string id = "test-document"; + libcmis::ObjectPtr object = session->getObject( id ); + string seriesId = object->getStringProperty( "cmis:versionSeriesId" ); + libcmis::DocumentPtr doc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + // Get all the versions (method to check) + vector< libcmis::DocumentPtr > versions = doc->getAllVersions( ); + + // Checks + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of versions", size_t( 2 ), versions.size( ) ); + + // Check the sent request + string xmlRequest = lcl_getCmisRequestXml( "http://mockup/ws/services/VersioningService" ); + string expectedRequest = "<cmism:getAllVersions" + lcl_getExpectedNs() + ">" + "<cmism:repositoryId>mock</cmism:repositoryId>" + "<cmism:objectId>" + seriesId + "</cmism:objectId>" + "<cmism:includeAllowableActions>true</cmism:includeAllowableActions>" + "</cmism:getAllVersions>"; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong request sent", expectedRequest, xmlRequest ); +} + +void WSTest::navigationServiceCopyTest() +{ + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + NavigationService service( session.get() ); + + { + NavigationService copy( service ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } + + { + NavigationService copy; + copy = service; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } +} + +void WSTest::repositoryServiceCopyTest() +{ + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + RepositoryService service( session.get() ); + + { + RepositoryService copy( service ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } + + { + RepositoryService copy; + copy = service; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } +} + +void WSTest::objectServiceCopyTest() +{ + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + ObjectService service( session.get() ); + + { + ObjectService copy( service ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } + + { + ObjectService copy; + copy = service; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } +} + +void WSTest::versioningServiceCopyTest() +{ + WSSessionPtr session = getTestSession( SERVER_USERNAME, SERVER_PASSWORD, true ); + VersioningService service( session.get() ); + + { + VersioningService copy( service ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } + + { + VersioningService copy; + copy = service; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Session not copied", service.m_session, copy.m_session ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "URL not copied", service.m_url, copy.m_url ); + } +} + +WSSessionPtr WSTest::getTestSession( string username, string password, bool noRepos ) +{ + WSSessionPtr session( new WSSession( ) ); + session->m_username = username; + session->m_password = password; + + string buf; + test::loadFromFile( DATA_DIR "/ws/CMISWS-Service.wsdl", buf ); + session->parseWsdl( buf ); + session->initializeResponseFactory( ); + + // Manually define the repositories to avoid the HTTP query + if ( noRepos ) + { + libcmis::RepositoryPtr repo = getTestRepository( ); + session->m_repositories.push_back( repo ); + session->m_repositoryId = repo->getId( ); + } + + return session; +} + +libcmis::RepositoryPtr WSTest::getTestRepository() +{ + libcmis::RepositoryPtr repo( new libcmis::Repository( ) ); + repo->m_id = "mock"; + repo->m_name = "Mockup"; + repo->m_description = "Repository sent by mockup server"; + repo->m_vendorName = "libcmis"; + repo->m_productName = "Libcmis mockup"; + repo->m_productVersion = "some-version"; + repo->m_cmisVersionSupported = "1.1"; + repo->m_rootId = "root-folder"; + + map< libcmis::Repository::Capability, string > capabilities; + capabilities[libcmis::Repository::ACL] = "manage"; + capabilities[libcmis::Repository::AllVersionsSearchable] = "false"; + capabilities[libcmis::Repository::Changes] = "none"; + capabilities[libcmis::Repository::ContentStreamUpdatability] = "anytime"; + capabilities[libcmis::Repository::GetDescendants] = "true"; + capabilities[libcmis::Repository::GetFolderTree] = "true"; + capabilities[libcmis::Repository::Multifiling] = "true"; + capabilities[libcmis::Repository::PWCSearchable] = "false"; + capabilities[libcmis::Repository::PWCUpdatable] = "true"; + capabilities[libcmis::Repository::Query] = "bothcombined"; + capabilities[libcmis::Repository::Renditions] = "read"; + capabilities[libcmis::Repository::Unfiling] = "true"; + capabilities[libcmis::Repository::VersionSpecificFiling] = "false"; + capabilities[libcmis::Repository::Join] = "none"; + repo->m_capabilities = capabilities; + + return repo; +} diff --git a/qa/libcmis/test-xmlutils.cxx b/qa/libcmis/test-xmlutils.cxx new file mode 100644 index 0000000..e4d5339 --- /dev/null +++ b/qa/libcmis/test-xmlutils.cxx @@ -0,0 +1,626 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <ctime> +#include <sstream> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestFixture.h> +#include <cppunit/TestAssert.h> +#include <cppunit/ui/text/TestRunner.h> +#include <libxml/tree.h> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif +#define private public +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <libcmis/object-type.hxx> +#include <libcmis/property.hxx> +#include <libcmis/property-type.hxx> +#include <libcmis/xml-utils.hxx> + +#include "test-helpers.hxx" + +using namespace boost; +using namespace std; +using namespace test; + +class XmlTest : public CppUnit::TestFixture +{ + public: + + // Parser tests + void parseDateTimeTest( ); + void parseBoolTest( ); + void parseIntegerTest( ); + void parseDoubleTest( ); + + void parsePropertyStringTest( ); + void parsePropertyIntegerTest( ); + void parsePropertyDateTimeTest( ); + void parsePropertyBoolTest( ); + + void parseEmptyPropertyTest( ); + void parsePropertyNoTypeTest( ); + + void parseRenditionTest( ); + void parseRepositoryCapabilitiesTest( ); + + // Writer tests + void propertyStringAsXmlTest( ); + void propertyIntegerAsXmlTest( ); + + // Other tests + void sha1Test( ); + void propertyTypeUpdateTest( ); + void escapeTest( ); + void unescapeTest( ); + + CPPUNIT_TEST_SUITE( XmlTest ); + CPPUNIT_TEST( parseDateTimeTest ); + CPPUNIT_TEST( parseBoolTest ); + CPPUNIT_TEST( parseIntegerTest ); + CPPUNIT_TEST( parseDoubleTest ); + CPPUNIT_TEST( parsePropertyStringTest ); + CPPUNIT_TEST( parsePropertyIntegerTest ); + CPPUNIT_TEST( parsePropertyDateTimeTest ); + CPPUNIT_TEST( parsePropertyBoolTest ); + CPPUNIT_TEST( parseEmptyPropertyTest ); + CPPUNIT_TEST( parsePropertyNoTypeTest ); + CPPUNIT_TEST( parseRenditionTest ); + CPPUNIT_TEST( parseRepositoryCapabilitiesTest ); + CPPUNIT_TEST( propertyStringAsXmlTest ); + CPPUNIT_TEST( propertyIntegerAsXmlTest ); + CPPUNIT_TEST( sha1Test ); + CPPUNIT_TEST( propertyTypeUpdateTest ); + CPPUNIT_TEST( escapeTest ); + CPPUNIT_TEST( unescapeTest ); + CPPUNIT_TEST_SUITE_END( ); +}; + +/** Dummy ObjectType implementation used as a PropertyType factory. + */ +class ObjectTypeDummy : public libcmis::ObjectType +{ + public: + ObjectTypeDummy( ); + virtual ~ObjectTypeDummy() { }; +}; + +ObjectTypeDummy::ObjectTypeDummy( ) +{ + // String Property + { + libcmis::PropertyTypePtr prop ( new libcmis::PropertyType( ) ); + prop->setId( string( "STR-ID" ) ); + prop->setLocalName( string( "LOCAL" ) ); + prop->setDisplayName( string( "DISPLAY" ) ); + prop->setQueryName( string( "QUERY" ) ); + prop->setTypeFromXml( "string" ); + + m_propertiesTypes.insert( pair< string, libcmis::PropertyTypePtr >( prop->getId( ), prop ) ); + } + + // Integer Property + { + libcmis::PropertyTypePtr prop ( new libcmis::PropertyType( ) ); + prop->setId( string( "INT-ID" ) ); + prop->setLocalName( string( "LOCAL" ) ); + prop->setDisplayName( string( "DISPLAY" ) ); + prop->setQueryName( string( "QUERY" ) ); + prop->setTypeFromXml( "integer" ); + + m_propertiesTypes.insert( pair< string, libcmis::PropertyTypePtr >( prop->getId( ), prop ) ); + } + + // DateTime Property + { + libcmis::PropertyTypePtr prop ( new libcmis::PropertyType( ) ); + prop->setId( string( "DATE-ID" ) ); + prop->setLocalName( string( "LOCAL" ) ); + prop->setDisplayName( string( "DISPLAY" ) ); + prop->setQueryName( string( "QUERY" ) ); + prop->setTypeFromXml( "datetime" ); + + m_propertiesTypes.insert( pair< string, libcmis::PropertyTypePtr >( prop->getId( ), prop ) ); + } + + // Boolean Property + { + libcmis::PropertyTypePtr prop ( new libcmis::PropertyType( ) ); + prop->setId( string( "BOOL-ID" ) ); + prop->setLocalName( string( "LOCAL" ) ); + prop->setDisplayName( string( "DISPLAY" ) ); + prop->setQueryName( string( "QUERY" ) ); + prop->setTypeFromXml( "boolean" ); + + m_propertiesTypes.insert( pair< string, libcmis::PropertyTypePtr >( prop->getId( ), prop ) ); + } +} + +void XmlTest::parseDateTimeTest( ) +{ + tm basis; + basis.tm_year = 2011 - 1900; + basis.tm_mon = 8; // Months are in 0..11 range + basis.tm_mday = 28; + basis.tm_hour = 12; + basis.tm_min = 44; + basis.tm_sec = 28; + + // Broken strings + { + posix_time::ptime expected( boost::date_time::not_a_date_time ); + + posix_time::ptime t = libcmis::parseDateTime( string() ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Broken time string case failed #1", + expected, t ); + + char toParse[50]; + strftime( toParse, sizeof( toParse ), "%FT", &basis ); + t = libcmis::parseDateTime( string( toParse ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Broken time string case failed #2", + expected, t ); + + strftime( toParse, sizeof( toParse ), "T%T", &basis ); + t = libcmis::parseDateTime( string( toParse ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Broken time string case failed #3", + expected, t ); + + strftime( toParse, sizeof( toParse ), "%T", &basis ); + t = libcmis::parseDateTime( string( toParse ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Broken time string case failed #4", + expected, t ); + } + + // No time zone test + { + char toParse[50]; + strftime( toParse, sizeof( toParse ), "%FT%T", &basis ); + posix_time::ptime t = libcmis::parseDateTime( string( toParse ) ); + + gregorian::date expDate( basis.tm_year + 1900, basis.tm_mon + 1, basis.tm_mday ); + posix_time::time_duration expTime( basis.tm_hour, basis.tm_min, basis.tm_sec ); + posix_time::ptime expected( expDate, expTime ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "No time zone case failed", expected, t ); + } + + // Z time zone test + { + char toParse[50]; + strftime( toParse, sizeof( toParse ), "%FT%TZ", &basis ); + posix_time::ptime t = libcmis::parseDateTime( string( toParse ) ); + + gregorian::date expDate( basis.tm_year + 1900, basis.tm_mon + 1, basis.tm_mday ); + posix_time::time_duration expTime( basis.tm_hour, basis.tm_min, basis.tm_sec ); + posix_time::ptime expected( expDate, expTime ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Z time zone case failed", expected, t ); + } + + // +XX:XX time zone test + { + char toParse[50]; + strftime( toParse, sizeof( toParse ), "%FT%T+02:00", &basis ); + posix_time::ptime t = libcmis::parseDateTime( string( toParse ) ); + + gregorian::date expDate( basis.tm_year + 1900, basis.tm_mon + 1, basis.tm_mday ); + posix_time::time_duration expTime( basis.tm_hour + 2, basis.tm_min, basis.tm_sec ); + posix_time::ptime expected( expDate, expTime ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "+XX:XX time zone case failed", expected, t ); + } + + // -XX:XX time zone test + { + char toParse[50]; + strftime( toParse, sizeof( toParse ), "%FT%T-02:00", &basis ); + posix_time::ptime t = libcmis::parseDateTime( string( toParse ) ); + + gregorian::date expDate( basis.tm_year + 1900, basis.tm_mon + 1, basis.tm_mday ); + posix_time::time_duration expTime( basis.tm_hour - 2, basis.tm_min, basis.tm_sec ); + posix_time::ptime expected( expDate, expTime ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "+XX:XX time zone case failed", expected, t ); + } + + // Error test + { + posix_time::ptime t = libcmis::parseDateTime( string( "Nothing interesting 9990" ) ); + CPPUNIT_ASSERT_MESSAGE( "Error case failed", t.is_not_a_date_time( ) ); + } +} + +void XmlTest::parseBoolTest( ) +{ + // 'true' test + { + bool result = libcmis::parseBool( string( "true" ) ); + CPPUNIT_ASSERT_MESSAGE( "'true' can't be parsed properly", result ); + } + + // '1' test + { + bool result = libcmis::parseBool( string( "1" ) ); + CPPUNIT_ASSERT_MESSAGE( "'1' can't be parsed properly", result ); + } + + // 'false' test + { + bool result = libcmis::parseBool( string( "false" ) ); + CPPUNIT_ASSERT_MESSAGE( "'false' can't be parsed properly", !result ); + } + + // '0' test + { + bool result = libcmis::parseBool( string( "0" ) ); + CPPUNIT_ASSERT_MESSAGE( "'0' can't be parsed properly", !result ); + } + + // Error test + { + try + { + libcmis::parseBool( string( "boolcheat" ) ); + CPPUNIT_FAIL( "Exception should be thrown" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad exception message", + string( "Invalid xsd:boolean input: boolcheat" ), string( e.what() ) ); + } + } +} + +void XmlTest::parseIntegerTest( ) +{ + // Positive value test + { + long result = libcmis::parseInteger( string( "123" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Positive integer can't be parsed properly", 123L, result ); + } + + // Negative value test + { + long result = libcmis::parseInteger( string( "-123" ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Negative integer can't be parsed properly", -123L, result ); + } + + // Overflow error test + { + try + { + libcmis::parseInteger( string( "9999999999999999999" ) ); + CPPUNIT_FAIL( "Exception should be thrown" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad exception message", + string( "xsd:integer input can't fit to long: 9999999999999999999" ), string( e.what() ) ); + } + } + + // Non integer test + { + try + { + libcmis::parseInteger( string( "123bad" ) ); + CPPUNIT_FAIL( "Exception should be thrown" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad exception message", + string( "Invalid xsd:integer input: 123bad" ), string( e.what() ) ); + } + } +} + +void XmlTest::parseDoubleTest( ) +{ + // Positive value test + { + double result = libcmis::parseDouble( string( "123.456" ) ); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Positive decimal can't be parsed properly", 123.456, result, 0.000001 ); + } + + // Negative value test + { + double result = libcmis::parseDouble( string( "-123.456" ) ); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Negative decimal can't be parsed properly", -123.456, result, 0.000001 ); + } + + // Non integer test + { + try + { + libcmis::parseDouble( string( "123.456bad" ) ); + CPPUNIT_FAIL( "Exception should be thrown" ); + } + catch ( const libcmis::Exception& e ) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Bad exception message", + string( "Invalid xsd:decimal input: 123.456bad" ), string( e.what() ) ); + } + } +} + +void XmlTest::parsePropertyStringTest( ) +{ + stringstream buf; + buf << "<cmis:propertyString " << getXmlns( ) + << "propertyDefinitionId=\"STR-ID\">" + << "<cmis:value>VALUE 1</cmis:value>" + << "<cmis:value>VALUE 2</cmis:value>" + << "</cmis:propertyString>"; + libcmis::ObjectTypePtr dummy( new ObjectTypeDummy( ) ); + libcmis::PropertyPtr actual = libcmis::parseProperty( getXmlNode( buf.str( ) ), dummy ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id parsed", string( "STR-ID" ), actual->getPropertyType( )->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of values parsed", vector<string>::size_type( 2 ), actual->getStrings( ).size( ) ); +} + +void XmlTest::parsePropertyIntegerTest( ) +{ + stringstream buf; + buf << "<cmis:propertyInteger " << getXmlns( ) + << "propertyDefinitionId=\"INT-ID\">" + << "<cmis:value>12345</cmis:value>" + << "<cmis:value>67890</cmis:value>" + << "</cmis:propertyInteger>"; + libcmis::ObjectTypePtr dummy( new ObjectTypeDummy( ) ); + libcmis::PropertyPtr actual = libcmis::parseProperty( getXmlNode( buf.str( ) ), dummy ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id parsed", string( "INT-ID" ), actual->getPropertyType()->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of values parsed", vector<string>::size_type( 2 ), actual->getLongs( ).size( ) ); +} + +void XmlTest::parsePropertyDateTimeTest( ) +{ + stringstream buf; + buf << "<cmis:propertyDateTime " << getXmlns( ) + << "propertyDefinitionId=\"DATE-ID\">" + << "<cmis:value>2012-01-19T09:06:57.388Z</cmis:value>" + << "<cmis:value>2011-01-19T09:06:57.388Z</cmis:value>" + << "</cmis:propertyDateTime>"; + libcmis::ObjectTypePtr dummy( new ObjectTypeDummy( ) ); + libcmis::PropertyPtr actual = libcmis::parseProperty( getXmlNode( buf.str( ) ), dummy ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id parsed", string( "DATE-ID" ), actual->getPropertyType()->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of values parsed", vector<string>::size_type( 2 ), actual->getDateTimes( ).size( ) ); +} + +void XmlTest::parsePropertyBoolTest( ) +{ + stringstream buf; + buf << "<cmis:propertyBoolean " << getXmlns( ) + << "propertyDefinitionId=\"BOOL-ID\">" + << "<cmis:value>true</cmis:value>" + << "</cmis:propertyBoolean>"; + libcmis::ObjectTypePtr dummy( new ObjectTypeDummy( ) ); + libcmis::PropertyPtr actual = libcmis::parseProperty( getXmlNode( buf.str( ) ), dummy ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id parsed", string( "BOOL-ID" ), actual->getPropertyType()->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of values parsed", vector<string>::size_type( 1 ), actual->getBools( ).size( ) ); +} + +void XmlTest::parseEmptyPropertyTest( ) +{ + stringstream buf; + buf << "<cmis:propertyId " << getXmlns( ) << "propertyDefinitionId=\"STR-ID\" />"; + libcmis::ObjectTypePtr dummy( new ObjectTypeDummy( ) ); + libcmis::PropertyPtr actual = libcmis::parseProperty( getXmlNode( buf.str( ) ), dummy ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id parsed", string( "STR-ID" ), actual->getPropertyType()->getId( ) ); + CPPUNIT_ASSERT_MESSAGE( "Should have no value", actual->getStrings( ).empty( ) ); +} + +void XmlTest::parsePropertyNoTypeTest( ) +{ + stringstream buf; + buf << "<cmis:propertyDateTime " << getXmlns( ) + << "propertyDefinitionId=\"DATE-ID\">" + << "<cmis:value>2012-01-19T09:06:57.388Z</cmis:value>" + << "<cmis:value>2011-01-19T09:06:57.388Z</cmis:value>" + << "</cmis:propertyDateTime>"; + libcmis::ObjectTypePtr noType; + libcmis::PropertyPtr actual = libcmis::parseProperty( getXmlNode( buf.str( ) ), noType ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong id parsed", string( "DATE-ID" ), actual->getPropertyType()->getId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong number of values parsed", vector<string>::size_type( 2 ), actual->getDateTimes( ).size( ) ); +} + +void XmlTest::parseRenditionTest( ) +{ + stringstream buf; + buf << "<cmis:rendition " << getXmlns( ) << ">" + << "<cmis:streamId>STREAM-ID</cmis:streamId>" + << "<cmis:mimetype>MIME</cmis:mimetype>" + << "<cmis:length>123456</cmis:length>" + << "<cmis:kind>KIND</cmis:kind>" + << "<cmis:title>TITLE</cmis:title>" + << "<cmis:height>123</cmis:height>" + << "<cmis:width>456</cmis:width>" + << "<cmis:renditionDocumentId>DOC-ID</cmis:renditionDocumentId>" + << "</cmis:rendition>"; + libcmis::RenditionPtr actual( new libcmis::Rendition( getXmlNode( buf.str( ) ) ) ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong stream id parsed", string( "STREAM-ID" ), actual->getStreamId( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong mime type parsed", string( "MIME" ), actual->getMimeType( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong length parsed", long( 123456 ), actual->getLength( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong kind parsed", string( "KIND" ), actual->getKind( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong title parsed", string( "TITLE" ), actual->getTitle( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong height parsed", long( 123 ), actual->getHeight( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong width parsed", long( 456 ), actual->getWidth( ) ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Wrong rendition doc id parsed", string( "DOC-ID" ), actual->getRenditionDocumentId( ) ); +} + +string lcl_findCapability( map< libcmis::Repository::Capability, string > store, libcmis::Repository::Capability capability ) +{ + string result; + map< libcmis::Repository::Capability, string >::iterator it = store.find( capability ); + if ( it != store.end( ) ) + result = it->second; + + return result; +} + +void XmlTest::parseRepositoryCapabilitiesTest( ) +{ + stringstream buf; + buf << "<cmis:capabilities " << getXmlns( ) << ">" + << "<cmis:capabilityACL>manage</cmis:capabilityACL>" + << "<cmis:capabilityAllVersionsSearchable>false</cmis:capabilityAllVersionsSearchable>" + << "<cmis:capabilityChanges>none</cmis:capabilityChanges>" + << "<cmis:capabilityContentStreamUpdatability>anytime</cmis:capabilityContentStreamUpdatability>" + << "<cmis:capabilityGetDescendants>true</cmis:capabilityGetDescendants>" + << "<cmis:capabilityGetFolderTree>true</cmis:capabilityGetFolderTree>" + << "<cmis:capabilityOrderBy>common</cmis:capabilityOrderBy>" + << "<cmis:capabilityMultifiling>true</cmis:capabilityMultifiling>" + << "<cmis:capabilityPWCSearchable>false</cmis:capabilityPWCSearchable>" + << "<cmis:capabilityPWCUpdatable>true</cmis:capabilityPWCUpdatable>" + << "<cmis:capabilityQuery>bothcombined</cmis:capabilityQuery>" + << "<cmis:capabilityRenditions>read</cmis:capabilityRenditions>" + << "<cmis:capabilityUnfiling>false</cmis:capabilityUnfiling>" + << "<cmis:capabilityVersionSpecificFiling>false</cmis:capabilityVersionSpecificFiling>" + << "<cmis:capabilityJoin>none</cmis:capabilityJoin>" + << "</cmis:capabilities>"; + + map< libcmis::Repository::Capability, string > capabilities = libcmis::Repository::parseCapabilities( getXmlNode( buf.str( ) ) ); + + CPPUNIT_ASSERT_EQUAL( string( "manage" ), lcl_findCapability( capabilities, libcmis::Repository::ACL ) ); + CPPUNIT_ASSERT_EQUAL( string( "false" ), lcl_findCapability( capabilities, libcmis::Repository::AllVersionsSearchable ) ); + CPPUNIT_ASSERT_EQUAL( string( "none" ), lcl_findCapability( capabilities, libcmis::Repository::Changes ) ); + CPPUNIT_ASSERT_EQUAL( string( "anytime" ), lcl_findCapability( capabilities, libcmis::Repository::ContentStreamUpdatability ) ); + CPPUNIT_ASSERT_EQUAL( string( "true" ), lcl_findCapability( capabilities, libcmis::Repository::GetDescendants ) ); + CPPUNIT_ASSERT_EQUAL( string( "true" ), lcl_findCapability( capabilities, libcmis::Repository::GetFolderTree ) ); + CPPUNIT_ASSERT_EQUAL( string( "common" ), lcl_findCapability( capabilities, libcmis::Repository::OrderBy ) ); + CPPUNIT_ASSERT_EQUAL( string( "true" ), lcl_findCapability( capabilities, libcmis::Repository::Multifiling ) ); + CPPUNIT_ASSERT_EQUAL( string( "false" ), lcl_findCapability( capabilities, libcmis::Repository::PWCSearchable ) ); + CPPUNIT_ASSERT_EQUAL( string( "true" ), lcl_findCapability( capabilities, libcmis::Repository::PWCUpdatable ) ); + CPPUNIT_ASSERT_EQUAL( string( "bothcombined" ), lcl_findCapability( capabilities, libcmis::Repository::Query ) ); + CPPUNIT_ASSERT_EQUAL( string( "read" ), lcl_findCapability( capabilities, libcmis::Repository::Renditions ) ); + CPPUNIT_ASSERT_EQUAL( string( "false" ), lcl_findCapability( capabilities, libcmis::Repository::Unfiling ) ); + CPPUNIT_ASSERT_EQUAL( string( "false" ), lcl_findCapability( capabilities, libcmis::Repository::VersionSpecificFiling ) ); + CPPUNIT_ASSERT_EQUAL( string( "none" ), lcl_findCapability( capabilities, libcmis::Repository::Join ) ); +} + +void XmlTest::propertyStringAsXmlTest( ) +{ + vector< string > values; + values.push_back( string( "Value 1" ) ); + values.push_back( string( "Value 2" ) ); + + ObjectTypeDummy factory; + map< string, libcmis::PropertyTypePtr >::iterator it = factory.getPropertiesTypes( ).find( "STR-ID" ); + CPPUNIT_ASSERT_MESSAGE( "Missing property type to setup fixture", it != factory.getPropertiesTypes( ).end() ); + libcmis::PropertyPtr property( new libcmis::Property( it->second, values ) ); + + string actual = writeXml( property ); + + stringstream expected; + expected << "<?xml version=\"1.0\"?>\n" + << "<cmis:propertyString propertyDefinitionId=\"STR-ID\" localName=\"LOCAL\" displayName=\"DISPLAY\" queryName=\"QUERY\">" + << "<cmis:value>Value 1</cmis:value>" + << "<cmis:value>Value 2</cmis:value>" + << "</cmis:propertyString>\n"; + + CPPUNIT_ASSERT_EQUAL( expected.str( ), actual ); +} + +void XmlTest::propertyIntegerAsXmlTest( ) +{ + vector< string > values; + values.push_back( string( "123" ) ); + values.push_back( string( "456" ) ); + + ObjectTypeDummy factory; + map< string, libcmis::PropertyTypePtr >::iterator it = factory.getPropertiesTypes( ).find( "INT-ID" ); + CPPUNIT_ASSERT_MESSAGE( "Missing property type to setup fixture", it != factory.getPropertiesTypes( ).end() ); + libcmis::PropertyPtr property( new libcmis::Property( it->second, values ) ); + + string actual = writeXml( property ); + + stringstream expected; + expected << "<?xml version=\"1.0\"?>\n" + << "<cmis:propertyInteger propertyDefinitionId=\"INT-ID\" localName=\"LOCAL\" displayName=\"DISPLAY\" queryName=\"QUERY\">" + << "<cmis:value>123</cmis:value>" + << "<cmis:value>456</cmis:value>" + << "</cmis:propertyInteger>\n"; + + CPPUNIT_ASSERT_EQUAL( expected.str( ), actual ); +} + +void XmlTest::sha1Test( ) +{ + { + string actual = libcmis::sha1( "Hello" ); + CPPUNIT_ASSERT_EQUAL( string( "f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0" ), actual ); + } + + { + // check correct width + string actual = libcmis::sha1( "35969137" ); + CPPUNIT_ASSERT_EQUAL( string( "0d93546909cfeb5c00089202104df3980000ec9f" ), actual ); + } +} + +void XmlTest::propertyTypeUpdateTest( ) +{ + libcmis::PropertyType propDef( "datetime", "DATE-ID", "", "", "" ); + + libcmis::ObjectTypePtr dummy( new ObjectTypeDummy( ) ); + vector< libcmis::ObjectTypePtr > typeDefs; + typeDefs.push_back( dummy ); + + propDef.update( typeDefs ); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not updated", + string( "LOCAL" ), propDef.getLocalName( ) ); + CPPUNIT_ASSERT_MESSAGE( "Property Type still marked temporary", + !propDef.m_temporary ); +} + +void XmlTest::escapeTest( ) +{ + string actual = libcmis::escape("something to escape$"); + CPPUNIT_ASSERT_EQUAL( string("something%20to%20escape%24"), actual); +} + +void XmlTest::unescapeTest( ) +{ + string actual = libcmis::unescape("something%20to%20escape%24"); + CPPUNIT_ASSERT_EQUAL( string("something to escape$"), actual); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( XmlTest ); diff --git a/qa/mockup/Makefile.am b/qa/mockup/Makefile.am new file mode 100644 index 0000000..ef34e12 --- /dev/null +++ b/qa/mockup/Makefile.am @@ -0,0 +1,15 @@ +if !OS_WIN32 +noinst_LTLIBRARIES = libcmis-mockup.la + +libcmis_mockup_la_SOURCES = \ + curl-mockup.cxx \ + internals.hxx \ + mockup-config.cxx \ + mockup-config.h \ + curl/curl.h + +libcmis_mockup_la_LIBADD = \ + $(top_builddir)/src/libcmis/libcmis.la + +libcmis_mockup_la_LDFLAGS = -export-dynamic -no-undefined +endif diff --git a/qa/mockup/curl-mockup.cxx b/qa/mockup/curl-mockup.cxx new file mode 100644 index 0000000..0a74a64 --- /dev/null +++ b/qa/mockup/curl-mockup.cxx @@ -0,0 +1,504 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sstream> + +#include "curl/curl.h" +#include "internals.hxx" + +using namespace std; + +namespace mockup +{ + extern Configuration* config; +} + +/** Code mostly coming from curl + */ +struct curl_slist *curl_slist_append( struct curl_slist * list, const char * data ) +{ + struct curl_slist* new_item = ( struct curl_slist* ) malloc( sizeof( struct curl_slist ) ); + + if( new_item ) + { + char *dupdata = strdup(data); + if ( dupdata ) + { + new_item->next = NULL; + new_item->data = dupdata; + } + else + { + free(new_item); + return NULL; + } + } + else + return NULL; + + if ( list ) + { + curl_slist* last = list; + while ( last->next ) + last = last->next; + last->next = new_item; + return list; + } + + /* if this is the first item, then new_item *is* the list */ + return new_item; +} + +void curl_slist_free_all( struct curl_slist * list ) +{ + if ( list ) + { + struct curl_slist* item = list; + struct curl_slist* next = NULL; + do + { + next = item->next; + if ( item->data ) + free( item->data ); + free(item); + item = next; + } while ( next ); + } +} + +void curl_free( void * p ) +{ + free( p ); +} + +CURLcode curl_global_init( long ) +{ + return CURLE_OK; +} + +CURL *curl_easy_init( void ) +{ + return new CurlHandle(); +} + +void curl_easy_cleanup( CURL * curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + delete( handle ); +} + +void curl_easy_reset( CURL * curl ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + handle->reset( ); +} + +char *curl_easy_escape( CURL *, const char *string, int length ) +{ + return strndup( string, length ); +} + +char *curl_unescape( const char *string, int length ) +{ + return strndup( string, length ); +} + +char *curl_easy_unescape( CURL *, const char *string, int length, int * ) +{ + return curl_unescape( string, length ); +} + +CURLcode curl_easy_setopt( CURL * curl, long option, ... ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + + va_list arg; + va_start( arg, option ); + switch ( option ) + { + /* TODO Add support for more options */ + case CURLOPT_POST: + { + if ( va_arg( arg, long ) ) + handle->m_method = string( "POST" ); + break; + } + case CURLOPT_UPLOAD: + { + if ( 0 != va_arg( arg, long ) ) + handle->m_method = string( "PUT" ); + break; + } + case CURLOPT_CUSTOMREQUEST: + handle->m_method = string( va_arg( arg, char* ) ); + break; + case CURLOPT_URL: + { + handle->m_url = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_WRITEFUNCTION: + { + handle->m_writeFn = va_arg( arg, write_callback ); + break; + } + case CURLOPT_WRITEDATA: + { + handle->m_writeData = va_arg( arg, void* ); + break; + } + case CURLOPT_READFUNCTION: + { + handle->m_readFn = va_arg( arg, read_callback ); + break; + } + case CURLOPT_READDATA: + { + handle->m_readData = va_arg( arg, void* ); + break; + } + case CURLOPT_INFILESIZE: + { + handle->m_readSize = va_arg( arg, long ); + break; + } + case CURLOPT_HEADERFUNCTION: + { + handle->m_headersFn = va_arg( arg, headers_callback ); + break; + } + case CURLOPT_WRITEHEADER: + { + handle->m_headersData = va_arg( arg, void* ); + break; + } + case CURLOPT_USERNAME: + { + handle->m_username = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PASSWORD: + { + handle->m_password = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_USERPWD: + { + string userpwd( va_arg( arg, char* ) ); + size_t pos = userpwd.find( ':' ); + if ( pos != string::npos ) + { + handle->m_username = userpwd.substr( 0, pos ); + handle->m_password = userpwd.substr( pos + 1 ); + } + break; + } + case CURLOPT_PROXY: + { + // FIXME curl does some more complex things with port and type + handle->m_proxy = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_NOPROXY: + { + handle->m_noProxy = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PROXYUSERNAME: + { + handle->m_proxyUser = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PROXYPASSWORD: + { + handle->m_proxyPass = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PROXYUSERPWD: + { + string userpwd( va_arg( arg, char* ) ); + size_t pos = userpwd.find( ':' ); + if ( pos != string::npos ) + { + handle->m_proxyUser = userpwd.substr( 0, pos ); + handle->m_proxyPass = userpwd.substr( pos + 1 ); + } + break; + } + case CURLOPT_SSL_VERIFYHOST: + { + handle->m_verifyHost = va_arg( arg, long ) != 0; + break; + } + case CURLOPT_SSL_VERIFYPEER: + { + handle->m_verifyPeer = va_arg( arg, long ) != 0; + break; + } + case CURLOPT_CERTINFO: + { + handle->m_certInfo = va_arg( arg, long ); + break; + } + case CURLOPT_HTTPHEADER: + { + handle->m_headers.clear(); + struct curl_slist* headers = va_arg( arg, struct curl_slist* ); + while ( headers != NULL ) + { + handle->m_headers.push_back( string( headers->data ) ); + headers = headers->next; + } + break; + } + default: + { + // We surely don't want to break the test for that. + } + } + va_end( arg ); + + return CURLE_OK; +} + +CURLcode curl_easy_perform( CURL * curl ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + + /* Fake a bad SSL Certificate? */ + if ( !mockup::config->m_badSSLCertificate.empty( ) && handle->m_verifyPeer && handle->m_verifyHost ) + { + return CURLE_SSL_CACERT; + } + + /* Populate the certificate infos */ + if ( handle->m_certInfo && !mockup::config->m_badSSLCertificate.empty( ) ) + { + curl_slist * certData = NULL; + string cert( "Cert:" + mockup::config->m_badSSLCertificate ); + char* c_cert = strdup( cert.c_str( ) ); + certData = curl_slist_append( certData, c_cert ); + free( c_cert ); + + handle->m_certs.num_of_certs = 1; + handle->m_certs.certinfo = ( struct curl_slist** )calloc( ( size_t )1, sizeof( struct curl_slist * ) ); + handle->m_certs.certinfo[0] = certData; + } + + /* Check the credentials */ + if ( mockup::config->hasCredentials( ) && + ( handle->m_username != mockup::config->m_username || + handle->m_password != mockup::config->m_password ) ) + { + // Send HTTP 401 + handle->m_httpError = 401; + return CURLE_HTTP_RETURNED_ERROR; + } + + // Store the requests for later verifications + stringstream body; + if ( handle->m_readFn && handle->m_readData ) + { + size_t bufSize = 2048; + char* buf = new char[bufSize]; + + size_t read = 0; + do + { + read = handle->m_readFn( buf, 1, bufSize, handle->m_readData ); + body.write( buf, read ); + } while ( read == bufSize ); + + delete[] buf; + } + + mockup::config->m_requests.push_back( mockup::Request( handle->m_url, handle->m_method, body.str( ), handle->m_headers ) ); + + + return mockup::config->writeResponse( handle ); +} + +CURLcode curl_easy_getinfo( CURL * curl, long info, ... ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + + va_list arg; + va_start( arg, info ); + switch ( info ) + { + case CURLINFO_RESPONSE_CODE: + { + long* buf = va_arg( arg, long* ); + *buf = handle->m_httpError; + break; + } + case CURLINFO_CERTINFO: + { + struct curl_slist** param = va_arg( arg, struct curl_slist** ); + if ( NULL != param ) + { + union + { + struct curl_certinfo * to_certinfo; + struct curl_slist * to_slist; + } ptr; + + // Fill it + ptr.to_certinfo = &handle->m_certs; + *param = ptr.to_slist; + } + break; + } + default: + { + // We surely don't want to break the test for that. + } + } + va_end( arg ); + + return CURLE_OK; +} + +CurlHandle::CurlHandle( ) : + m_url( ), + m_writeFn( NULL ), + m_writeData( NULL ), + m_readFn( NULL ), + m_readData( NULL ), + m_readSize( 0 ), + m_headersFn( NULL ), + m_headersData( NULL ), + m_username( ), + m_password( ), + m_proxy( ), + m_noProxy( ), + m_proxyUser( ), + m_proxyPass( ), + m_verifyHost( true ), + m_verifyPeer( true ), + m_certInfo( false ), + m_certs( ), + m_httpError( 0 ), + m_method( "GET" ), + m_headers( ) +{ +} + +CurlHandle::CurlHandle( const CurlHandle& copy ) : + m_url( copy.m_url ), + m_writeFn( copy.m_writeFn ), + m_writeData( copy.m_writeData ), + m_readFn( copy.m_readFn ), + m_readData( copy.m_readData ), + m_readSize( copy.m_readSize ), + m_headersFn( copy.m_headersFn ), + m_headersData( copy.m_headersData ), + m_username( copy.m_username ), + m_password( copy.m_password ), + m_proxy( copy.m_proxy ), + m_noProxy( copy.m_noProxy ), + m_proxyUser( copy.m_proxyUser ), + m_proxyPass( copy.m_proxyPass ), + m_verifyHost( copy.m_verifyHost ), + m_verifyPeer( copy.m_verifyPeer ), + m_certInfo( copy.m_certInfo ), + m_certs( copy.m_certs ), + m_httpError( copy.m_httpError ), + m_method( copy.m_method ), + m_headers( copy.m_headers ) +{ +} + +CurlHandle& CurlHandle::operator=( const CurlHandle& copy ) +{ + if ( this != © ) + { + m_url = copy.m_url; + m_writeFn = copy.m_writeFn; + m_writeData = copy.m_writeData; + m_readFn = copy.m_readFn; + m_readData = copy.m_readData; + m_readSize = copy.m_readSize; + m_headersFn = copy.m_headersFn; + m_headersData = copy.m_headersData; + m_username = copy.m_username; + m_password = copy.m_password; + m_proxy = copy.m_proxy; + m_noProxy = copy.m_noProxy; + m_proxyUser = copy.m_proxyUser; + m_proxyPass = copy.m_proxyPass; + m_verifyHost = copy.m_verifyHost; + m_verifyPeer = copy.m_verifyPeer; + m_certInfo = copy.m_certInfo; + m_certs = copy.m_certs; + m_httpError = copy.m_httpError; + m_method = copy.m_method; + m_headers = copy.m_headers; + } + return *this; +} + +CurlHandle::~CurlHandle( ) +{ + reset(); +} + +void CurlHandle::reset( ) +{ + m_url = string( ); + m_writeFn = NULL; + m_writeData = NULL; + m_readFn = NULL; + m_readData = NULL; + m_readSize = 0; + m_username = string( ); + m_password = string( ); + m_proxy = string( ); + m_noProxy = string( ); + m_proxyUser = string( ); + m_proxyPass = string( ); + m_verifyHost = true; + m_verifyPeer = true; + m_certInfo = false; + + for ( int i = 0; i < m_certs.num_of_certs; ++i ) + { + curl_slist_free_all( m_certs.certinfo[i] ); + m_certs.certinfo[i] = NULL; + } + free( m_certs.certinfo ); + m_certs.certinfo = NULL; + m_certs.num_of_certs = 0; + + m_method = "GET"; + m_headers.clear( ); +} diff --git a/qa/mockup/curl/curl.h b/qa/mockup/curl/curl.h new file mode 100644 index 0000000..50f1cfe --- /dev/null +++ b/qa/mockup/curl/curl.h @@ -0,0 +1,153 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _MOCKUP_CURL_CURL_H_ +#define _MOCKUP_CURL_CURL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Curl used symbols to mockup */ + +typedef void CURL; + +typedef enum +{ + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) + +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +typedef enum +{ + CURLOPT_WRITEFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 11, + CURLOPT_READFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 12, + CURLOPT_WRITEDATA = CURLOPTTYPE_OBJECTPOINT + 1, + CURLOPT_HEADERFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 79, + CURLOPT_WRITEHEADER = CURLOPTTYPE_OBJECTPOINT + 29, + CURLOPT_FOLLOWLOCATION = CURLOPTTYPE_LONG + 52, + CURLOPT_MAXREDIRS = CURLOPTTYPE_LONG + 68, + CURLOPT_INFILESIZE = CURLOPTTYPE_LONG + 14, + CURLOPT_READDATA = CURLOPTTYPE_OBJECTPOINT + 9, + CURLOPT_UPLOAD = CURLOPTTYPE_LONG + 46, + CURLOPT_IOCTLFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 130, + CURLOPT_IOCTLDATA = CURLOPTTYPE_OBJECTPOINT + 131, + CURLOPT_HTTPHEADER = CURLOPTTYPE_OBJECTPOINT + 23, + CURLOPT_POSTFIELDSIZE = CURLOPTTYPE_LONG + 60, + CURLOPT_POST = CURLOPTTYPE_LONG + 47, + CURLOPT_CUSTOMREQUEST = CURLOPTTYPE_OBJECTPOINT + 36, + CURLOPT_URL = CURLOPTTYPE_OBJECTPOINT + 2, + CURLOPT_HTTPAUTH = CURLOPTTYPE_LONG + 107, + CURLOPT_USERNAME = CURLOPTTYPE_OBJECTPOINT + 173, + CURLOPT_PASSWORD = CURLOPTTYPE_OBJECTPOINT + 174, + CURLOPT_USERPWD = CURLOPTTYPE_OBJECTPOINT + 5, + CURLOPT_ERRORBUFFER = CURLOPTTYPE_OBJECTPOINT + 10, + CURLOPT_FAILONERROR = CURLOPTTYPE_LONG + 45, + CURLOPT_VERBOSE = CURLOPTTYPE_LONG + 41, + CURLOPT_PROXY = CURLOPTTYPE_OBJECTPOINT + 4, + CURLOPT_PROXYUSERPWD = CURLOPTTYPE_OBJECTPOINT + 6, + CURLOPT_PROXYAUTH = CURLOPTTYPE_LONG + 111, + CURLOPT_PROXYUSERNAME = CURLOPTTYPE_OBJECTPOINT + 175, + CURLOPT_PROXYPASSWORD = CURLOPTTYPE_OBJECTPOINT + 176, + CURLOPT_NOPROXY = CURLOPTTYPE_OBJECTPOINT + 177, + CURLOPT_SSL_VERIFYPEER = CURLOPTTYPE_LONG + 64, + CURLOPT_SSL_VERIFYHOST = CURLOPTTYPE_LONG + 81, + CURLOPT_CERTINFO = CURLOPTTYPE_LONG + 172 +} CURLoption; + +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) + +typedef enum +{ + CURLE_OK = 0, + CURLE_HTTP_RETURNED_ERROR = 22, + CURLE_SSL_CACERT = 60, + /* TODO Add some more error codes from curl? */ + CURL_LAST +} CURLcode; + +struct curl_slist +{ + char *data; + struct curl_slist *next; +}; + +struct curl_slist *curl_slist_append( struct curl_slist *, const char * ); +void curl_slist_free_all( struct curl_slist * ); + +void curl_free( void *p ); +CURLcode curl_global_init( long flags ); + +CURL *curl_easy_init( void ); +void curl_easy_cleanup( CURL *curl ); +CURLcode curl_easy_setopt( CURL *curl, long option, ... ); +char *curl_easy_escape( CURL *handle, const char *string, int length ); +char *curl_unescape( const char *string, int length ); +char *curl_easy_unescape( CURL *handle, const char *string, int length, int *outlength ); +CURLcode curl_easy_perform( CURL *curl ); +void curl_easy_reset( CURL *curl ); + +struct curl_certinfo +{ + int num_of_certs; + struct curl_slist **certinfo; +}; + +#define CURLINFO_LONG 0x200000 +#define CURLINFO_SLIST 0x400000 + +typedef enum +{ + CURLINFO_NONE, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_LASTONE = 42 +} CURLINFO; + +CURLcode curl_easy_getinfo( CURL *curl, long info, ... ); + +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 26 +#define LIBCURL_VERSION_PATCH 0 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/qa/mockup/internals.hxx b/qa/mockup/internals.hxx new file mode 100644 index 0000000..43c001e --- /dev/null +++ b/qa/mockup/internals.hxx @@ -0,0 +1,133 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef INCLUDED_QA_MOCKUP_INTERNALS_HXX +#define INCLUDED_QA_MOCKUP_INTERNALS_HXX + +#include <map> +#include <string> +#include <vector> + +typedef size_t ( *write_callback )( char *ptr, size_t size, size_t nmemb, void *userdata ); +typedef size_t ( *read_callback )( char *ptr, size_t size, size_t nmemb, void *userdata ); +typedef size_t ( *headers_callback )( char *ptr, size_t size, size_t nmemb, void *userdata ); + +class CurlHandle +{ + public: + CurlHandle( ); + CurlHandle( const CurlHandle& copy ); + CurlHandle& operator=( const CurlHandle& copy ); + ~CurlHandle( ); + + std::string m_url; + + write_callback m_writeFn; + void* m_writeData; + read_callback m_readFn; + void* m_readData; + long m_readSize; + headers_callback m_headersFn; + void* m_headersData; + + std::string m_username; + std::string m_password; + std::string m_proxy; + std::string m_noProxy; + std::string m_proxyUser; + std::string m_proxyPass; + + bool m_verifyHost; + bool m_verifyPeer; + bool m_certInfo; + + struct curl_certinfo m_certs; + + long m_httpError; + std::string m_method; + std::vector< std::string > m_headers; + + void reset( ); +}; + +namespace mockup +{ + class Response + { + public: + Response( std::string response, unsigned int status, bool isFilePath, + std::string headers ); + + std::string m_response; + unsigned int m_status; + bool m_isFilePath; + std::string m_headers; + }; + + class Request + { + public: + Request( std::string url, std::string method, std::string body, + std::vector< std::string > headers ); + + std::string m_url; + std::string m_method; + std::string m_body; + std::vector< std::string > m_headers; + }; + + class RequestMatcher + { + public: + RequestMatcher( std::string baseUrl, std::string matchParam, + std::string method, std::string matchBody ); + bool operator< ( const RequestMatcher& compare ) const; + + std::string m_baseUrl; + std::string m_matchParam; + std::string m_method; + std::string m_matchBody; + }; + + class Configuration + { + public: + Configuration( ); + + bool hasCredentials( ); + CURLcode writeResponse( CurlHandle* handle ); + + std::map< RequestMatcher, Response > m_responses; + std::vector< Request > m_requests; + std::string m_username; + std::string m_password; + std::string m_badSSLCertificate; + }; +} + +#endif diff --git a/qa/mockup/mockup-config.cxx b/qa/mockup/mockup-config.cxx new file mode 100644 index 0000000..7678518 --- /dev/null +++ b/qa/mockup/mockup-config.cxx @@ -0,0 +1,409 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "mockup-config.h" + +#include <memory> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <iostream> + +#include <boost/algorithm/string.hpp> + +#include "internals.hxx" + +using namespace std; + +namespace +{ + void lcl_splitUrl( const string& url, string& urlBase, string& params ) + { + size_t pos = url.find( "?" ); + urlBase = url; + if ( pos != string::npos ) + { + urlBase = url.substr( 0, pos ); + params = url.substr( pos + 1 ); + } + } + + const char** lcl_toStringArray( const vector< string >& vect ) + { + const char** array = new const char*[vect.size() + 1]; + for ( size_t i = 0; i < vect.size( ); i++ ) + array[i] = vect[i].c_str(); + array[vect.size()] = NULL; + return array; + } +} + +namespace mockup +{ + Response::Response( string response, unsigned int status, bool isFilePath, string headers ) : + m_response( response ), + m_status( status ), + m_isFilePath( isFilePath ), + m_headers( headers ) + { + } + + Request::Request( string url, string method, string body, vector< string > headers ) : + m_url( url ), + m_method( method ), + m_body( body ), + m_headers( headers ) + { + } + + RequestMatcher::RequestMatcher( string baseUrl, string matchParam, string method, string matchBody ) : + m_baseUrl( baseUrl ), + m_matchParam( matchParam ), + m_method( method ), + m_matchBody( matchBody ) + { + } + + bool RequestMatcher::operator< ( const RequestMatcher& compare ) const + { + int cmpBaseUrl = m_baseUrl.compare( compare.m_baseUrl ) ; + if ( cmpBaseUrl != 0 ) + return cmpBaseUrl < 0; + + int cmpMatchParam = m_matchParam.compare( compare.m_matchParam ); + if ( cmpMatchParam != 0 ) + return cmpMatchParam < 0; + + int cmpMatchMethod = m_method.compare( compare.m_method ); + if ( cmpMatchMethod != 0 ) + return cmpMatchMethod < 0; + + int cmpMatchBody = m_matchBody.compare( compare.m_matchBody ); + return cmpMatchBody < 0; + } + + Configuration::Configuration( ) : + m_responses( ), + m_requests( ), + m_username( ), + m_password( ), + m_badSSLCertificate( ) + { + } + + bool Configuration::hasCredentials( ) + { + return !m_username.empty( ) && !m_password.empty( ); + } + + /** Find a suitable response + * using the request as a search key + */ + CURLcode Configuration::writeResponse( CurlHandle* handle ) + { + CURLcode code = CURLE_OK; + + string headers; + string response; + bool foundResponse = false; + bool isFilePath = true; + const string& url = handle->m_url; + + string urlBase = url; + string params; + lcl_splitUrl( url, urlBase, params ); + string method = handle->m_method; + string body = m_requests.back().m_body; + + for ( map< RequestMatcher, Response >::iterator it = m_responses.begin( ); + it != m_responses.end( ) && response.empty( ); ++it ) + { + RequestMatcher matcher = it->first; + string& paramFind = matcher.m_matchParam; + bool matchBaseUrl = matcher.m_baseUrl.empty() || ( urlBase == matcher.m_baseUrl ); + bool matchParams = paramFind.empty( ) || ( params.find( paramFind ) != string::npos ); + bool matchMethod = it->first.m_method.empty( ) || ( it->first.m_method == method ); + bool matchBody = matcher.m_matchBody.empty( ) || ( body.find( matcher.m_matchBody ) != string::npos ); + + if ( matchBaseUrl && matchParams && matchMethod && matchBody ) + { + foundResponse = true; + response = it->second.m_response; + handle->m_httpError = it->second.m_status; + isFilePath = it->second.m_isFilePath; + headers = it->second.m_headers; + } + } + + // Output headers is any + if ( !headers.empty() ) + { + char* buf = strdup( headers.c_str() ); + handle->m_headersFn( buf, 1, headers.size( ), handle->m_headersData ); + free( buf ); + } + + // If nothing matched, then send a 404 HTTP error instead + if ( !foundResponse || ( foundResponse && isFilePath && response.empty() ) ) + handle->m_httpError = 404; + else + { + if ( isFilePath ) + { + FILE* fd = fopen( response.c_str( ), "r" ); + if ( !fd ) { + cerr << "Missing test file: " << response << endl; + handle->m_httpError = 500; + return CURLE_HTTP_RETURNED_ERROR; + } + + + size_t bufSize = 2048; + char* buf = new char[bufSize]; + + size_t read = 0; + size_t written = 0; + do + { + read = fread( buf, 1, bufSize, fd ); + written = handle->m_writeFn( buf, 1, read, handle->m_writeData ); + } while ( read == bufSize && written == read ); + + fclose( fd ); + delete[] buf; + } + else + { + if ( !response.empty() ) + { + char* buf = strdup( response.c_str() ); + handle->m_writeFn( buf, 1, response.size( ), handle->m_writeData ); + free( buf ); + } + } + } + + // What curl error code to give? + if ( handle->m_httpError == 0 ) + handle->m_httpError = 200; + + if ( handle->m_httpError < 200 || handle->m_httpError >= 300 ) + code = CURLE_HTTP_RETURNED_ERROR; + + return code; + } + + unique_ptr<Configuration> config{ new Configuration( ) }; +} + +void curl_mockup_reset( ) +{ + mockup::config.reset( new mockup::Configuration( ) ); +} + +void curl_mockup_addResponse( const char* urlBase, const char* matchParam, const char* method, + const char* response, unsigned int status, bool isFilePath, + const char* headers, const char* matchBody ) +{ + string matchBodyStr; + if ( matchBody ) + matchBodyStr = matchBody; + mockup::RequestMatcher matcher( urlBase, matchParam, method, matchBodyStr ); + map< mockup::RequestMatcher, mockup::Response >::iterator it = mockup::config->m_responses.find( matcher ); + if ( it != mockup::config->m_responses.end( ) ) + mockup::config->m_responses.erase( it ); + + string headersStr; + if ( headers != NULL ) + headersStr = headers; + mockup::Response responseDesc( response, status, isFilePath, headersStr ); + mockup::config->m_responses.insert( pair< mockup::RequestMatcher, mockup::Response >( matcher, responseDesc ) ); +} + +void curl_mockup_setResponse( const char* filepath ) +{ + mockup::config->m_responses.clear( ); + curl_mockup_addResponse( "", "", "", filepath ); +} + +void curl_mockup_setCredentials( const char* username, const char* password ) +{ + mockup::config->m_username = string( username ); + mockup::config->m_password = string( password ); +} + +const struct HttpRequest* curl_mockup_getRequest( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody ) +{ + struct HttpRequest* request = NULL; + + string urlBaseString( urlBase ); + string matchParamString( matchParam ); + + string matchBodyStr; + if ( matchBody ) + matchBodyStr = matchBody; + + for ( vector< mockup::Request >::iterator it = mockup::config->m_requests.begin( ); + it != mockup::config->m_requests.end( ) && request == NULL; ++it ) + { + string url; + string params; + if ( it->m_method == string( method ) ) + { + lcl_splitUrl( it->m_url, url, params ); + + bool matchBaseUrl = urlBaseString.empty() || boost::starts_with( url, urlBaseString ); + bool matchParams = matchParamString.empty( ) || ( params.find( matchParamString ) != string::npos ); + bool matchBodyPart = !matchBody || ( it->m_body.find( matchBodyStr ) != string::npos ); + + if ( matchBaseUrl && matchParams && matchBodyPart ) + { + request = new HttpRequest; + request->url = it->m_url.c_str(); + request->body = it->m_body.c_str(); + request->headers = lcl_toStringArray( it->m_headers ); + } + } + } + + return request; +} + +const char* curl_mockup_getRequestBody( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody ) +{ + const struct HttpRequest* request = curl_mockup_getRequest( urlBase, matchParam, method, matchBody ); + if ( request ) + { + const char* body = request->body; + curl_mockup_HttpRequest_free( request ); + return body; + } + return NULL; +} + +int curl_mockup_getRequestsCount( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody ) +{ + int count = 0; + + string urlBaseString( urlBase ); + string matchParamString( matchParam ); + string matchBodyStr( matchBody ); + + for ( vector< mockup::Request >::iterator it = mockup::config->m_requests.begin( ); + it != mockup::config->m_requests.end( ); ++it ) + { + string url; + string params; + if ( it->m_method == string( method ) ) + { + lcl_splitUrl( it->m_url, url, params ); + + bool matchBaseUrl = urlBaseString.empty() || boost::starts_with( url, urlBaseString ); + bool matchParams = matchParamString.empty( ) || + ( params.find( matchParamString ) != string::npos ); + bool matchBodyPart = matchBodyStr.empty() || + ( it->m_body.find( matchBodyStr ) != string::npos ); + + if ( matchBaseUrl && matchParams && matchBodyPart ) + { + count++; + } + } + } + return count; +} + +void curl_mockup_HttpRequest_free( const struct HttpRequest* request ) +{ + delete[] request->headers; + delete request; +} + +char* curl_mockup_HttpRequest_getHeader( const struct HttpRequest* request, const char* name ) +{ + char* value = NULL; + size_t i = 0; + while ( request->headers[i] != NULL && value == NULL ) + { + string header = request->headers[i]; + const string prefix = string( name ) + ":"; + size_t pos = header.find( prefix ); + if ( pos == 0 ) + { + value = strdup( header.substr( prefix.size() ).c_str() ); + } + ++i; + } + return value; +} + +const char* curl_mockup_getProxy( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_proxy.c_str(); + return NULL; +} + +const char* curl_mockup_getNoProxy( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_noProxy.c_str(); + return NULL; +} + +const char* curl_mockup_getProxyUser( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_proxyUser.c_str(); + return NULL; +} + +const char* curl_mockup_getProxyPass( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_proxyPass.c_str(); + return NULL; +} + +void curl_mockup_setSSLBadCertificate( const char* certificate ) +{ + mockup::config->m_badSSLCertificate = string( certificate ); +} diff --git a/qa/mockup/mockup-config.h b/qa/mockup/mockup-config.h new file mode 100644 index 0000000..d0fc3bb --- /dev/null +++ b/qa/mockup/mockup-config.h @@ -0,0 +1,113 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <curl/curl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mockup behavior configuration functions */ +void curl_mockup_reset( ); + +/** Add a new HTTP response the server should send. + + \param baseURL + the base URL of the request without parameters + \param matchParam + a string to find in the parameters part of the URL to match + \param method + HTTP method to match like PUT, GET, POST or DELETE. An empty + string matches any method. + \param response + a string corresponding either to the file path of the request + body to send or directly the content to send. This value has + a different meaning depending on isFilePath parameter. + \param status + the HTTP status to return. 0 means HTTP OK (200). + \param isFilePath + if this value is true the response value is used as a file path, + otherwise, the response value is used as the body of the HTTP + response to send. + \param headers + the HTTP headers block to send with the response. By default + no header is sent. + \param matchBody + a string to find in the request body to match + */ +void curl_mockup_addResponse( const char* baseUrl, const char* matchParam, const char* method, + const char* response, unsigned int status = 0, bool isFilePath = true, + const char* headers = 0, const char* matchBody = 0 ); + +/** Set the HTTP response the server is supposed to send. + This will reset all already defined responses. + */ +void curl_mockup_setResponse( const char* filepath ); +void curl_mockup_setCredentials( const char* username, const char* password ); + +struct HttpRequest +{ + const char* url; + const char* body; + ///< NULL terminated array of headers. + const char** headers; +}; + +const struct HttpRequest* curl_mockup_getRequest( const char* baseUrl, + const char* matchParam, + const char* method, + const char* matchBody = 0 ); +const char* curl_mockup_getRequestBody( const char* baseUrl, + const char* matchParam, + const char* method, + const char* matchBody = 0 ); +int curl_mockup_getRequestsCount( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody = "" ); + +void curl_mockup_HttpRequest_free( const struct HttpRequest* request ); + +/** The resulting value is either NULL (no such header found) or the value + of the header. In such a case, the result needs to be freed by the caller. + */ +char* curl_mockup_HttpRequest_getHeader( const struct HttpRequest* request, const char* name ); + +const char* curl_mockup_getProxy( CURL* handle ); +const char* curl_mockup_getNoProxy( CURL* handle ); +const char* curl_mockup_getProxyUser( CURL* handle ); +const char* curl_mockup_getProxyPass( CURL* handle ); + +/** Set a fake invalid certificate to raise CURLE_SSL_CACERT. Setting it + to an empty string will reset to no certificate. + */ +void curl_mockup_setSSLBadCertificate( const char* certificate ); + +#ifdef __cplusplus +} +#endif diff --git a/samples/populate.sh b/samples/populate.sh new file mode 100644 index 0000000..8a47359 --- /dev/null +++ b/samples/populate.sh @@ -0,0 +1,168 @@ +#!/bin/sh +# libcmis +# Version: MPL 1.1 / GPLv2+ / LGPLv2+ +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License or as specified alternatively below. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# Major Contributor(s): +# Copyright (C) 2012 SUSE <cbosdonnat@suse.com> +# +# +# All Rights Reserved. +# +# For minor contributions see the git repository. +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPLv2+"), or +# the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), +# in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable +# instead of those above. + +function usage ( ) +{ + echo "$0 --url http://binding/url [-u|--username user -p|--password pass] [-r|--repository repo] path/to/local/folder /path/to/remote/folder" +} + + +# Utility function to call cmis-client +function cmis_client ( ) +{ + repo_opt= + if test "z$REPO" != "z"; then + repo_opt=" -r \"$REPO\"" + fi + $CMIS_CLIENT --url "$BINDING_URL" -u "$USER" -p "$PASS"$repo_opt "$@" +} + + +BASE_FOLDER= +SERVER_FOLDER= +BINDING_URL= +USER= +PASS= +REPO= + +# Parse the arguments into variables +while test -n "$1"; do + case $1 in + --url) + shift + BINDING_URL="$1" + shift + ;; + --username|-u) + shift + USER="$1" + shift + ;; + --password|-p) + shift + PASS="$1" + shift + ;; + --repository|-r) + shift + REPO="$1" + shift + ;; + --help|-h) + usage + exit 1 + ;; + *) + if test -z "$BASE_FOLDER"; then + BASE_FOLDER="$1" + shift + elif test -z "$SERVER_FOLDER"; then + SERVER_FOLDER="$1" + shift + fi + ;; + esac +done + +# Check that we had the input we need +if test ! -d "$BASE_FOLDER"; then + usage + echo "" + echo "Missing or invalid local folder path" + exit 1 +fi + +if test -z "$BINDING_URL"; then + usage + echo "" + echo "Missing CMIS binding URL" + exit 1 +fi + +# Look for cmis-client in PATH +CMIS_CLIENT=`which cmis-client 2>/dev/null` +if test -z $CMIS_CLIENT; then + echo "cmis-client executable isn't in the PATH" + exit 1 +fi + +# Make sure the SERVER_FOLDER is existing +SERVER_FOLDER_ID=`cmis_client show-by-path "$SERVER_FOLDER" 2>&1 | grep '^Id:' | sed -e 's/^[^:]*: \(.*\)$/\1/'` +if test -z $SERVER_FOLDER_ID; then + echo "Server folder '$SERVER_FOLDER' doesn't exist, please indicate an existing folder" + exit 1 +fi + +SERVER_FOLDER_BASE_TYPE=`cmis_client show-by-path "$SERVER_FOLDER" 2>&1 | grep '^Base type:' | sed -e 's/^[^:]*: \(.*\)$/\1/'` +if test "$SERVER_FOLDER_BASE_TYPE" != "cmis:folder"; then + echo "'$SERVER_FOLDER' isn't a folder, please indicate an existing folder" + exit 1 +fi + +# Make sure that SERVER_FOLDER has no trailing slash +SERVER_FOLDER=${SERVER_FOLDER%/} + +cd $BASE_FOLDER/.. +BASE_FOLDER_NAME=`basename "$BASE_FOLDER"` +find $BASE_FOLDER_NAME -print0 | while read -d $'\0' FILE_PATH +do + FILE_NAME=`basename "$FILE_PATH"` + DIRNAME=`dirname "$FILE_PATH"` + DIRNAME=${DIRNAME#.} + SERVER_PARENT=$SERVER_FOLDER/$DIRNAME + if test -z "$DIRNAME"; then + SERVER_PARENT=$SERVER_FOLDER + fi + + PARENT_ID=`cmis_client show-by-path "$SERVER_PARENT" 2>&1 | grep '^Id:' | sed -e 's/^[^:]*: \(.*\)$/\1/'` + + # We couldn't find the parent id, then we need to create the folder on the server + if test -z "$PARENT_ID"; then + PARENT_NAME=`basename "$SERVER_PARENT"` + PARENT_PARENT_PATH=`dirname "$SERVER_PARENT"` + PARENT_PARENT_ID=`cmis_client show-by-path "$PARENT_PARENT_PATH" 2>&1 | grep '^Id:' | sed -e 's/^[^:]*: \(.*\)$/\1/'` + PARENT_ID=`cmis_client create-folder $PARENT_PARENT_ID $PARENT_NAME 2>&1 | grep '^Id:' | sed -e 's/^[^:]*: \(.*\)$/\1/'` + fi + + if test -d "$FILE_PATH"; then + cmis_client create-folder "$PARENT_ID" "$FILE_NAME" >/dev/null + else + FILE_MIME=`file --mime-type "$FILE_PATH" | cut -d ' ' -f 2` + + cmis_client --input-file "$FILE_PATH" --input-type $FILE_MIME \ + create-document "$PARENT_ID" "$FILE_NAME" >/dev/null + fi + + if test "x$?" == "x0"; + then + echo -ne "OK\t" + else + echo -ne "Failed\t" + fi + echo $SERVER_PARENT/$FILE_NAME +done diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..01c3d19 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = libcmis libcmis-c + +if ENABLE_CLIENT +AM_CXXFLAGS = \ + -I$(top_srcdir)/inc \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) + +bin_PROGRAMS = cmis-client + +cmis_client_SOURCES = cmis-client.cxx +cmis_client_LDADD = $(top_builddir)/src/libcmis/libcmis-@LIBCMIS_API_VERSION@.la \ + $(XML2_LIBS) \ + $(BOOST_PROGRAM_OPTIONS_LIBS) \ + $(BOOST_DATE_TIME_LIBS) \ + $(JSON_LIBS) +endif diff --git a/src/cmis-client.cxx b/src/cmis-client.cxx new file mode 100644 index 0000000..73b6f42 --- /dev/null +++ b/src/cmis-client.cxx @@ -0,0 +1,1158 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <stdio.h> + +#include <exception> +#include <fstream> +#include <iostream> +#include <map> +#include <memory> +#include <string> + +#include <boost/program_options.hpp> + +#include <libcmis/libcmis.hxx> + +using namespace std; +using namespace boost::program_options; +using libcmis::PropertyPtrMap; + +namespace +{ + char* lcl_queryAuthCode( const char* url, const char* /*username*/, const char* /*password*/ ) + { + string code; + cout << "Copy the following link to your browser and take the code: " << endl << endl << url << endl << endl; + cout << "Enter the code:" << endl; + cin >> code; + + return strdup( code.c_str() ); + } + + class AuthCodeProvider + { + private: + static string m_authCode; + + public: + static void setAuthCode( string authCode ); + static char* getAuthCode( const char* /*url*/, const char* /*username*/, const char* /*password*/ ); + }; + + string AuthCodeProvider::m_authCode = string( ); + void AuthCodeProvider::setAuthCode( string authCode ) + { + m_authCode = authCode; + } + + char* AuthCodeProvider::getAuthCode( const char* /*url*/, const char* /*username*/, const char* /*password*/ ) + { + return strdup( m_authCode.c_str( ) ); + } + + class CinAuthProvider : public libcmis::AuthProvider + { + private: + string m_user; + string m_pass; + + public: + CinAuthProvider( ) : m_user( ), m_pass( ) { } + ~CinAuthProvider( ) { } + + virtual bool authenticationQuery( string& username, string& password ); + }; + + bool CinAuthProvider::authenticationQuery( string& username, string& password ) + { + bool cancelled = false; + bool askUsername = username.empty(); + + if ( askUsername ) + { + if ( m_user.empty() ) + { + cout << "Username (empty to cancel): "; + getline( cin, m_user ); + } + + cancelled = m_user.empty(); + username = m_user; + } + + if ( !cancelled && ( askUsername || password.empty( ) ) ) + { + if ( m_pass.empty() ) + { + cout << "Password (empty to cancel): "; + getline( cin, m_pass ); + } + + cancelled = m_pass.empty(); + password = m_pass; + } + + return !cancelled; + } + + class CinCertValidationHandler : public libcmis::CertValidationHandler + { + private: + map< string, bool > m_answers; + + public: + CinCertValidationHandler( ) : m_answers( ) { } + ~CinCertValidationHandler( ) { } + + virtual bool validateCertificate( vector< string > certificates ) + { + if ( certificates.empty( ) ) + return false; // Should never happen + + // Show the first certificate (even base64-encoded) + string cert = certificates.front(); + map< string, bool >::iterator it = m_answers.find( cert ); + if ( it != m_answers.end( ) ) + return it->second; + + cout << "Invalid SSL certificate:" << endl << cert << endl; + cout << "'openssl x509 -noout -text' can show you the details of this certificate." << endl << endl; + + // Ask whether to validate + cout << "Do you want to ignore this problem and go on? yes/no [default: no]: "; + string answer; + getline( cin, answer ); + + m_answers[cert] = answer == "yes"; + + return answer == "yes"; + } + }; +} + +class CommandException : public exception +{ + private: + string m_msg; + + public: + CommandException( string msg ) : m_msg( msg ) { } + ~CommandException( ) noexcept { } + CommandException( const CommandException& copy ) : m_msg( copy.m_msg ) { } + + CommandException& operator=( const CommandException& copy ) + { + if ( this != © ) + m_msg = copy.m_msg; + return *this; + } + + virtual const char* what() const noexcept { return m_msg.c_str(); } +}; + +class CmisClient +{ + private: + variables_map& m_vm; + public: + CmisClient( variables_map& vm ) : m_vm( vm ) { } + + libcmis::Session* getSession( bool inGetRepositories = false ); + + void execute( ); + + void printHelp( ); + + static options_description getOptionsDescription( ); + + private: + map< int, string > getSessionParams(); + map< string, string > getObjectProperties( ); +}; + +map< string, string > CmisClient::getObjectProperties( ) +{ + map< string, string > result; + if ( m_vm.count( "object-property" ) > 0 ) + { + vector< string > params = m_vm["object-property"].as< vector< string > >( ); + for ( vector< string >::iterator it = params.begin( ); it != params.end( ); ++it ) + { + size_t pos = it->find( "=" ); + if ( pos != string::npos ) + { + string name = it->substr( 0, pos ); + string value = it->substr( pos + 1 ); + result.insert( pair< string, string >( name, value ) ); + } + } + } + + return result; +} + +libcmis::Session* CmisClient::getSession( bool inGetRepositories ) +{ + if ( m_vm.count( "url" ) == 0 ) + throw CommandException( "Missing binding URL" ); + + // Setup the authentication provider in case we have missing credentials + libcmis::AuthProviderPtr provider( new CinAuthProvider( ) ); + libcmis::SessionFactory::setAuthenticationProvider( provider ); + + libcmis::CertValidationHandlerPtr certValidator( new CinCertValidationHandler( ) ); + libcmis::SessionFactory::setCertificateValidationHandler( certValidator ); + + string url = m_vm["url"].as<string>(); + + // Look for the credentials + string username; + string password; + if ( m_vm.count( "username" ) > 0 ) + { + username = m_vm["username"].as< string >(); + + if ( m_vm.count( "password" ) > 0 ) + password = m_vm["password"].as< string >(); + } + + // Look for proxy settings + string proxyUrl; + string proxyUser; + string proxyPass; + string noproxy; + if ( m_vm.count( "proxy" ) > 0 ) + { + proxyUrl = m_vm["proxy"].as< string >(); + + if ( m_vm.count( "proxy-user" ) > 0 ) + proxyUser = m_vm["proxy-user"].as< string >(); + + if ( m_vm.count( "proxy-password" ) > 0 ) + proxyPass = m_vm["proxy-password"].as< string >(); + + if ( m_vm.count( "noproxy" ) > 0 ) + noproxy = m_vm["noproxy"].as< string >(); + + libcmis::SessionFactory::setProxySettings( proxyUrl, noproxy, proxyUser, proxyPass ); + } + + bool verbose = m_vm.count( "verbose" ) > 0; + + libcmis::Session* session = NULL; + string repoId; + // The repository ID is needed to initiate a session + if ( m_vm.count( "repository" ) == 0 && !inGetRepositories ) + { + // Do we have a single repository on the server? + session = getSession( true ); + if ( session != NULL ) + { + vector< libcmis::RepositoryPtr > repos = session->getRepositories(); + if ( repos.size() == 1 ) + { + repoId = repos.front( )->getId( ); + session->setRepository( repoId ); + } + } + + // We couldn't auto-guess the repository, then throw an error + if ( repoId.empty( ) ) + { + delete session; + throw CommandException( "Missing repository ID" ); + } + } + else if ( m_vm.count( "repository" ) > 0 ) + { + repoId = m_vm["repository"].as< string >(); + } + + if ( session == NULL ) + { + bool noSslCheck = m_vm.count( "no-ssl-check" ) > 0; + + // Should we use OAuth2? + string oauth2ClientId; + string oauth2ClientSecret; + string oauth2AuthUrl; + string oauth2TokenUrl; + string oauth2RedirectUri; + string oauth2Scope; + if ( m_vm.count( "oauth2-client-id" ) > 0 ) + oauth2ClientId = m_vm["oauth2-client-id"].as< string >(); + if ( m_vm.count( "oauth2-client-secret" ) > 0 ) + oauth2ClientSecret = m_vm["oauth2-client-secret"].as< string >(); + if ( m_vm.count( "oauth2-auth-url" ) > 0 ) + oauth2AuthUrl = m_vm["oauth2-auth-url"].as< string >(); + if ( m_vm.count( "oauth2-token-url" ) > 0 ) + oauth2TokenUrl = m_vm["oauth2-token-url"].as< string >(); + if ( m_vm.count( "oauth2-redirect-uri" ) > 0 ) + oauth2RedirectUri = m_vm["oauth2-redirect-uri"].as< string >(); + if ( m_vm.count( "oauth2-scope" ) > 0 ) + oauth2Scope = m_vm["oauth2-scope"].as< string >(); + if ( m_vm.count( "oauth2-auth-code" ) > 0 ) + AuthCodeProvider::setAuthCode( m_vm["oauth2-auth-code"].as< string >() ); + + libcmis::OAuth2DataPtr oauth2Data( new libcmis::OAuth2Data( oauth2AuthUrl, oauth2TokenUrl, + oauth2Scope, oauth2RedirectUri, oauth2ClientId, oauth2ClientSecret) ); + + if ( oauth2Data->isComplete( ) ) + { + // Set the fallback AuthCode provider + if ( m_vm.count( "oauth2-auth-code" ) > 0 ) + { + libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthCodeProvider::getAuthCode ); + } + else + { + libcmis::SessionFactory::setOAuth2AuthCodeProvider( lcl_queryAuthCode ); + } + } + else + { + oauth2Data.reset( ); + } + + session = libcmis::SessionFactory::createSession( url, username, password, repoId, noSslCheck, oauth2Data, verbose ); + } + + return session; +} + +void CmisClient::execute( ) +{ + if ( ( m_vm.count( "help" ) > 0 ) || m_vm.count( "command" ) != 1 ) + { + printHelp(); + return; + } + + if ( m_vm.count( "command" ) == 1 ) + { + string command = m_vm["command"].as<string>(); + if ( "list-repos" == command ) + { + libcmis::Session* session = getSession( true ); + if ( session != NULL ) + { + vector< libcmis::RepositoryPtr > repos = session->getRepositories(); + + cout << "Repositories: name (id)" << endl; + for ( vector< libcmis::RepositoryPtr >::iterator it = repos.begin(); it != repos.end(); ++it ) + cout << "\t" << ( *it )->getName( ) << " (" << ( *it )->getId( ) << ")" << endl; + } + else + { + cerr << "Couldn't create a session for some reason" << endl; + } + } + else if ( "show-root" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + libcmis::FolderPtr root = session->getRootFolder(); + if ( root.get() ) + { + cout << "------------------------------------------------" << endl; + cout << root->toString() << endl; + } + } + else if ( "repo-infos" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + libcmis::RepositoryPtr repo = session->getRepository( ); + + if ( repo ) + { + cout << "------------------------------------------------" << endl; + cout << repo->toString() << endl; + } + else + throw CommandException( "Please select a repository" ); + } + else if ( "type-by-id" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the types to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node ids to show as command args" ); + + vector< string > ids = m_vm["args"].as< vector< string > >( ); + + + for ( vector< string >::iterator it = ids.begin(); it != ids.end(); ++it ) + { + cout << "------------------------------------------------" << endl; + try + { + libcmis::ObjectTypePtr type = session->getType( *it ); + cout << type->toString() << endl; + } + catch ( const libcmis::Exception& e ) + { + cout << e.what() << endl; + } + } + } + else if ( "show-by-id" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node ids to show as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + + for ( vector< string >::iterator it = objIds.begin(); it != objIds.end(); ++it ) + { + libcmis::ObjectPtr cmisObj = session->getObject( *it ); + cout << "------------------------------------------------" << endl; + if ( cmisObj.get() ) + cout << cmisObj->toString() << endl; + else + cout << "No such node: " << *it << endl; + } + } + else if ( "show-by-path" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the paths of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node paths to show as command args" ); + + vector< string > objPaths = m_vm["args"].as< vector< string > >( ); + + + for ( vector< string >::iterator it = objPaths.begin(); it != objPaths.end(); ++it ) + { + libcmis::ObjectPtr cmisObj = session->getObjectByPath( *it ); + cout << "------------------------------------------------" << endl; + if ( cmisObj.get() ) + cout << cmisObj->toString() << endl; + else + cout << "No such node: " << *it << endl; + } + } + else if ( "get-content" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + if ( objIds.empty( ) ) + throw CommandException( "Please provide a content object Id" ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != document ) + { + // TODO Handle name clashes + string streamId; + if ( m_vm.count( "stream-id" ) > 0 ) + streamId = m_vm["stream-id"].as<string>(); + + boost::shared_ptr< istream > in = document->getContentStream( streamId ); + ofstream out( document->getContentFilename().c_str() ); + out << in->rdbuf(); + out.close(); + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else if ( "set-content" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + if ( objIds.empty( ) ) + throw CommandException( "Please provide a content object Id" ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != document ) + { + if ( m_vm.count( "input-file" ) == 0 ) + throw CommandException( "Missing --input-file" ); + if ( m_vm.count( "input-type" ) == 0 ) + throw CommandException( "Missing --input-type" ); + + string type = m_vm["input-type"].as<string>(); + string filename; + if ( m_vm.count( "input-name" ) > 0 ) + filename = m_vm["input-name"].as<string>(); + string file = m_vm["input-file"].as<string>(); + ifstream is( file.c_str(), ifstream::in ); + boost::shared_ptr< ostream > os ( new ostream ( is.rdbuf( ) ) ); + if ( is.fail( ) ) + throw CommandException( string( "Unable to open file " ) + file ); + + document->setContentStream( os, type, filename ); + + is.close( ); + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else if ( "create-folder" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > >( ); + if ( args.size() < 2 ) + throw CommandException( "Please provide a parent Id and folder name" ); + + libcmis::FolderPtr parent = session->getFolder( args[0] ); + + // Get the folder type to create + string folderType( "cmis:folder" ); + if ( m_vm.count( "object-type" ) != 0 ) + folderType = m_vm["object-type"].as<string>( ); + + libcmis::ObjectTypePtr type = session->getType( folderType ); + if ( "cmis:folder" != type->getBaseType( )->getId( ) ) + throw CommandException( string( "Not a folder type: " ) + folderType ); + + PropertyPtrMap properties; + map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( ); + + // Set the name + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( string( "cmis:name" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:name on the object type... weird" ) ); + vector< string > nameValues; + string newFolderName = args[1]; + for ( unsigned int i = 2; i < args.size( ); i++ ) + { + newFolderName += " " + args[i]; + } + nameValues.push_back( newFolderName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( typeIt->second, nameValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // Set the objectTypeId + typeIt = propertiesTypes.find( string( "cmis:objectTypeId" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:objectTypeId on the object type... weird" ) ); + vector< string > typeIdValues; + typeIdValues.push_back( folderType ); + libcmis::PropertyPtr typeIdProperty( new libcmis::Property( typeIt->second, typeIdValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeIdProperty ) ); + + // Checks for the properties to set if any + map< string, string > propsToSet = getObjectProperties( ); + for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it ) + { + // Create the CMIS property if it exists + typeIt = propertiesTypes.find( it->first ); + if ( typeIt != propertiesTypes.end( ) ) + { + vector< string > values; + values.push_back( it->second ); + libcmis::PropertyPtr cmisProperty( new libcmis::Property( typeIt->second, values ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( it->first, cmisProperty ) ); + } + } + + libcmis::FolderPtr created = parent->createFolder( properties ); + + cout << "------------------------------------------------" << endl; + cout << created->toString() << endl; + } + else if ( "create-document" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > >( ); + if ( args.size() < 2 ) + throw CommandException( "Please provide a parent Id and document name" ); + + libcmis::FolderPtr parent = session->getFolder( args[0] ); + + // Get the document type to create + string documentType( "cmis:document" ); + if ( m_vm.count( "object-type" ) != 0 ) + documentType = m_vm["object-type"].as<string>( ); + + libcmis::ObjectTypePtr type = session->getType( documentType ); + if ( "cmis:document" != type->getBaseType( )->getId( ) ) + throw CommandException( string( "Not a document type: " ) + documentType ); + + PropertyPtrMap properties; + map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( ); + + // Set the name + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( string( "cmis:name" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:name on the object type... weird" ) ); + vector< string > nameValues; + string newDocumentName = args[1]; + for ( unsigned int i = 2; i < args.size( ); i++ ) + { + newDocumentName += " " + args[i]; + } + nameValues.push_back( newDocumentName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( typeIt->second, nameValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // Set the objectTypeId + typeIt = propertiesTypes.find( string( "cmis:objectTypeId" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:objectTypeId on the object type... weird" ) ); + vector< string > typeIdValues; + typeIdValues.push_back( documentType ); + libcmis::PropertyPtr typeIdProperty( new libcmis::Property( typeIt->second, typeIdValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeIdProperty ) ); + + // Checks for the properties to set if any + map< string, string > propsToSet = getObjectProperties( ); + for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it ) + { + // Create the CMIS property if it exists + typeIt = propertiesTypes.find( it->first ); + if ( typeIt != propertiesTypes.end( ) ) + { + vector< string > values; + values.push_back( it->second ); + libcmis::PropertyPtr cmisProperty( new libcmis::Property( typeIt->second, values ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( it->first, cmisProperty ) ); + } + } + + // Get the content type and stream + boost::shared_ptr< ostream > contentStream; + string contentType; + string filename; + + bool hasInputFile = m_vm.count( "input-file" ) != 0; + bool hasInputType = m_vm.count( "input-type" ) != 0; + bool hasInputName = m_vm.count( "input-name" ) != 0; + + if ( hasInputType && !hasInputFile ) + throw CommandException( "Missing --input-file" ); + if ( hasInputFile && !hasInputType ) + throw CommandException( "Missing --input-type" ); + + if ( hasInputFile && hasInputType ) + { + contentType = m_vm["input-type"].as<string>(); + string file = m_vm["input-file"].as<string>(); + fstream is( file.c_str() ); + if ( is.fail( ) ) + throw CommandException( string( "Unable to open file " ) + file ); + contentStream.reset( new ostringstream( ios_base::out | ios_base::in ) ); + + *contentStream << is.rdbuf(); + } + + if ( hasInputName ) + filename = m_vm[ "input-name" ].as< string >( ); + + // Actually create the document + libcmis::DocumentPtr created = parent->createDocument( properties, contentStream, contentType, filename ); + + cout << "------------------------------------------------" << endl; + cout << created->toString() << endl; + } + else if ( "update-object" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > >( ); + if ( args.size() != 1 ) + throw CommandException( "Please provide an object id" ); + + libcmis::ObjectPtr object = session->getObject( args[0] ); + libcmis::ObjectTypePtr type = session->getType( object->getType( ) ); + map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( ); + + PropertyPtrMap properties; + + // Checks for the properties to set if any + map< string, string > propsToSet = getObjectProperties( ); + for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it ) + { + // Create the CMIS property if it exists + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( it->first ); + if ( typeIt != propertiesTypes.end( ) && typeIt->second->isUpdatable( ) ) + { + vector< string > values; + values.push_back( it->second ); + libcmis::PropertyPtr property( new libcmis::Property( typeIt->second, values ) ); + properties[ it->first ] = property; + } + } + + libcmis::ObjectPtr updated = object->updateProperties( properties ); + + cout << "------------------------------------------------" << endl; + // Output updated instead of object as it may be different depending on the server + cout << updated->toString() << endl; + } + else if ( "move-object" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > > ( ); + if ( args.size() != 3 ) + throw CommandException( "Please provide an object id and source and destination folder ids" ); + string& objId = args[0]; + string& srcId = args[1]; + string& dstId = args[2]; + + libcmis::ObjectPtr obj = session->getObject( objId ); + + libcmis::ObjectPtr src = session->getObject( srcId ); + libcmis::FolderPtr srcFolder = boost::dynamic_pointer_cast< libcmis::Folder > ( src ); + if ( !srcFolder ) + throw CommandException( "Source object is not a folder" ); + + libcmis::ObjectPtr dst = session->getObject( dstId ); + libcmis::FolderPtr dstFolder = boost::dynamic_pointer_cast< libcmis::Folder > ( dst ); + if ( !dstFolder ) + throw CommandException( "Destinaton object is not a folder" ); + + obj->move( srcFolder, dstFolder ); + + cout << "------------------------------------------------" << endl; + cout << obj->toString( ) << endl; + } + else if ( "delete" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node ids to delete as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + vector< string > errors; + for ( vector< string >::iterator it = objIds.begin(); it != objIds.end(); ++it ) + { + libcmis::ObjectPtr cmisObj = session->getObject( *it ); + libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( cmisObj.get() ); + if ( NULL != folder ) + { + try + { + vector< string > failed = folder->removeTree( ); + string error; + for ( vector< string >::iterator dumpIt = failed.begin( ); + dumpIt != failed.end( ); ++dumpIt ) + { + if ( dumpIt == failed.begin( ) ) + error += "Failed to remove children nodes: "; + else + error += ", "; + error = *dumpIt; + } + if ( !error.empty( ) ) + errors.push_back( error ); + } + catch ( const libcmis::Exception& e ) + { + string msg = *it + ": " + e.what( ); + errors.push_back( msg ); + } + } + else if ( cmisObj.get() ) + { + try + { + cmisObj->remove( ); + } + catch ( const libcmis::Exception& e ) + { + string msg = *it + ": " + e.what( ); + errors.push_back( msg ); + } + } + else + { + string msg = "No such node: " + *it; + errors.push_back( msg ); + } + } + + // Show the errors + if ( !errors.empty( ) ) + { + cout << "Errors:" << endl; + for ( vector< string >::iterator it = errors.begin( ); it != errors.end( ); ++it ) + { + cout << "\t" << *it << endl; + } + } + else + { + cout << "All nodes have been removed" << endl; + } + } + else if ( "checkout" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node id to checkout as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + if ( cmisObj.get() ) + { + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != doc ) + { + libcmis::DocumentPtr pwc = doc->checkOut( ); + if ( pwc.get( ) ) + { + cout << "------------------------------------------------" << endl; + cout << pwc->toString() << endl; + } + else + cout << "No Private Working Copy returned?" << endl; + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "cancel-checkout" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the private working copy object id to cancel as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + cout << "------------------------------------------------" << endl; + if ( cmisObj.get() ) + { + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != doc ) + { + doc->cancelCheckout( ); + cout << "Checkout cancelled" << endl; + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "checkin" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node id to checkin as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr object = session->getObject( objIds.front() ); + if ( object.get() ) + { + // Create the properties map + PropertyPtrMap properties; + map< string, string > propsToSet = getObjectProperties( ); + libcmis::ObjectTypePtr type = session->getType( object->getType( ) ); + map< string, libcmis::PropertyTypePtr > propertyTypes = type->getPropertiesTypes( ); + for ( map< string, string >::iterator propIt = propsToSet.begin(); + propIt != propsToSet.end(); ++propIt ) + { + string name = propIt->first; + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertyTypes.find( name ); + if ( typeIt != propertyTypes.end( ) ) + { + vector< string > values; + values.push_back( propIt->second ); + libcmis::PropertyPtr prop( new libcmis::Property( typeIt->second, values ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( name, prop ) ); + } + } + + // Get the content stream if any + string contentType; + string filename; + boost::shared_ptr< ostream > stream; + if ( m_vm.count( "input-file" ) > 0 ) + { + string file = m_vm["input-file"].as<string>(); + ifstream is( file.c_str(), ios_base::in ); + stringstream os; + os << is.rdbuf( ); + if ( is.fail( ) ) + throw CommandException( string( "Unable to open file " ) + file ); + + string content = os.str( ); + stream.reset( new stringstream( content ) ); + is.close( ); + + if ( m_vm.count( "input-type" ) > 0 ) + { + contentType = m_vm["input-type"].as<string>(); + } + if ( m_vm.count( "input-name" ) > 0 ) + { + filename = m_vm["input-name"].as<string>(); + } + } + + bool major = false; + if ( m_vm.count( "major" ) > 0 ) + major = "yes"; + + string comment; + if ( m_vm.count( "message" ) > 0 ) + comment = m_vm["message"].as< string >( ); + + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( object.get() ); + if ( NULL != doc ) + { + libcmis::DocumentPtr newDoc = doc->checkIn( major, comment, properties, stream, contentType, filename ); + + cout << "------------------------------------------------" << endl; + cout << newDoc->toString() << endl; + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "get-versions" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node id to get versions from as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr object = session->getObject( objIds.front() ); + if ( object.get() ) + { + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( object.get() ); + if ( NULL != doc ) + { + vector< libcmis::DocumentPtr > versions = doc->getAllVersions( ); + + for ( vector< libcmis::DocumentPtr >::iterator it = versions.begin( ); + it != versions.end( ); ++it ) + { + cout << "------------------------------------------------" << endl; + cout << ( *it )->toString() << endl; + } + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "help" == command ) + { + printHelp(); + } + else + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: Unknown command: " << command << endl; + cerr << "------------------------------------------------" << endl; + printHelp( ); + } + + // TODO Add some more useful commands here + } +} + +options_description CmisClient::getOptionsDescription( ) +{ + options_description desc( "Allowed options" ); + desc.add_options( ) + ( "help", "Produce help message and exists" ) + ( "verbose,v", "Show loads of useful messages for debugging" ) + ( "url", value< string >(), "URL of the binding of the server" ) + ( "repository,r", value< string >(), "Name of the repository to use" ) + ( "username,u", value< string >(), "Username used to authenticate to the repository" ) + ( "password,p", value< string >(), "Password used to authenticate to the repository" ) + ( "no-ssl-check", "Disable the verification of SSL certificates. This may come handy" + "for self-signed certificates for example, though it lowers the security" ) + ( "proxy", value< string >(), "HTTP proxy url to override the system settings" ) + ( "noproxy", value< string >(), "Coma separated list if host and domain names not going" + "through the proxy" ) + ( "proxy-username", value< string >(), "Username to authenticate on the proxy" ) + ( "proxy-password", value< string >(), "Password to authenticate on the proxy" ) + ( "oauth2-client-id", value< string >(), "OAuth2 application client_id" ) + ( "oauth2-client-secret", value< string >(), "OAuth2 application client_secret" ) + ( "oauth2-auth-url", value< string >(), "URL to authenticate in the OAuth2 flow" ) + ( "oauth2-token-url", value< string >(), "URL to convert code to tokens in the OAuth2 flow" ) + ( "oauth2-redirect-uri", value< string >(), "redirect URI indicating that the authentication is finished in OAuth2 flow" ) + ( "oauth2-scope", value< string >(), "The authentication scope in OAuth2" ) + ( "oauth2-auth-code", value< string >(), "The authentication code required to get the access token" ) + ; + + options_description setcontentOpts( "modification operations options" ); + setcontentOpts.add_options( ) + ( "input-file", value< string >(), "File to push to the repository" ) + ( "input-type", value< string >(), "Mime type of the file to push to the repository" ) + ( "input-name", value< string >(), "Name of the file to push to the repository" + "(may be different from the local name)" ) + ( "object-type", value< string >(), "CMIS type of the object to create" ) + ( "object-property", value< vector< string > >(), "under the form prop-id=prop-value, defines a property" + "to be set on the object" ) + ( "message,m", value< string >(), "Check in message" ) + ( "major", "The version to create during the check in will be a major version." ) + ( "stream-id", value< string >(), "streamId of the rendition to get content." ) + ; + + desc.add( setcontentOpts ); + return desc; +} + +void CmisClient::printHelp( ) +{ + cerr << "cmis-client [options] [command] arguments" << endl; + + cerr << endl << "Commands" << endl; + cerr << " list-repos\n" + " Lists the repositories available on the server" << endl; + cerr << " repo-infos\n" + " Show the informations and capabilities of the selected repository" << endl; + cerr << " show-root\n" + " Dump the root node of the repository." << endl; + cerr << " type-by-id <Type Id 1> [... <Type Id N>]\n" + " Dumps the type informations for all the ids." << endl; + cerr << " show-by-id <Object Id 1> [... <Object Id N>]\n" + " Dumps the objects informations for all the ids." << endl; + cerr << " show-by-path <Object Path 1> [... <Object Path N>]\n" + " Dumps the objects informations for all the paths." << endl; + cerr << " get-content <Object Id>\n" + " Saves the stream of the content object in the\n" + " current folder. Any existing file is overwritten.\n" + " streamId can be used to get the desired rendition with --stream-id"<< endl; + cerr << " set-content <Object Id>\n" + " Replaces the stream of the content object by the\n" + " file selected with --input-file." << endl; + cerr << " create-folder <Parent Id> <Folder Name>\n" + " Creates a new folder inside the folder <Parent Id> named <Folder Name>." << endl; + cerr << " create-document <Parent Id> <Document Name>\n" + " Creates a new document inside the folder <Parent Id>\n" + " named <Document Name>.\n" + " Note that --input-file and --input-type may be requested if\n" + " the server requires a content stream." << endl; + cerr << " update-object <Object Id>\n" + " Update the object matching id <Object Id> with the properties\n" + " defined with --object-property." << endl; + cerr << " move-object <Object Id> <Source Folder Id> <Destination Folder Id>\n" + " Move the object matching id <Object Id> from the\n" + " folder <Source Folder Id> to the folder <Destination Folder Id>." << endl; + cerr << " delete <Object Id 1> [... <Object Id N>]\n" + " Delete the objects corresponding to the ids. If the node\n" + " is a folder, its content will be removed as well." << endl; + cerr << " checkout <Object Id>\n" + " Check out the document corresponding to the id and shows the\n" + " Private Working Copy document infos." << endl; + cerr << " cancel-checkout <Object Id>\n" + " Cancel the Private Working Copy corresponding to the id" << endl; + cerr << " checkin <Object Id>\n" + " Check in the Private Working copy corresponding to the id.\n" + " Use the --message and --major parameters to give more\n" + " details about the new version.\n" + " The modification options may be needed to set the new\n" + " version properties and content stream if the repository\n" + " doesn't allow to change the private working copies." << endl; + cerr << " get-versions <Object-Id>\n" + " Show all the versions of a document." << endl; + cerr << " help\n" + " Prints this help message and exits (like --help option)." << endl; + + cerr << endl << getOptionsDescription() << endl; +} + +int main ( int argc, char* argv[] ) +{ + options_description hidden( "Hidden options" ); + hidden.add_options( ) + ( "command", value< string >(), "Command" ) + ( "args", value< vector< string > >(), "Arguments for the command" ) + ; + + options_description allOptions = CmisClient::getOptionsDescription( ); + allOptions.add( hidden ); + + positional_options_description pd; + pd.add( "command", 1 ); + pd.add( "args", -1 ); + + variables_map vm; + store( command_line_parser( argc, argv ).options( allOptions ).positional( pd ).run( ), vm ); + notify( vm ); + + CmisClient client( vm ); + try + { + client.execute( ); + } + catch ( const CommandException& e ) + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: " << e.what() << endl; + cerr << "------------------------------------------------" << endl; + client.printHelp(); + return 1; + } + catch ( const libcmis::Exception& e ) + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: " << e.what() << endl; + cerr << "------------------------------------------------" << endl; + return 1; + } + catch ( const exception& e ) + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: " << e.what() << endl; + cerr << "------------------------------------------------" << endl; + return 1; + } + + return 0; +} diff --git a/src/libcmis-c/Makefile.am b/src/libcmis-c/Makefile.am new file mode 100644 index 0000000..1510272 --- /dev/null +++ b/src/libcmis-c/Makefile.am @@ -0,0 +1,39 @@ + +libcmis_c_@LIBCMIS_API_VERSION@_la_CXXFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/inc/libcmis-c \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) + +libcmis_c_@LIBCMIS_API_VERSION@_la_CPPFLAGS = -DLIBCMIS_C_BUILD +if ENABLE_VISIBILITY +libcmis_c_@LIBCMIS_API_VERSION@_la_CXXFLAGS += -fvisibility=hidden +libcmis_c_@LIBCMIS_API_VERSION@_la_CPPFLAGS += -DLIBCMIS_C_VISIBILITY +endif + +lib_LTLIBRARIES = libcmis-c-@LIBCMIS_API_VERSION@.la +libcmis_c_@LIBCMIS_API_VERSION@_la_SOURCES = \ + allowable-actions.cxx \ + document.cxx \ + error.cxx \ + folder.cxx \ + internals.hxx \ + oauth2-data.cxx \ + object-type.cxx \ + object.cxx \ + property-type.cxx \ + property.cxx \ + rendition.cxx \ + repository.cxx \ + session-factory.cxx \ + session.cxx \ + vectors.cxx + +libcmis_c_@LIBCMIS_API_VERSION@_la_LDFLAGS = -export-dynamic -no-undefined -version-info 6:0:0 + +libcmis_c_@LIBCMIS_API_VERSION@_la_LIBADD = \ + ../libcmis/libcmis-@LIBCMIS_API_VERSION@.la \ + $(XML2_LIBS) \ + $(CURL_LIBS) \ + $(BOOST_SMART_PTR_LIBS) \ + $(BOOST_DATE_TIME_LIBS) diff --git a/src/libcmis-c/allowable-actions.cxx b/src/libcmis-c/allowable-actions.cxx new file mode 100644 index 0000000..f08f366 --- /dev/null +++ b/src/libcmis-c/allowable-actions.cxx @@ -0,0 +1,56 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/allowable-actions.h> + +#include "internals.hxx" + +void libcmis_allowable_actions_free( libcmis_AllowableActionsPtr allowable ) +{ + delete allowable; +} + + +bool libcmis_allowable_actions_isAllowed( libcmis_AllowableActionsPtr allowable, + libcmis_allowable_actions_Type action ) +{ + bool result = false; + if ( allowable != NULL && allowable->handle.get( ) != NULL ) + result = allowable->handle->isAllowed( libcmis::ObjectAction::Type( action ) ); + return result; +} + + +bool libcmis_allowable_actions_isDefined( libcmis_AllowableActionsPtr allowable, + libcmis_allowable_actions_Type action ) +{ + bool result = false; + if ( allowable != NULL && allowable->handle.get( ) != NULL ) + result = allowable->handle->isDefined( libcmis::ObjectAction::Type( action ) ); + return result; +} diff --git a/src/libcmis-c/document.cxx b/src/libcmis-c/document.cxx new file mode 100644 index 0000000..74d04d9 --- /dev/null +++ b/src/libcmis-c/document.cxx @@ -0,0 +1,448 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/document.h> + +#include "internals.hxx" + +using namespace std; +using libcmis::DocumentPtr; +using libcmis::FolderPtr; +using libcmis::PropertyPtrMap; +using boost::dynamic_pointer_cast; + +void libcmis_vector_document_free( libcmis_vector_document_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_document_size( libcmis_vector_document_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_DocumentPtr libcmis_vector_document_get( libcmis_vector_document_Ptr vector, size_t i ) +{ + libcmis_DocumentPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::DocumentPtr handle = vector->handle[i]; + item = new( nothrow ) libcmis_document( ); + item->handle = handle; + } + return item; +} + +bool libcmis_is_document( libcmis_ObjectPtr object ) +{ + bool isDocument = false; + if ( object != NULL && object->handle.get( ) != NULL ) + { + DocumentPtr document = dynamic_pointer_cast< libcmis::Document >( object->handle ); + isDocument = document.get( ) != NULL; + } + return isDocument; +} + + +libcmis_DocumentPtr libcmis_document_cast( libcmis_ObjectPtr object ) +{ + libcmis_DocumentPtr document = NULL; + + if ( object != NULL && object->handle.get( ) != NULL && + libcmis_is_document( object ) ) + { + document = new ( nothrow ) libcmis_document( ); + document->handle = object->handle; + } + + return document; +} + + +void libcmis_document_free( libcmis_DocumentPtr document ) +{ + delete document; +} + + +libcmis_vector_folder_Ptr libcmis_document_getParents( libcmis_DocumentPtr document, libcmis_ErrorPtr error ) +{ + libcmis_vector_folder_Ptr parents = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + vector< libcmis::FolderPtr > handles = doc->getParents( ); + parents = new libcmis_vector_folder( ); + parents->handle = handles; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return parents; +} + + +void libcmis_document_getContentStream( + libcmis_DocumentPtr document, + libcmis_writeFn writeFn, + void* userData, + libcmis_ErrorPtr error ) +{ + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + boost::shared_ptr< istream > stream = doc->getContentStream( ); + + stream->seekg( 0 ); + int bufSize = 2048; + char* buf = new char[ bufSize ]; + while ( !stream->eof( ) ) + { + stream->read( buf, bufSize ); + size_t read = stream->gcount( ); + writeFn( ( const void * )buf, size_t( 1 ), read, userData ); + } + delete[] buf; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + catch ( ... ) + { + } + } +} + + +void libcmis_document_setContentStream( + libcmis_DocumentPtr document, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* fileName, + bool overwrite, + libcmis_ErrorPtr error ) +{ + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + boost::shared_ptr< std::ostream > stream( new stringstream( ) ); + + size_t bufSize = 2048; + char* buf = new char[ bufSize ]; + size_t read = 0; + do + { + read = readFn( ( void * )buf, size_t( 1 ), bufSize, userData ); + stream->write( buf, read ); + } while ( read == bufSize ); + delete[] buf; + + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + doc->setContentStream( stream, contentType, fileName, overwrite ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + } +} + + +char* libcmis_document_getContentType( libcmis_DocumentPtr document ) +{ + char* value = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + value = strdup( doc->getContentType( ).c_str( ) ); + } + return value; +} + + +char* libcmis_document_getContentFilename( libcmis_DocumentPtr document ) +{ + char* value = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + value = strdup( doc->getContentFilename( ).c_str( ) ); + } + return value; +} + + +long libcmis_document_getContentLength( libcmis_DocumentPtr document ) +{ + long value = 0; + if ( document != NULL && document->handle.get( ) != NULL ) + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + value = doc->getContentLength( ); + } + return value; +} + + +libcmis_DocumentPtr libcmis_document_checkOut( libcmis_DocumentPtr document, libcmis_ErrorPtr error ) +{ + libcmis_DocumentPtr pwc = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + libcmis::DocumentPtr handle = doc->checkOut( ); + pwc= new libcmis_document( ); + pwc->handle = handle; + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + error->badAlloc = true; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } + return pwc; +} + + +void libcmis_document_cancelCheckout( libcmis_DocumentPtr document, libcmis_ErrorPtr error ) +{ + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + doc->cancelCheckout( ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + +libcmis_DocumentPtr libcmis_document_checkIn( + libcmis_DocumentPtr document, + bool isMajor, + const char* comment, + libcmis_vector_property_Ptr properties, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* filename, + libcmis_ErrorPtr error ) +{ + libcmis_DocumentPtr newVersion = NULL; + + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + // Create the ostream + boost::shared_ptr< std::ostream > stream( new stringstream( ) ); + + size_t bufSize = 2048; + char * buf = new char[ bufSize ]; + size_t read = 0; + do + { + read = readFn( ( void * )buf, size_t( 1 ), bufSize, userData ); + stream->write( buf, read ); + } while ( read == bufSize ); + delete[] buf; + + // Create the property map + PropertyPtrMap propertiesMap; + if ( properties != NULL ) + { + for ( vector< libcmis::PropertyPtr >::iterator it = properties->handle.begin( ); + it != properties->handle.end( ); ++it ) + { + string id = ( *it )->getPropertyType( )->getId( ); + propertiesMap.insert( pair< string, libcmis::PropertyPtr >( id, *it ) ); + } + } + + libcmis::DocumentPtr handle = doc->checkIn( isMajor, comment, propertiesMap, + stream, contentType, filename ); + newVersion = new libcmis_document( ); + newVersion->handle = handle; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + } + return newVersion; +} + +libcmis_vector_document_Ptr libcmis_document_getAllVersions( + libcmis_DocumentPtr document, + libcmis_ErrorPtr error ) +{ + libcmis_vector_document_Ptr result = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + std::vector< libcmis::DocumentPtr > handles = doc->getAllVersions( ); + result = new libcmis_vector_document( ); + result->handle = handles; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} diff --git a/src/libcmis-c/error.cxx b/src/libcmis-c/error.cxx new file mode 100644 index 0000000..8fdb681 --- /dev/null +++ b/src/libcmis-c/error.cxx @@ -0,0 +1,74 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/error.h> + +#include <stdlib.h> +#include <string.h> + +#include "internals.hxx" + +using namespace std; + +libcmis_ErrorPtr libcmis_error_create( ) +{ + libcmis_ErrorPtr error = new( nothrow ) libcmis_error( ); + return error; +} + + +void libcmis_error_free( libcmis_ErrorPtr error ) +{ + if ( error != NULL ) + { + free( error->message ); + free( error->type ); + delete error; + } +} + +const char* libcmis_error_getMessage( libcmis_ErrorPtr error ) +{ + if ( error != NULL ) + { + if ( error->badAlloc ) + return "Failed to allocate memory"; + else + return error->message; + } + else + return ""; +} + +const char* libcmis_error_getType( libcmis_ErrorPtr error ) +{ + if ( error != NULL ) + return error->type; + else + return NULL; +} diff --git a/src/libcmis-c/folder.cxx b/src/libcmis-c/folder.cxx new file mode 100644 index 0000000..8d7555a --- /dev/null +++ b/src/libcmis-c/folder.cxx @@ -0,0 +1,369 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/folder.h> + +#include "internals.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; +using libcmis::FolderPtr; +using boost::dynamic_pointer_cast; + +void libcmis_vector_folder_free( libcmis_vector_folder_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_folder_size( libcmis_vector_folder_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_FolderPtr libcmis_vector_folder_get( libcmis_vector_folder_Ptr vector, size_t i ) +{ + libcmis_FolderPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::FolderPtr handle = vector->handle[i]; + item = new ( nothrow ) libcmis_folder( ); + if ( item ) + item->handle = handle; + } + return item; +} + + +bool libcmis_is_folder( libcmis_ObjectPtr object ) +{ + bool isFolder = false; + if ( object != NULL && object->handle.get( ) != NULL ) + { + libcmis::FolderPtr folder = boost::dynamic_pointer_cast< libcmis::Folder >( object->handle ); + isFolder = folder.get( ) != NULL; + } + return isFolder; +} + + +libcmis_FolderPtr libcmis_folder_cast( libcmis_ObjectPtr object ) +{ + libcmis_FolderPtr folder = NULL; + + if ( object != NULL && object->handle.get( ) != NULL ) + { + libcmis::FolderPtr handle = boost::dynamic_pointer_cast< libcmis::Folder >( object->handle ); + if ( handle.get( ) != NULL ) + { + folder = new ( nothrow ) libcmis_folder( ); + if ( folder ) + folder->handle = handle; + } + } + + return folder; +} + + +void libcmis_folder_free( libcmis_FolderPtr folder ) +{ + delete folder; +} + + +libcmis_FolderPtr libcmis_folder_getParent( libcmis_FolderPtr folder, libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr parent = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + try + { + FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + libcmis::FolderPtr handle = folderHandle->getFolderParent( ); + if ( handle.get( ) != NULL ) + { + parent = new libcmis_folder( ); + parent->handle = handle; + } + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return parent; +} + + +libcmis_vector_object_Ptr libcmis_folder_getChildren( libcmis_FolderPtr folder, libcmis_ErrorPtr error ) +{ + libcmis_vector_object_Ptr result = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + try + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + std::vector< libcmis::ObjectPtr > handles = folderHandle->getChildren( ); + result = new libcmis_vector_object( ); + result->handle = handles; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} + + +char* libcmis_folder_getPath( libcmis_FolderPtr folder ) +{ + char* path = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + path = strdup( folderHandle->getPath( ).c_str( ) ); + } + return path; +} + + +bool libcmis_folder_isRootFolder( libcmis_FolderPtr folder ) +{ + bool isRoot = false; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + isRoot = folderHandle->isRootFolder( ); + } + return isRoot; +} + +libcmis_FolderPtr libcmis_folder_createFolder( + libcmis_FolderPtr folder, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr result = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + try + { + PropertyPtrMap mappedProperties; + if ( properties != NULL ) + { + size_t size = properties->handle.size( ); + for ( size_t i = 0; i < size; ++i ) + { + libcmis::PropertyPtr property = properties->handle[i]; + if ( property.get( ) != NULL ) + { + string id = property->getPropertyType( )->getId( ); + mappedProperties.insert( pair< string, libcmis::PropertyPtr >( id, property ) ); + } + } + } + + libcmis::FolderPtr handle = folderHandle->createFolder( mappedProperties ); + result = new libcmis_folder( ); + result->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + } + return result; +} + + +libcmis_DocumentPtr libcmis_folder_createDocument( + libcmis_FolderPtr folder, + libcmis_vector_property_Ptr properties, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* filename, + libcmis_ErrorPtr error ) +{ + libcmis_DocumentPtr created = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + try + { + // Create the ostream + boost::shared_ptr< std::ostream > stream( new stringstream( ) ); + + size_t bufSize = 2048; + char* buf = new char[ bufSize ]; + size_t read = 0; + do + { + read = readFn( ( void * )buf, size_t( 1 ), bufSize, userData ); + stream->write( buf, read ); + } while ( read == bufSize ); + delete[] buf; + + // Create the property map + PropertyPtrMap propertiesMap; + if ( properties != NULL ) + { + for ( vector< libcmis::PropertyPtr >::iterator it = properties->handle.begin( ); + it != properties->handle.end( ); ++it ) + { + string id = ( *it )->getPropertyType( )->getId( ); + propertiesMap.insert( pair< string, libcmis::PropertyPtr >( id, *it ) ); + } + } + + libcmis::DocumentPtr handle = folderHandle->createDocument( propertiesMap, stream, contentType, filename ); + created = new libcmis_document( ); + created->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + } + } + return created; +} + + +libcmis_vector_string_Ptr libcmis_folder_removeTree( libcmis_FolderPtr folder, + bool allVersion, + libcmis_folder_UnfileObjects unfile, + bool continueOnError, + libcmis_ErrorPtr error ) +{ + libcmis_vector_string_Ptr failed = NULL; + try + { + failed = new libcmis_vector_string( ); + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + vector< string > handle = folderHandle->removeTree( allVersion, + libcmis::UnfileObjects::Type( unfile ), continueOnError ); + failed->handle = handle; + } + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + return failed; +} diff --git a/src/libcmis-c/internals.hxx b/src/libcmis-c/internals.hxx new file mode 100644 index 0000000..e4a5b6b --- /dev/null +++ b/src/libcmis-c/internals.hxx @@ -0,0 +1,242 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_INTERNALS_H_ +#define _LIBCMIS_INTERNALS_H_ + +#include <vector> + +#include <libcmis/allowable-actions.hxx> +#include <libcmis/document.hxx> +#include <libcmis/exception.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> +#include <libcmis/property.hxx> +#include <libcmis/repository.hxx> +#include <libcmis/session.hxx> +#include <libcmis/session-factory.hxx> + +std::string createString( char* str ); + +struct libcmis_error +{ + char* message; + char* type; + bool badAlloc; + + libcmis_error( ) : message( NULL ), type( NULL ), badAlloc( false ) { } +}; + +struct libcmis_session +{ + libcmis::Session* handle; + libcmis::AuthProviderPtr provider; + + // Constructors + + libcmis_session( ) : + handle( NULL ), + provider( ) + { + } + + libcmis_session( const libcmis_session& copy ) : + handle( copy.handle ), + provider( copy.provider ) + { + } + + libcmis_session& operator=( const libcmis_session& copy ) + { + if ( this != © ) + { + handle = copy.handle; + provider = copy.provider; + } + return *this; + } +}; + +struct libcmis_repository +{ + libcmis::RepositoryPtr handle; + + libcmis_repository( ) : handle( ) { } +}; + +struct libcmis_object +{ + libcmis::ObjectPtr handle; + + libcmis_object( ) : handle( ) { } + virtual ~libcmis_object( ) { } +}; + +struct libcmis_object_type +{ + libcmis::ObjectTypePtr handle; + + libcmis_object_type( ) : handle( ) { } +}; + +struct libcmis_allowable_actions +{ + libcmis::AllowableActionsPtr handle; + + libcmis_allowable_actions( ) : handle ( ) { } +}; + +struct libcmis_property_type +{ + libcmis::PropertyTypePtr handle; + + libcmis_property_type( ) : handle( ) { } +}; + +struct libcmis_property +{ + libcmis::PropertyPtr handle; + + libcmis_property( ) : handle( ) { } +}; + +struct libcmis_folder : public libcmis_object +{ + libcmis_folder( ) : libcmis_object( ) { } +}; + +struct libcmis_document : public libcmis_object +{ + libcmis_document( ) : libcmis_object( ) { } +}; + +struct libcmis_oauth2data +{ + libcmis::OAuth2DataPtr handle; + + libcmis_oauth2data( ) : handle( ) { } +}; + +struct libcmis_rendition +{ + libcmis::RenditionPtr handle; + + libcmis_rendition( ) : handle( ) { } +}; + +struct libcmis_vector_bool +{ + std::vector< bool > handle; + + libcmis_vector_bool( ) : handle( ) { } +}; + +struct libcmis_vector_string +{ + std::vector< std::string > handle; + + libcmis_vector_string( ) : handle( ) { } +}; + +struct libcmis_vector_long +{ + std::vector< long > handle; + + libcmis_vector_long( ) : handle( ) { } +}; + +struct libcmis_vector_double +{ + std::vector< double > handle; + + libcmis_vector_double( ) : handle( ) { } +}; + +struct libcmis_vector_time +{ + std::vector< boost::posix_time::ptime > handle; + + libcmis_vector_time( ) : handle( ) { } +}; + +struct libcmis_vector_object_type +{ + std::vector< libcmis::ObjectTypePtr > handle; + + libcmis_vector_object_type( ) : handle( ) { } +}; + +struct libcmis_vector_property_type +{ + std::vector< libcmis::PropertyTypePtr > handle; + + libcmis_vector_property_type( ) : handle( ) { } +}; + +struct libcmis_vector_property +{ + std::vector< libcmis::PropertyPtr > handle; + + libcmis_vector_property( ) : handle( ) { } +}; + +struct libcmis_vector_object +{ + std::vector< libcmis::ObjectPtr > handle; + + libcmis_vector_object( ) : handle( ) { } +}; + +struct libcmis_vector_folder +{ + std::vector< libcmis::FolderPtr > handle; + + libcmis_vector_folder( ) : handle( ) { } +}; + +struct libcmis_vector_document +{ + std::vector< libcmis::DocumentPtr > handle; + + libcmis_vector_document( ) : handle( ) { } +}; + +struct libcmis_vector_repository +{ + std::vector< libcmis::RepositoryPtr > handle; + + libcmis_vector_repository( ) : handle( ) { } +}; + +struct libcmis_vector_rendition +{ + std::vector< libcmis::RenditionPtr > handle; + + libcmis_vector_rendition( ) : handle( ) { } +}; + +#endif diff --git a/src/libcmis-c/oauth2-data.cxx b/src/libcmis-c/oauth2-data.cxx new file mode 100644 index 0000000..9b7c69f --- /dev/null +++ b/src/libcmis-c/oauth2-data.cxx @@ -0,0 +1,110 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/oauth2-data.h> + +#include "internals.hxx" + +using namespace std; + + +libcmis_OAuth2DataPtr libcmis_oauth2data_create( + char* authUrl, char* tokenUrl, char* scope, char* redirectUri, + char* clientId, char* clientSecret ) +{ + libcmis_OAuth2DataPtr data = new( nothrow ) libcmis_oauth2data( ); + + if ( NULL != data ) + data->handle.reset( new libcmis::OAuth2Data( + authUrl, tokenUrl, scope, redirectUri, + clientId, clientSecret ) ); + return data; +} + + +void libcmis_oauth2data_free( libcmis_OAuth2DataPtr oauth2 ) +{ + delete oauth2; +} + + +bool libcmis_oauth2data_isComplete( libcmis_OAuth2DataPtr oauth2 ) +{ + bool result = false; + if ( oauth2 != NULL && oauth2->handle != NULL ) + result = oauth2->handle->isComplete(); + return result; +} + + +const char* libcmis_oauth2data_getAuthUrl( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getAuthUrl().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getTokenUrl( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getTokenUrl().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getClientId( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getClientId().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getClientSecret( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getClientSecret().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getScope( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getScope().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getRedirectUri( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getRedirectUri().c_str(); + return NULL; +} diff --git a/src/libcmis-c/object-type.cxx b/src/libcmis-c/object-type.cxx new file mode 100644 index 0000000..f1b3b9f --- /dev/null +++ b/src/libcmis-c/object-type.cxx @@ -0,0 +1,388 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/object-type.h> + +#include "internals.hxx" + +using namespace std; + + +void libcmis_vector_object_type_free( libcmis_vector_object_type_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_object_type_size( libcmis_vector_object_type_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_ObjectTypePtr libcmis_vector_object_type_get( libcmis_vector_object_type_Ptr vector, size_t i ) +{ + libcmis_ObjectTypePtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::ObjectTypePtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_object_type( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_object_type_free( libcmis_ObjectTypePtr type ) +{ + delete type; +} + + +char* libcmis_object_type_getId( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getLocalName( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getLocalNamespace( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalNamespace( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getQueryName( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getQueryName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getDisplayName( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getDisplayName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getDescription( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getDescription( ).c_str( ) ); + else + return NULL; +} + + +libcmis_ObjectTypePtr libcmis_object_type_getParentType( + libcmis_ObjectTypePtr type, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectTypePtr result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + try + { + libcmis::ObjectTypePtr handle = type->handle->getParentType( ); + if ( handle.get ( ) ) + { + result = new libcmis_object_type( ); + result->handle = handle; + } + } + catch( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + + return result; +} + + +libcmis_ObjectTypePtr libcmis_object_type_getBaseType( + libcmis_ObjectTypePtr type, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectTypePtr result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + try + { + libcmis::ObjectTypePtr handle = type->handle->getBaseType( ); + result = new libcmis_object_type( ); + result->handle = handle; + } + catch( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + + return result; +} + + +char* libcmis_object_type_getParentTypeId( libcmis_ObjectTypePtr type ) +{ + char* result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + result = strdup( type->handle->getParentTypeId( ).c_str() ); + } + + return result; +} + + +char* libcmis_object_type_getBaseTypeId( libcmis_ObjectTypePtr type ) +{ + char* result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + result = strdup( type->handle->getBaseTypeId( ).c_str() ); + } + + return result; +} + + +libcmis_vector_object_type_Ptr libcmis_object_type_getChildren( + libcmis_ObjectTypePtr type, libcmis_ErrorPtr error ) +{ + libcmis_vector_object_type_Ptr children = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + try + { + std::vector< libcmis::ObjectTypePtr > types = type->handle->getChildren( ); + children = new libcmis_vector_object_type( ); + children->handle = types; + } + catch( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + + return children; +} + + +bool libcmis_object_type_isCreatable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isCreatable( ); + return value; +} + + +bool libcmis_object_type_isFileable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isFileable( ); + return value; +} + + +bool libcmis_object_type_isQueryable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isQueryable( ); + return value; +} + + +bool libcmis_object_type_isFulltextIndexed( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isFulltextIndexed( ); + return value; +} + + +bool libcmis_object_type_isIncludedInSupertypeQuery( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isIncludedInSupertypeQuery( ); + return value; +} + + +bool libcmis_object_type_isControllablePolicy( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isControllablePolicy( ); + return value; +} + + +bool libcmis_object_type_isControllableACL( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isControllableACL( ); + return value; +} + + +bool libcmis_object_type_isVersionable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isVersionable( ); + return value; +} + + +libcmis_object_type_ContentStreamAllowed libcmis_object_type_getContentStreamAllowed( libcmis_ObjectTypePtr type ) +{ + libcmis_object_type_ContentStreamAllowed result = libcmis_NotAllowed; + if ( type != NULL && type->handle.get( ) != NULL ) + { + libcmis::ObjectType::ContentStreamAllowed value = type->handle->getContentStreamAllowed( ); + result = libcmis_object_type_ContentStreamAllowed( value ); + } + return result; +} + + +libcmis_vector_property_type_Ptr libcmis_object_type_getPropertiesTypes( libcmis_ObjectTypePtr type ) +{ + libcmis_vector_property_type_Ptr propertyTypes = NULL; + if ( type != NULL && type->handle != NULL ) + { + map< string, libcmis::PropertyTypePtr >& handles = type->handle->getPropertiesTypes( ); + propertyTypes = new ( nothrow ) libcmis_vector_property_type( ); + if ( propertyTypes ) + { + int i = 0; + for ( map< string, libcmis::PropertyTypePtr >::iterator it = handles.begin( ); + it != handles.end( ); ++it, ++i ) + { + propertyTypes->handle.push_back( it->second ); + } + } + } + + return propertyTypes; +} + +libcmis_PropertyTypePtr libcmis_object_type_getPropertyType( libcmis_ObjectTypePtr type, const char* id ) +{ + libcmis_PropertyTypePtr propertyType = NULL; + if ( type != NULL && type->handle != NULL ) + { + map< string, libcmis::PropertyTypePtr >& handles = type->handle->getPropertiesTypes( ); + map< string, libcmis::PropertyTypePtr >::iterator it = handles.find( string( id ) ); + if ( it != handles.end( ) ) + { + propertyType = new ( nothrow ) libcmis_property_type( ); + if ( propertyType ) + propertyType->handle = it->second; + } + } + + return propertyType; +} + + +char* libcmis_object_type_toString( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->toString( ).c_str( ) ); + else + return NULL; +} + + diff --git a/src/libcmis-c/object.cxx b/src/libcmis-c/object.cxx new file mode 100644 index 0000000..323cb31 --- /dev/null +++ b/src/libcmis-c/object.cxx @@ -0,0 +1,512 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/object.h> + +#include <libcmis-c/folder.h> + +#include "internals.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; +using boost::dynamic_pointer_cast; + +namespace +{ + string lcl_stdString( const char* str ) + { + string result; + if ( str ) + result = string( str ); + return result; + } + + PropertyPtrMap lcl_createPropertiesMap( libcmis_vector_property_Ptr properties ) + { + PropertyPtrMap propertiesMap; + if ( properties ) + { + for ( vector< libcmis::PropertyPtr >::iterator it = properties->handle.begin( ); + it != properties->handle.end( ); ++it ) + { + libcmis::PropertyPtr propHandle = *it; + propertiesMap[ propHandle->getPropertyType()->getId( ) ] = propHandle; + } + } + return propertiesMap; + } +} + +void libcmis_vector_object_free( libcmis_vector_object_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_object_size( libcmis_vector_object_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_ObjectPtr libcmis_vector_object_get( libcmis_vector_object_Ptr vector, size_t i ) +{ + libcmis_ObjectPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::ObjectPtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_object( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_object_free( libcmis_ObjectPtr object ) +{ + delete object; +} + + +char* libcmis_object_getId( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_getName( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getName( ).c_str( ) ); + else + return NULL; +} + +libcmis_vector_string_Ptr libcmis_object_getPaths( libcmis_ObjectPtr object ) +{ + libcmis_vector_string_Ptr c_paths = NULL; + if ( object != NULL && object->handle != NULL ) + { + std::vector< std::string > paths = object->handle->getPaths( ); + c_paths = new ( nothrow ) libcmis_vector_string( ); + if ( c_paths ) + c_paths->handle = paths; + } + return c_paths; +} + +char* libcmis_object_getBaseType( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getBaseType( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_getType( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getType( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_getCreatedBy( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getCreatedBy( ).c_str( ) ); + else + return NULL; +} + + +time_t libcmis_object_getCreationDate( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + { + tm time = boost::posix_time::to_tm( object->handle->getCreationDate( ) ); + return mktime( &time ); + } + else + return 0; +} + + +char* libcmis_object_getLastModifiedBy( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getLastModifiedBy( ).c_str( ) ); + else + return NULL; +} + + +time_t libcmis_object_getLastModificationDate( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + { + tm time = boost::posix_time::to_tm( object->handle->getLastModificationDate( ) ); + return mktime( &time ); + } + else + return 0; +} + + +char* libcmis_object_getChangeToken( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getChangeToken( ).c_str( ) ); + else + return NULL; +} + +char* libcmis_object_getThumbnailUrl( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getThumbnailUrl( ).c_str( ) ); + else + return NULL; +} + +libcmis_vector_rendition_Ptr libcmis_object_getRenditions( libcmis_ObjectPtr object, + libcmis_ErrorPtr error ) +{ + libcmis_vector_rendition_Ptr result = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + try + { + std::vector< libcmis::RenditionPtr > handles = object->handle->getRenditions( ); + result = new libcmis_vector_rendition( ); + result->handle = handles; + } + + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} + +bool libcmis_object_isImmutable( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return object->handle->isImmutable( ); + else + return true; +} + +libcmis_vector_string_Ptr libcmis_object_getSecondaryTypes( libcmis_ObjectPtr object ) +{ + libcmis_vector_string_Ptr c_types = NULL; + if ( object != NULL && object->handle != NULL ) + { + vector< string > types = object->handle->getSecondaryTypes( ); + c_types = new ( nothrow ) libcmis_vector_string( ); + if ( c_types ) + c_types->handle = types; + } + return c_types; +} + +libcmis_ObjectPtr +libcmis_object_addSecondaryType( libcmis_ObjectPtr object, + const char* id, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr updated = NULL; + if ( object != NULL && object->handle != NULL && properties != NULL ) + { + try + { + PropertyPtrMap propertiesMap = lcl_createPropertiesMap( properties ); + libcmis::ObjectPtr result = object->handle->addSecondaryType( + lcl_stdString( id ), + propertiesMap ); + updated = new libcmis_object( ); + updated->handle = result; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return updated; +} + +libcmis_ObjectPtr +libcmis_object_removeSecondaryType( libcmis_ObjectPtr object, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr updated = NULL; + if ( object != NULL && object->handle != NULL ) + { + try + { + libcmis::ObjectPtr result = object->handle->removeSecondaryType( + lcl_stdString( id ) ); + updated = new libcmis_object( ); + updated->handle = result; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return updated; +} + +libcmis_vector_property_Ptr libcmis_object_getProperties( libcmis_ObjectPtr object ) +{ + libcmis_vector_property_Ptr properties = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + PropertyPtrMap& handles = object->handle->getProperties( ); + properties = new ( nothrow ) libcmis_vector_property( ); + if ( properties ) + { + int i = 0; + for ( PropertyPtrMap::iterator it = handles.begin( ); + it != handles.end( ); ++it, ++i ) + { + properties->handle.push_back( it->second ); + } + } + } + return properties; +} + + +libcmis_PropertyPtr libcmis_object_getProperty( libcmis_ObjectPtr object, const char* name ) +{ + libcmis_PropertyPtr property = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + PropertyPtrMap& handles = object->handle->getProperties( ); + PropertyPtrMap::iterator it = handles.find( lcl_stdString( name ) ); + if ( it != handles.end( ) ) + { + property = new ( nothrow ) libcmis_property( ); + if ( property ) + property->handle = it->second; + } + } + return property; +} + + +libcmis_ObjectPtr libcmis_object_updateProperties( + libcmis_ObjectPtr object, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr result = NULL; + if ( object != NULL && object->handle != NULL && properties != NULL ) + { + try + { + // Build the map of changed properties + PropertyPtrMap propertiesMap = lcl_createPropertiesMap( properties ); + libcmis::ObjectPtr handle = object->handle->updateProperties( propertiesMap ); + result = new libcmis_object( ); + result->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} + + +libcmis_ObjectTypePtr libcmis_object_getTypeDescription( libcmis_ObjectPtr object ) +{ + libcmis_ObjectTypePtr result = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + result = new ( nothrow ) libcmis_object_type( ); + if ( result ) + result->handle = object->handle->getTypeDescription( ); + } + return result; +} + + +libcmis_AllowableActionsPtr libcmis_object_getAllowableActions( libcmis_ObjectPtr object ) +{ + libcmis_AllowableActionsPtr result = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + result = new ( nothrow ) libcmis_allowable_actions( ); + if ( result ) + result->handle = object->handle->getAllowableActions( ); + } + return result; +} + + +void libcmis_object_refresh( libcmis_ObjectPtr object, libcmis_ErrorPtr error ) +{ + if ( object != NULL && object->handle != NULL ) + { + try + { + object->handle->refresh( ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + + +time_t libcmis_object_getRefreshTimestamp( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return object->handle->getRefreshTimestamp( ); + else + return 0; +} + + +void libcmis_object_remove( libcmis_ObjectPtr object, bool allVersions, libcmis_ErrorPtr error ) +{ + if ( object != NULL && object->handle != NULL ) + { + try + { + object->handle->remove( allVersions ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + + +void libcmis_object_move( libcmis_ObjectPtr object, + libcmis_FolderPtr source, + libcmis_FolderPtr dest, + libcmis_ErrorPtr error ) +{ + if ( object != NULL && object->handle != NULL ) + { + try + { + libcmis::FolderPtr sourceHandle; + if ( source != NULL ) + sourceHandle = dynamic_pointer_cast< libcmis::Folder >( source->handle ); + libcmis::FolderPtr destHandle; + if ( dest != NULL ) + destHandle = dynamic_pointer_cast< libcmis::Folder >( dest->handle ); + + object->handle->move( sourceHandle, destHandle ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + + +char* libcmis_object_toString( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->toString( ).c_str( ) ); + else + return NULL; +} diff --git a/src/libcmis-c/property-type.cxx b/src/libcmis-c/property-type.cxx new file mode 100644 index 0000000..3f23939 --- /dev/null +++ b/src/libcmis-c/property-type.cxx @@ -0,0 +1,201 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/property-type.h> + +#include "internals.hxx" + +void libcmis_vector_property_type_free( libcmis_vector_property_type* vector ) +{ + delete vector; +} + + +size_t libcmis_vector_property_type_size( libcmis_vector_property_type* vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_PropertyTypePtr libcmis_vector_property_type_get( libcmis_vector_property_type* vector, size_t i ) +{ + libcmis_PropertyTypePtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::PropertyTypePtr type = vector->handle[i]; + item = new ( std::nothrow ) libcmis_property_type( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_property_type_free( libcmis_PropertyTypePtr type ) +{ + delete type; +} + + +char* libcmis_property_type_getId( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getLocalName( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getLocalNamespace( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalNamespace( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getDisplayName( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getDisplayName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getQueryName( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getQueryName( ).c_str( ) ); + else + return NULL; +} + + +libcmis_property_type_Type libcmis_property_type_getType( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return libcmis_property_type_Type( type->handle->getType( ) ); + else + return libcmis_String; +} + + +char* libcmis_property_type_getXmlType( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getXmlType( ).c_str( ) ); + else + return NULL; +} + + +bool libcmis_property_type_isMultiValued( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isMultiValued( ); + return value; +} + + +bool libcmis_property_type_isUpdatable( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isUpdatable( ); + return value; +} + + +bool libcmis_property_type_isInherited( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isInherited( ); + return value; +} + + +bool libcmis_property_type_isRequired( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isRequired( ); + return value; +} + + +bool libcmis_property_type_isQueryable( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isQueryable( ); + return value; +} + + +bool libcmis_property_type_isOrderable( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isOrderable( ); + return value; +} + + +bool libcmis_property_type_isOpenChoice( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isOpenChoice( ); + return value; +} + +void libcmis_property_type_update( libcmis_PropertyTypePtr propDef, + libcmis_vector_object_type_Ptr types ) +{ + if ( propDef != NULL && propDef->handle.get( ) != NULL && types != NULL ) + { + std::vector< libcmis::ObjectTypePtr > typesHandle = types->handle; + propDef->handle->update( typesHandle ); + } +} diff --git a/src/libcmis-c/property.cxx b/src/libcmis-c/property.cxx new file mode 100644 index 0000000..2886d55 --- /dev/null +++ b/src/libcmis-c/property.cxx @@ -0,0 +1,200 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/property.h> + +#include "internals.hxx" + +using namespace std; + + +libcmis_vector_property_Ptr libcmis_vector_property_create( ) +{ + return new ( nothrow ) libcmis_vector_property( ); +} + + +void libcmis_vector_property_free( libcmis_vector_property_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_property_size( libcmis_vector_property_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_PropertyPtr libcmis_vector_property_get( libcmis_vector_property_Ptr vector, size_t i ) +{ + libcmis_PropertyPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::PropertyPtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_property( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_vector_property_append( libcmis_vector_property_Ptr vector, libcmis_PropertyPtr item ) +{ + if ( vector != NULL && + item != NULL && item->handle.get( ) != NULL ) + { + vector->handle.push_back( item->handle ); + } +} + + +libcmis_PropertyPtr libcmis_property_create( libcmis_PropertyTypePtr type, const char** strValues, size_t size ) +{ + libcmis_PropertyPtr property = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + property = new ( nothrow ) libcmis_property( ); + if ( property ) + { + vector< string > values; + for ( size_t i = 0; i < size; ++i ) + values.push_back( string( strValues[i] ) ); + libcmis::PropertyPtr prop( new ( nothrow ) libcmis::Property( type->handle, values ) ); + property->handle = prop; + } + } + + return property; +} + + +void libcmis_property_free( libcmis_PropertyPtr property ) +{ + delete property; +} + + +libcmis_PropertyTypePtr libcmis_property_getPropertyType( libcmis_PropertyPtr property ) +{ + libcmis_PropertyTypePtr type = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + libcmis::PropertyTypePtr handle = property->handle->getPropertyType( ); + type = new ( nothrow ) libcmis_property_type( ); + if ( type ) + type->handle = handle; + } + return type; +} + + +libcmis_vector_time_Ptr libcmis_property_getDateTimes( libcmis_PropertyPtr property ) +{ + libcmis_vector_time_Ptr times = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< boost::posix_time::ptime > handles = property->handle->getDateTimes( ); + times = new ( nothrow ) libcmis_vector_time( ); + if ( times ) + times->handle = handles; + } + return times; +} + + +libcmis_vector_bool_Ptr libcmis_property_getBools( libcmis_PropertyPtr property ) +{ + libcmis_vector_bool_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< bool > handles = property->handle->getBools( ); + values = new ( nothrow ) libcmis_vector_bool( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +libcmis_vector_string_Ptr libcmis_property_getStrings( libcmis_PropertyPtr property ) +{ + libcmis_vector_string_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< string > handles = property->handle->getStrings( ); + values = new ( nothrow ) libcmis_vector_string( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +libcmis_vector_long_Ptr libcmis_property_getLongs( libcmis_PropertyPtr property ) +{ + libcmis_vector_long_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< long > handles = property->handle->getLongs( ); + values = new ( nothrow ) libcmis_vector_long( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +libcmis_vector_double_Ptr libcmis_property_getDoubles( libcmis_PropertyPtr property ) +{ + libcmis_vector_double_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< double > handles = property->handle->getDoubles( ); + values = new ( nothrow ) libcmis_vector_double( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +void libcmis_property_setValues( libcmis_PropertyPtr property, const char** strValues, size_t size ) +{ + if ( property != NULL && property->handle.get() != NULL ) + { + vector< string > values; + for ( size_t i = 0; i < size; ++i ) + values.push_back( string( strValues[i] ) ); + property->handle->setValues( values ); + } +} diff --git a/src/libcmis-c/rendition.cxx b/src/libcmis-c/rendition.cxx new file mode 100644 index 0000000..8adfd42 --- /dev/null +++ b/src/libcmis-c/rendition.cxx @@ -0,0 +1,110 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/rendition.h> + +#include "internals.hxx" + +using namespace std; + +void libcmis_rendition_free( libcmis_RenditionPtr rendition ) +{ + delete rendition; +} + +bool libcmis_rendition_isThumbnail( libcmis_RenditionPtr rendition ) +{ + bool result = false; + if ( rendition != NULL && rendition->handle != NULL ) + result = rendition->handle->isThumbnail(); + return result; +} + +const char* libcmis_rendition_getStreamId( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getStreamId().c_str(); + return NULL; +} + +const char* libcmis_rendition_getMimeType( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getMimeType().c_str(); + return NULL; +} + +const char* libcmis_rendition_getKind( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getKind().c_str(); + return NULL; +} + +const char* libcmis_rendition_getUrl( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getUrl().c_str(); + return NULL; +} + +const char* libcmis_rendition_getTitle( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getTitle().c_str(); + return NULL; +} + +long libcmis_rendition_getLength( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getLength(); + return -1; +} + +long libcmis_rendition_getWidth( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getWidth(); + return -1; +} + +long libcmis_rendition_getHeight( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getHeight(); + return -1; +} + +const char* libcmis_rendition_getRenditionDocumentId( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getRenditionDocumentId().c_str(); + return NULL; +} + diff --git a/src/libcmis-c/repository.cxx b/src/libcmis-c/repository.cxx new file mode 100644 index 0000000..41169a1 --- /dev/null +++ b/src/libcmis-c/repository.cxx @@ -0,0 +1,208 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/repository.h> + +#include "internals.hxx" + +using std::nothrow; + +void libcmis_vector_repository_free( libcmis_vector_Repository_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_repository_size( libcmis_vector_Repository_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_RepositoryPtr libcmis_vector_repository_get( libcmis_vector_Repository_Ptr vector, size_t i ) +{ + libcmis_RepositoryPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::RepositoryPtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_repository( ); + if ( item ) + item->handle = type; + } + return item; +} + + +libcmis_RepositoryPtr libcmis_repository_create( xmlNodePtr node ) +{ + libcmis_RepositoryPtr repository = new ( nothrow ) libcmis_repository( ); + + if ( repository ) + { + libcmis::RepositoryPtr handle( new ( nothrow ) libcmis::Repository( node ) ); + repository->handle = handle; + } + + return repository; +} + + +void libcmis_repository_free( libcmis_RepositoryPtr repository ) +{ + delete repository; +} + + +char* libcmis_repository_getId( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getName( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getDescription( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getDescription( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getVendorName( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getVendorName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getProductName( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getProductName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getProductVersion( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getProductVersion( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getRootId( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getRootId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getCmisVersionSupported( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getCmisVersionSupported( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getThinClientUri( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL && + repository->handle->getThinClientUri( ).get( ) != NULL ) + return strdup( repository->handle->getThinClientUri( )->c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getPrincipalAnonymous( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL && + repository->handle->getPrincipalAnonymous( ).get( ) != NULL ) + return strdup( repository->handle->getPrincipalAnonymous( )->c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getPrincipalAnyone( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL && + repository->handle->getPrincipalAnyone( ).get( ) != NULL ) + return strdup( repository->handle->getPrincipalAnyone( )->c_str( ) ); + else + return NULL; +} + +char* libcmis_repository_getCapability( + libcmis_RepositoryPtr repository, + libcmis_repository_capability_Type capability ) +{ + if ( repository != NULL && repository->handle != NULL ) + { + std::string value = repository->handle->getCapability( ( libcmis::Repository::Capability ) capability ); + return strdup( value.c_str( ) ); + } + else + return NULL; +} + +bool libcmis_repository_getCapabilityAsBool( + libcmis_RepositoryPtr repository, + libcmis_repository_capability_Type capability ) +{ + if ( repository != NULL && repository->handle != NULL ) + { + return repository->handle->getCapabilityAsBool( ( libcmis::Repository::Capability ) capability ); + } + else + return false; +} diff --git a/src/libcmis-c/session-factory.cxx b/src/libcmis-c/session-factory.cxx new file mode 100644 index 0000000..cee4532 --- /dev/null +++ b/src/libcmis-c/session-factory.cxx @@ -0,0 +1,239 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/session-factory.h> + +#include <map> +#include <string> +#include <stdlib.h> + +#include <libcmis/session-factory.hxx> + +#include <libcmis-c/session.h> +#include <libcmis-c/vectors.h> + +#include "internals.hxx" + +using namespace std; + +namespace +{ + size_t const CRED_MAX_LEN = 1024; + + class WrapperAuthProvider : public libcmis::AuthProvider + { + private: + libcmis_authenticationCallback m_callback; + + public: + WrapperAuthProvider( libcmis_authenticationCallback callback ) : + m_callback( callback ) + { + } + virtual ~WrapperAuthProvider( ) { }; + + virtual bool authenticationQuery( string& username, string& password ); + }; + + bool WrapperAuthProvider::authenticationQuery( string& username, string& password ) + { + /* NOTE: As I understand this, the callback is responsible for + * filling the correct username and password (possibly using + * the passed values as defaults in some dialog or so). But then + * there is no guarantee that the new username/password will + * not be longer than the present one, in which case it will + * not fit into the available space! For now, use a buffer size + * big enough for practical purposes. + * + * It might be a better idea to change the callback's signature + * to bool ( * )( char** username, char** password ) + * and make it the callee's responsibility to reallocate the + * strings if it needs to. + */ + char user[CRED_MAX_LEN]; + strncpy(user, username.c_str( ), sizeof( user ) ); + user[CRED_MAX_LEN - 1] = '\0'; + char pass[CRED_MAX_LEN]; + strncpy(pass, password.c_str( ), sizeof( pass ) ); + pass[CRED_MAX_LEN - 1] = '\0'; + + bool result = m_callback( user, pass ); + + // Update the username and password with the input + username = user; + password = pass; + + return result; + } + + + class WrapperCertHandler : public libcmis::CertValidationHandler + { + private: + libcmis_certValidationCallback m_callback; + public: + WrapperCertHandler( libcmis_certValidationCallback callback ) : + m_callback( callback ) + { + } + virtual ~WrapperCertHandler( ) { }; + + virtual bool validateCertificate( vector< string > certificatesChain ); + }; + + bool WrapperCertHandler::validateCertificate( vector< string > certificatesChain ) + { + libcmis_vector_string_Ptr chain = new ( nothrow ) libcmis_vector_string( ); + if ( chain ) + chain->handle = certificatesChain; + + bool result = m_callback( chain ); + + libcmis_vector_string_free( chain ); + return result; + } +} + +std::string createString( char* str ) +{ + if ( str ) + return string( str ); + else + return string( ); +} + +void libcmis_setAuthenticationCallback( libcmis_authenticationCallback callback ) +{ + libcmis::AuthProviderPtr provider( new ( nothrow ) WrapperAuthProvider( callback ) ); + if ( provider ) + libcmis::SessionFactory::setAuthenticationProvider( provider ); +} + +void libcmis_setCertValidationCallback( libcmis_certValidationCallback callback ) +{ + libcmis::CertValidationHandlerPtr handler( new ( nothrow )WrapperCertHandler( callback ) ); + if ( handler ) + libcmis::SessionFactory::setCertificateValidationHandler( handler ); +} + +void libcmis_setOAuth2AuthCodeProvider( libcmis_oauth2AuthCodeProvider callback ) +{ + libcmis::SessionFactory::setOAuth2AuthCodeProvider( callback ); +} + +libcmis_oauth2AuthCodeProvider libcmis_getOAuth2AuthCodeProvider( ) +{ + return libcmis::SessionFactory::getOAuth2AuthCodeProvider( ); +} + +void libcmis_setProxySettings( char* proxy, char* noProxy, + char* proxyUser, char* proxyPass ) +{ + libcmis::SessionFactory::setProxySettings( string( proxy ), string( noProxy ), + string( proxyUser ), string( proxyPass ) ); +} + +const char* libcmis_getProxy( ) +{ + return libcmis::SessionFactory::getProxy( ).c_str(); +} + +const char* libcmis_getNoProxy( ) +{ + return libcmis::SessionFactory::getNoProxy( ).c_str(); +} + +const char* libcmis_getProxyUser( ) +{ + return libcmis::SessionFactory::getProxyUser( ).c_str(); +} + +const char* libcmis_getProxyPass( ) +{ + return libcmis::SessionFactory::getProxyPass( ).c_str(); +} + +libcmis_SessionPtr libcmis_createSession( + char* bindingUrl, + char* repositoryId, + char* username, + char* password, + bool noSslCheck, + libcmis_OAuth2DataPtr oauth2, + bool verbose, + libcmis_ErrorPtr error ) +{ + libcmis_SessionPtr session = NULL; + + try + { + libcmis::OAuth2DataPtr oauth2Handle; + if ( oauth2 != NULL ) + oauth2Handle = oauth2->handle; + + libcmis::Session* handle = libcmis::SessionFactory::createSession( + createString( bindingUrl ), + createString( username ), + createString( password ), + createString( repositoryId ), noSslCheck, oauth2Handle, verbose ); + session = new libcmis_session( ); + session->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + + return session; +} + +libcmis_vector_Repository_Ptr libcmis_getRepositories( + char* bindingUrl, + char* username, + char* password, + bool verbose, + libcmis_ErrorPtr error ) +{ + libcmis_SessionPtr session = libcmis_createSession( + bindingUrl, NULL, username, password, false, NULL, verbose, error ); + libcmis_vector_Repository_Ptr repositories = libcmis_session_getRepositories( session ); + libcmis_session_free( session ); + return repositories; +} diff --git a/src/libcmis-c/session.cxx b/src/libcmis-c/session.cxx new file mode 100644 index 0000000..df6b503 --- /dev/null +++ b/src/libcmis-c/session.cxx @@ -0,0 +1,305 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/session.h> + +#include <utility> + +#include "internals.hxx" + +using namespace std; + +void libcmis_session_free( libcmis_SessionPtr session ) +{ + if ( session != NULL ) + { + delete session->handle; + delete session; + } +} + +libcmis_RepositoryPtr libcmis_session_getRepository( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ) +{ + libcmis_RepositoryPtr repository = NULL; + + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::RepositoryPtr handle = session->handle->getRepository( ); + if ( handle ) + { + repository = new ( nothrow ) libcmis_repository( ); + if ( repository ) + repository->handle = handle; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } + + return repository; +} + +libcmis_vector_Repository_Ptr libcmis_session_getRepositories( libcmis_SessionPtr session ) +{ + libcmis_vector_Repository_Ptr result = NULL; + if ( session != NULL && session->handle != NULL ) + { + vector< libcmis::RepositoryPtr > handles = session->handle->getRepositories(); + result = new ( nothrow ) libcmis_vector_repository( ); + if ( result ) + result->handle = handles; + } + + return result; +} + +bool libcmis_session_setRepository( libcmis_SessionPtr session, const char* id ) +{ + bool success = false; + if ( session && session->handle && id ) + { + success = session->handle->setRepository( id ); + } + return success; +} + +libcmis_FolderPtr libcmis_session_getRootFolder( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr folder = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::FolderPtr handle = session->handle->getRootFolder( ); + folder = new libcmis_folder( ); + folder->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return folder; +} + + +libcmis_ObjectPtr libcmis_session_getObject( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr object = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::ObjectPtr handle = session->handle->getObject( string( id ) ); + object = new libcmis_object( ); + object->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return object; +} + + +libcmis_ObjectPtr libcmis_session_getObjectByPath( + libcmis_SessionPtr session, + const char* path, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr object = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::ObjectPtr handle = session->handle->getObjectByPath( string( path ) ); + object = new libcmis_object( ); + object->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return object; +} + + +libcmis_FolderPtr libcmis_session_getFolder( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr folder = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::FolderPtr handle = session->handle->getFolder( string( id ) ); + folder = new libcmis_folder( ); + folder->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return folder; +} + + +libcmis_ObjectTypePtr libcmis_session_getType( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectTypePtr type = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::ObjectTypePtr handle = session->handle->getType( string( id ) ); + type = new libcmis_object_type( ); + type->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return type; +} + +libcmis_vector_object_type_Ptr libcmis_session_getBaseTypes( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ) +{ + libcmis_vector_object_type_Ptr types = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + vector< libcmis::ObjectTypePtr > handles = session->handle->getBaseTypes( ); + types = new libcmis_vector_object_type( ); + types->handle = handles; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return types; +} diff --git a/src/libcmis-c/vectors.cxx b/src/libcmis-c/vectors.cxx new file mode 100644 index 0000000..e520751 --- /dev/null +++ b/src/libcmis-c/vectors.cxx @@ -0,0 +1,139 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/vectors.h> + +#include "internals.hxx" + +void libcmis_vector_bool_free( libcmis_vector_bool_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_bool_size( libcmis_vector_bool_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +bool libcmis_vector_bool_get( libcmis_vector_bool_Ptr vector, size_t i ) +{ + bool item = false; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i]; + return item; +} + +void libcmis_vector_string_free( libcmis_vector_string_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_string_size( libcmis_vector_string_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +const char* libcmis_vector_string_get( libcmis_vector_string_Ptr vector, size_t i ) +{ + const char* item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i].c_str( ); + return item; +} + +void libcmis_vector_long_free( libcmis_vector_long_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_long_size( libcmis_vector_long_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +long libcmis_vector_long_get( libcmis_vector_long_Ptr vector, size_t i ) +{ + long item = 0; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i]; + return item; +} + +void libcmis_vector_double_free( libcmis_vector_double_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_double_size( libcmis_vector_double_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +double libcmis_vector_double_get( libcmis_vector_double_Ptr vector, size_t i ) +{ + double item = 0.0; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i]; + return item; +} + +void libcmis_vector_time_free( libcmis_vector_time_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_time_size( libcmis_vector_time_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +time_t libcmis_vector_time_get( libcmis_vector_time_Ptr vector, size_t i ) +{ + time_t item = 0; + if ( vector != NULL && i < vector->handle.size( ) ) + { + tm time = boost::posix_time::to_tm( vector->handle[i] ); + item = mktime( &time ); + } + return item; +} diff --git a/src/libcmis/Makefile.am b/src/libcmis/Makefile.am new file mode 100644 index 0000000..bc924f9 --- /dev/null +++ b/src/libcmis/Makefile.am @@ -0,0 +1,147 @@ +AM_CXXFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + $(XML2_CFLAGS) \ + $(CURL_CFLAGS) \ + $(BOOST_CPPFLAGS) + +if ENABLE_VISIBILITY +AM_CXXFLAGS += -fvisibility=hidden +endif + +noinst_LTLIBRARIES = libcmis.la + +lib_LTLIBRARIES = libcmis-@LIBCMIS_API_VERSION@.la + +libcmis_@LIBCMIS_API_VERSION@_la_SOURCES = \ + dummy.cxx + +libcmis_la_CPPFLAGS = -DLIBCMIS_BUILD +if ENABLE_VISIBILITY +libcmis_la_CPPFLAGS += -DLIBCMIS_VISIBILITY +endif + +libcmis_la_SOURCES = \ + allowable-actions.cxx \ + atom-document.cxx \ + atom-document.hxx \ + atom-folder.cxx \ + atom-folder.hxx \ + atom-object-type.cxx \ + atom-object-type.hxx \ + atom-object.cxx \ + atom-object.hxx \ + atom-session.cxx \ + atom-session.hxx \ + atom-workspace.cxx \ + atom-workspace.hxx \ + base-session.cxx \ + base-session.hxx \ + document.cxx \ + folder.cxx \ + gdrive-allowable-actions.hxx \ + gdrive-document.cxx \ + gdrive-document.hxx \ + gdrive-folder.cxx \ + gdrive-folder.hxx \ + gdrive-object-type.cxx \ + gdrive-object-type.hxx \ + gdrive-object.cxx \ + gdrive-object.hxx \ + gdrive-property.cxx \ + gdrive-property.hxx \ + gdrive-repository.cxx \ + gdrive-repository.hxx \ + gdrive-session.cxx \ + gdrive-session.hxx \ + gdrive-utils.cxx \ + gdrive-utils.hxx \ + http-session.cxx \ + http-session.hxx \ + json-utils.cxx \ + json-utils.hxx \ + oauth2-data.cxx \ + oauth2-handler.cxx \ + oauth2-handler.hxx \ + oauth2-providers.cxx \ + oauth2-providers.hxx \ + object-type.cxx \ + object.cxx \ + onedrive-allowable-actions.hxx \ + onedrive-document.cxx \ + onedrive-document.hxx \ + onedrive-folder.cxx \ + onedrive-folder.hxx \ + onedrive-object-type.cxx \ + onedrive-object-type.hxx \ + onedrive-object.cxx \ + onedrive-object.hxx \ + onedrive-property.cxx \ + onedrive-property.hxx \ + onedrive-repository.cxx \ + onedrive-repository.hxx \ + onedrive-session.cxx \ + onedrive-session.hxx \ + onedrive-utils.cxx \ + onedrive-utils.hxx \ + property-type.cxx \ + property.cxx \ + rendition.cxx \ + repository.cxx \ + session-factory.cxx \ + sharepoint-allowable-actions.hxx \ + sharepoint-document.cxx \ + sharepoint-document.hxx \ + sharepoint-folder.cxx \ + sharepoint-folder.hxx \ + sharepoint-object-type.cxx \ + sharepoint-object-type.hxx \ + sharepoint-object.cxx \ + sharepoint-object.hxx \ + sharepoint-property.cxx \ + sharepoint-property.hxx \ + sharepoint-repository.cxx \ + sharepoint-repository.hxx \ + sharepoint-session.cxx \ + sharepoint-session.hxx \ + sharepoint-utils.cxx \ + sharepoint-utils.hxx \ + ws-document.cxx \ + ws-document.hxx \ + ws-folder.cxx \ + ws-folder.hxx \ + ws-navigationservice.cxx \ + ws-navigationservice.hxx \ + ws-object-type.cxx \ + ws-object-type.hxx \ + ws-object.cxx \ + ws-object.hxx \ + ws-objectservice.cxx \ + ws-objectservice.hxx \ + ws-relatedmultipart.cxx \ + ws-relatedmultipart.hxx \ + ws-repositoryservice.cxx \ + ws-repositoryservice.hxx \ + ws-requests.cxx \ + ws-requests.hxx \ + ws-session.cxx \ + ws-session.hxx \ + ws-soap.cxx \ + ws-soap.hxx \ + ws-versioningservice.cxx \ + ws-versioningservice.hxx \ + xml-utils.cxx + +# -version-info current:revision:age see https://autotools.info/libtool/version.html +# Always increase the revision value. +# Increase the current value whenever an interface has been added, removed or changed. +# Increase the age value only if the changes made to the ABI are backward compatible. +libcmis_@LIBCMIS_API_VERSION@_la_LDFLAGS = -export-dynamic -no-undefined -version-info 7:1:1 + +libcmis_@LIBCMIS_API_VERSION@_la_LIBADD = \ + libcmis.la \ + $(XML2_LIBS) \ + $(CURL_LIBS) \ + $(BOOST_SMART_PTR_LIBS) \ + $(BOOST_DATE_TIME_LDFLAGS) \ + $(BOOST_DATE_TIME_LIBS) diff --git a/src/libcmis/allowable-actions.cxx b/src/libcmis/allowable-actions.cxx new file mode 100644 index 0000000..533069c --- /dev/null +++ b/src/libcmis/allowable-actions.cxx @@ -0,0 +1,294 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/allowable-actions.hxx> + +#include <libcmis/object.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + ObjectAction::ObjectAction( xmlNodePtr node ) : + m_type( ObjectAction::DeleteObject ), + m_enabled( false ), + m_valid( false ) + { + try + { + m_type = parseType( string( ( char* ) node->name ) ); + m_valid = true; + } + catch ( const Exception& ) + { + m_valid = false; + } + + // Invalid xsd:bool will be mean false... not sure what the spec says + try + { + xmlChar* content = xmlNodeGetContent( node ); + m_enabled = parseBool( string( ( char* )content ) ); + xmlFree( content ); + } + catch ( const Exception& ) + { + m_enabled = false; + } + } + + ObjectAction::Type ObjectAction::parseType( string type ) + { + Type value = DeleteObject; + if ( type == "canDeleteObject" ) + value = DeleteObject; + else if ( type == "canUpdateProperties" ) + value = UpdateProperties; + else if ( type == "canGetFolderTree" ) + value = GetFolderTree; + else if ( type == "canGetProperties" ) + value = GetProperties; + else if ( type == "canGetObjectRelationships" ) + value = GetObjectRelationships; + else if ( type == "canGetObjectParents" ) + value = GetObjectParents; + else if ( type == "canGetFolderParent" ) + value = GetFolderParent; + else if ( type == "canGetDescendants" ) + value = GetDescendants; + else if ( type == "canMoveObject" ) + value = MoveObject; + else if ( type == "canDeleteContentStream" ) + value = DeleteContentStream; + else if ( type == "canCheckOut" ) + value = CheckOut; + else if ( type == "canCancelCheckOut" ) + value = CancelCheckOut; + else if ( type == "canCheckIn" ) + value = CheckIn; + else if ( type == "canSetContentStream" ) + value = SetContentStream; + else if ( type == "canGetAllVersions" ) + value = GetAllVersions; + else if ( type == "canAddObjectToFolder" ) + value = AddObjectToFolder; + else if ( type == "canRemoveObjectFromFolder" ) + value = RemoveObjectFromFolder; + else if ( type == "canGetContentStream" ) + value = GetContentStream; + else if ( type == "canApplyPolicy" ) + value = ApplyPolicy; + else if ( type == "canGetAppliedPolicies" ) + value = GetAppliedPolicies; + else if ( type == "canRemovePolicy" ) + value = RemovePolicy; + else if ( type == "canGetChildren" ) + value = GetChildren; + else if ( type == "canCreateDocument" ) + value = CreateDocument; + else if ( type == "canCreateFolder" ) + value = CreateFolder; + else if ( type == "canCreateRelationship" ) + value = CreateRelationship; + else if ( type == "canDeleteTree" ) + value = DeleteTree; + else if ( type == "canGetRenditions" ) + value = GetRenditions; + else if ( type == "canGetACL" ) + value = GetACL; + else if ( type == "canApplyACL" ) + value = ApplyACL; + else + throw Exception( "Invalid AllowableAction type: " + type ); + + return value; + } + + AllowableActions::AllowableActions( ) : + m_states( ) + { + } + + AllowableActions::AllowableActions( xmlNodePtr node ) : + m_states( ) + { + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + // Check for non text children... "\n" is also a node ;) + if ( !xmlNodeIsText( child ) ) + { + ObjectAction action( child ); + if ( action.isValid( ) ) + m_states.insert( pair< libcmis::ObjectAction::Type, bool >( + action.getType( ), + action.isEnabled() ) ); + } + } + } + + AllowableActions::AllowableActions( const AllowableActions& copy ) : + m_states( copy.m_states ) + { + } + + AllowableActions::~AllowableActions( ) + { + m_states.clear(); + } + + AllowableActions& AllowableActions::operator=( const AllowableActions& copy ) + { + if ( this != © ) + m_states = copy.m_states; + + return *this; + } + + bool AllowableActions::isAllowed( ObjectAction::Type action ) + { + bool allowed = false; + + map< ObjectAction::Type, bool>::iterator it = m_states.find( action ); + if ( it != m_states.end() ) + allowed = it->second; + + return allowed; + } + + bool AllowableActions::isDefined( ObjectAction::Type action ) + { + map< ObjectAction::Type, bool>::iterator it = m_states.find( action ); + return it != m_states.end(); + } + + // LCOV_EXCL_START + string AllowableActions::toString( ) + { + stringstream buf; + + for ( map< ObjectAction::Type, bool >::iterator it = m_states.begin( ); + it != m_states.end( ); ++it ) + { + switch ( it->first ) + { + case ObjectAction::DeleteObject: + buf << "canDeleteObject"; + break; + case ObjectAction::UpdateProperties: + buf << "canUpdateProperties"; + break; + case ObjectAction::GetFolderTree: + buf << "canGetFolderTree"; + break; + case ObjectAction::GetProperties: + buf << "canGetProperties"; + break; + case ObjectAction::GetObjectRelationships: + buf << "canGetObjectRelationships"; + break; + case ObjectAction::GetObjectParents: + buf << "canGetObjectParents"; + break; + case ObjectAction::GetFolderParent: + buf << "canGetFolderParent"; + break; + case ObjectAction::GetDescendants: + buf << "canGetDescendants"; + break; + case ObjectAction::MoveObject: + buf << "canMoveObject"; + break; + case ObjectAction::DeleteContentStream: + buf << "canDeleteContentStream"; + break; + case ObjectAction::CheckOut: + buf << "canCheckOut"; + break; + case ObjectAction::CancelCheckOut: + buf << "canCancelCheckOut"; + break; + case ObjectAction::CheckIn: + buf << "canCheckIn"; + break; + case ObjectAction::SetContentStream: + buf << "canSetContentStream"; + break; + case ObjectAction::GetAllVersions: + buf << "canGetAllVersions"; + break; + case ObjectAction::AddObjectToFolder: + buf << "canAddObjectToFolder"; + break; + case ObjectAction::RemoveObjectFromFolder: + buf << "canRemoveObjectFromFolder"; + break; + case ObjectAction::GetContentStream: + buf << "canGetContentStream"; + break; + case ObjectAction::ApplyPolicy: + buf << "canApplyPolicy"; + break; + case ObjectAction::GetAppliedPolicies: + buf << "canGetAppliedPolicies"; + break; + case ObjectAction::RemovePolicy: + buf << "canRemovePolicy"; + break; + case ObjectAction::GetChildren: + buf << "canGetChildren"; + break; + case ObjectAction::CreateDocument: + buf << "canCreateDocument"; + break; + case ObjectAction::CreateFolder: + buf << "canCreateFolder"; + break; + case ObjectAction::CreateRelationship: + buf << "canCreateRelationship"; + break; + case ObjectAction::DeleteTree: + buf << "canDeleteTree"; + break; + case ObjectAction::GetRenditions: + buf << "canGetRenditions"; + break; + case ObjectAction::GetACL: + buf << "canGetACL"; + break; + case ObjectAction::ApplyACL: + buf << "canApplyACL"; + break; + } + buf << ": " << it->second << endl; + } + + return buf.str( ); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/atom-document.cxx b/src/libcmis/atom-document.cxx new file mode 100644 index 0000000..e0400b1 --- /dev/null +++ b/src/libcmis/atom-document.cxx @@ -0,0 +1,480 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-document.hxx" + +#include <algorithm> +#include <stdlib.h> +#include <sstream> + +#include <curl/curl.h> + +#include <libcmis/xml-utils.hxx> + +#include "atom-session.hxx" + +using namespace std; +using namespace libcmis; + +AtomDocument::AtomDocument( AtomPubSession* session ) : + libcmis::Object( session ), + libcmis::Document( session ), + AtomObject( session ), + m_contentUrl( ) +{ +} + + +AtomDocument::AtomDocument( AtomPubSession* session, xmlNodePtr entryNd ) : + libcmis::Object( session ), + libcmis::Document( session ), + AtomObject( session ), + m_contentUrl( ) +{ + xmlDocPtr doc = libcmis::wrapInDoc( entryNd ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + +AtomDocument::~AtomDocument( ) +{ +} + +vector< libcmis::FolderPtr > AtomDocument::getParents( ) +{ + AtomLink* parentsLink = getLink( "up", "" ); + + if ( ( NULL == parentsLink ) || + ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetObjectParents ) ) ) + throw libcmis::Exception( string( "GetObjectParents not allowed on node " ) + getId() ); + + vector< libcmis::FolderPtr > parents; + + string buf; + try + { + buf = getSession()->httpGetRequest( parentsLink->getHref( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), parentsLink->getHref( ).c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + xmlDocPtr entryDoc = libcmis::wrapInDoc( node ); + libcmis::ObjectPtr object = getSession()->createObjectFromEntryDoc( entryDoc ); + libcmis::FolderPtr folder = boost::dynamic_pointer_cast< libcmis::Folder >( object ); + + if ( folder.get() ) + parents.push_back( folder ); + xmlFreeDoc( entryDoc ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse folder infos" ); + } + xmlFreeDoc( doc ); + + return parents; +} + +boost::shared_ptr< istream > AtomDocument::getContentStream( string /*streamId*/ ) +{ + if ( getAllowableActions().get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetContentStream ) ) + throw libcmis::Exception( string( "GetContentStream is not allowed on document " ) + getId() ); + + boost::shared_ptr< istream > stream; + try + { + stream = getSession()->httpGetRequest( m_contentUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + return stream; +} + +void AtomDocument::setContentStream( boost::shared_ptr< ostream > os, string contentType, string fileName, bool overwrite ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + if ( getAllowableActions().get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetContentStream ) ) + throw libcmis::Exception( string( "SetContentStream is not allowed on document " ) + getId() ); + + string overwriteStr( "false" ); + if ( overwrite ) + overwriteStr = "true"; + + string urlPattern( m_contentUrl ); + if ( urlPattern.find( '?' ) != string::npos ) + urlPattern += "&"; + else + urlPattern += "?"; + urlPattern += "overwriteFlag={overwriteFlag}"; + + map< string, string > params; + params["overwriteFlag"] = overwriteStr; + + // Use the changeToken if set on the object + if ( !getChangeToken().empty() ) + { + urlPattern += "&changeToken={changeToken}"; + params["changeToken"] = getChangeToken(); + } + + string putUrl = getSession()->createUrl( urlPattern, params ); + + bool tryBase64 = false; + do + { + try + { + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + if ( tryBase64 ) + { + tryBase64 = false; + + // Encode the content + stringstream* encodedIn = new stringstream( ); + libcmis::EncodedData encoder( encodedIn ); + encoder.setEncoding( "base64" ); + + int bufLength = 1000; + char* buf = new char[ bufLength ]; + do + { + is->read( buf, bufLength ); + int size = is->gcount( ); + encoder.encode( buf, 1, size ); + } while ( !is->eof( ) && !is->fail( ) ); + delete[] buf; + encoder.finish( ); + + encodedIn->seekg( 0, ios_base::beg ); + encodedIn->clear( ); + + is.reset( encodedIn ); + } + vector< string > headers; + headers.push_back( string( "Content-Type: " ) + contentType ); + if ( !fileName.empty( ) ) + headers.push_back( string( "Content-Disposition: attachment; filename=" ) + fileName ); + getSession()->httpPutRequest( putUrl, *is, headers ); + + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + throw libcmis::Exception( "Document content wasn't set for some reason" ); + refresh( ); + } + catch ( const CurlException& e ) + { + // SharePoint wants base64 encoded content... let's try to figure out + // if we falled in that case. + if ( !tryBase64 && e.getHttpStatus() == 400 ) + tryBase64 = true; + else + throw e.getCmisException( ); + } + } + while ( tryBase64 ); +} + +libcmis::DocumentPtr AtomDocument::checkOut( ) +{ + if ( ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::CheckOut ) ) ) + throw libcmis::Exception( string( "CanCheckout not allowed on document " ) + getId() ); + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + // Create a document with only the needed properties + PropertyPtrMap props; + PropertyPtrMap::iterator it = getProperties( ).find( string( "cmis:objectId" ) ); + if ( it != getProperties( ).end( ) ) + { + props.insert( *it ); + } + + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, props, stream, string( ) ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + libcmis::HttpResponsePtr resp; + string urlPattern = getSession()->getAtomRepository( )->getCollectionUrl( Collection::CheckedOut ); + if ( urlPattern.find( "?" ) != string::npos ) + urlPattern += "&"; + else + urlPattern += "?"; + urlPattern += "objectId={objectId}"; + + map< string, string > params; + params[ "objectId" ] = getId( ); + string checkedOutUrl = getSession( )->createUrl( urlPattern, params ); + + try + { + resp = getSession( )->httpPostRequest( checkedOutUrl, is, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string respBuf = resp->getStream( )->str(); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), checkedOutUrl.c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc, AtomPubSession::RESULT_DOCUMENT ); + xmlFreeDoc( doc ); + + libcmis::DocumentPtr pwc = boost::dynamic_pointer_cast< libcmis::Document >( created ); + if ( !pwc.get( ) ) + throw libcmis::Exception( string( "Created object is not a document: " ) + created->getId( ) ); + + return pwc; +} + +void AtomDocument::cancelCheckout( ) +{ + if ( ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::CancelCheckOut ) ) ) + throw libcmis::Exception( string( "CanCancelCheckout not allowed on document " ) + getId() ); + + string url = getInfosUrl( ); + + // Use working-copy link if provided as a workaround + // for some non-compliant repositories + AtomLink* link = getLink( "working-copy", "application/atom+xml;type=entry" ); + if ( link ) + url = link->getHref( ); + + try + { + getSession( )->httpDeleteRequest( url ); + } + catch ( CurlException const& e ) + { + throw e.getCmisException( ); + } +} + +libcmis::DocumentPtr AtomDocument::checkIn( bool isMajor, string comment, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > stream, string contentType, string ) +{ + if ( ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::CheckIn ) ) ) + throw libcmis::Exception( string( "CanCheckIn not allowed on document " ) + getId() ); + + string urlPattern = getInfosUrl( ); + + // Use working-copy link if provided as a workaround + // for some non-compliant repositories + AtomLink* link = getLink( "working-copy", "application/atom+xml;type=entry" ); + if ( link ) + urlPattern = link->getHref( ); + + if ( urlPattern.find( "?" ) != string::npos ) + urlPattern += "&"; + else + urlPattern += "?"; + urlPattern += "checkin=true&major={major}&checkinComment={checkinComment}"; + + map< string, string > params; + + string majorStr = "false"; + if ( isMajor ) + majorStr = "true"; + params[ "major" ] = majorStr; + params[ "checkinComment" ] = comment; + string checkInUrl = getSession( )->createUrl( urlPattern, params ); + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + AtomObject::writeAtomEntry( writer, properties, stream, contentType ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + // Run the request + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( string( "Content-Type: application/atom+xml;type=entry" ) ); + response = getSession( )->httpPutRequest( checkInUrl, is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // Get the returned entry and update using it + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), checkInUrl.c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + + libcmis::ObjectPtr newVersion = getSession( )->createObjectFromEntryDoc( doc, AtomPubSession::RESULT_DOCUMENT ); + + if ( newVersion->getId( ) == getId( ) ) + refreshImpl( doc ); + xmlFreeDoc( doc ); + + return boost::dynamic_pointer_cast< libcmis::Document >( newVersion ); +} + +vector< libcmis::DocumentPtr > AtomDocument::getAllVersions( ) +{ + if ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetAllVersions ) ) + throw libcmis::Exception( string( "GetAllVersions not allowed on node " ) + getId() ); + + vector< libcmis::DocumentPtr > versions; + AtomLink* link = getLink( "version-history", string( ) ); + if ( link != NULL ) + { + string pageUrl = link->getHref( ); + + string buf; + try + { + buf = getSession()->httpGetRequest( pageUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), pageUrl.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + // Get the entries + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + xmlDocPtr entryDoc = libcmis::wrapInDoc( node ); + libcmis::ObjectPtr cmisObject = getSession()->createObjectFromEntryDoc( entryDoc ); + libcmis::DocumentPtr cmisDoc = boost::dynamic_pointer_cast< libcmis::Document >( cmisObject ); + + if ( cmisDoc.get() ) + versions.push_back( cmisDoc ); + xmlFreeDoc( entryDoc ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse versions infos" ); + } + xmlFreeDoc( doc ); + + } + return versions; +} + +void AtomDocument::extractInfos( xmlDocPtr doc ) +{ + AtomObject::extractInfos( doc ); + + // Get the content url + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + if ( NULL != doc ) + { + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//atom:content" ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlNodePtr contentNd = xpathObj->nodesetval->nodeTab[0]; + xmlChar* src = xmlGetProp( contentNd, BAD_CAST( "src" ) ); + m_contentUrl = string( ( char* ) src ); + xmlFree( src ); + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } +} diff --git a/src/libcmis/atom-document.hxx b/src/libcmis/atom-document.hxx new file mode 100644 index 0000000..40d7109 --- /dev/null +++ b/src/libcmis/atom-document.hxx @@ -0,0 +1,66 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_DOCUMENT_HXX_ +#define _ATOM_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "atom-object.hxx" + +class AtomDocument : public libcmis::Document, public AtomObject +{ + private: + std::string m_contentUrl; + + public: + AtomDocument( AtomPubSession* session ); + AtomDocument( AtomPubSession* session, xmlNodePtr entryNd ); + ~AtomDocument( ); + + virtual std::vector< libcmis::FolderPtr > getParents( ); + + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, std::string contentType, + std::string fileName, bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + virtual libcmis::DocumentPtr checkIn( bool isMajor, std::string comment, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); + + protected: + virtual void extractInfos( xmlDocPtr doc ); +}; + +#endif diff --git a/src/libcmis/atom-folder.cxx b/src/libcmis/atom-folder.cxx new file mode 100644 index 0000000..5e41194 --- /dev/null +++ b/src/libcmis/atom-folder.cxx @@ -0,0 +1,322 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-folder.hxx" + +#include <sstream> + +#include <boost/shared_ptr.hpp> + +#include <libcmis/xml-utils.hxx> + +#include "atom-document.hxx" +#include "atom-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace +{ +} + +AtomFolder::AtomFolder( AtomPubSession* session, xmlNodePtr entryNd ) : + libcmis::Object( session ), + libcmis::Folder( session ), + AtomObject( session ) +{ + xmlDocPtr doc = libcmis::wrapInDoc( entryNd ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + + +AtomFolder::~AtomFolder( ) +{ +} + +vector< libcmis::ObjectPtr > AtomFolder::getChildren( ) +{ + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + + // Some servers aren't giving the GetChildren properly... if not defined, we need to try + // as we may have the right to proceed. + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && + ( !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetChildren ) && + getAllowableActions()->isDefined( libcmis::ObjectAction::GetChildren ) ) ) ) + throw libcmis::Exception( string( "GetChildren not allowed on node " ) + getId() ); + + vector< libcmis::ObjectPtr > children; + + string pageUrl = childrenLink->getHref( ); + + bool hasNext = true; + while ( hasNext ) + { + string buf; + try + { + buf = getSession()->httpGetRequest( pageUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), pageUrl.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + // Check if there is a next link to handled paged results + const string& nextReq( "/atom:feed/atom:link[@rel='next']/attribute::href" ); + string nextHref = libcmis::getXPathValue( xpathCtx, nextReq ); + hasNext = !nextHref.empty( ); + if ( hasNext ) + pageUrl = nextHref; + + // Get the page entries + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + xmlDocPtr entryDoc = libcmis::wrapInDoc( node ); + libcmis::ObjectPtr cmisObject = getSession()->createObjectFromEntryDoc( entryDoc ); + + if ( cmisObject.get() ) + children.push_back( cmisObject ); + xmlFreeDoc( entryDoc ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse folder infos" ); + } + xmlFreeDoc( doc ); + } + + return children; +} + +libcmis::FolderPtr AtomFolder::createFolder( const PropertyPtrMap& properties ) +{ + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateFolder ) ) ) + throw libcmis::Exception( string( "CreateFolder not allowed on folder " ) + getId(), "permissionDenied" ); + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + // Copy and remove the readonly properties before serializing + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, properties, stream, string( ) ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + libcmis::HttpResponsePtr response; + try + { + response = getSession( )->httpPostRequest( childrenLink->getHref( ), is, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + /* 409 here is more likely to be a constraint error */ + if ( e.getHttpStatus() == 409 ) { + throw libcmis::Exception( e.what(), "constraint" ); + } + throw e.getCmisException( ); + } + + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc, AtomPubSession::RESULT_FOLDER ); + xmlFreeDoc( doc ); + + libcmis::FolderPtr newFolder = boost::dynamic_pointer_cast< libcmis::Folder >( created ); + if ( !newFolder.get( ) ) + throw libcmis::Exception( string( "Created object is not a folder: " ) + created->getId( ), "constraint" ); + + return newFolder; +} + +libcmis::DocumentPtr AtomFolder::createDocument( const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType, string ) +{ + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateDocument ) && + getAllowableActions()->isDefined( libcmis::ObjectAction::CreateDocument ) ) ) + throw libcmis::Exception( string( "CreateDocument not allowed on folder " ) + getId() ); + + stringstream ss; + xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(libcmis::stringstream_write_callback, NULL, &ss, NULL); + xmlTextWriterPtr writer = xmlNewTextWriter(buf); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + AtomObject::writeAtomEntry( writer, properties, os, contentType ); + + xmlTextWriterEndDocument( writer ); + xmlFreeTextWriter( writer ); + + libcmis::HttpResponsePtr response; + try + { + response = getSession( )->httpPostRequest( childrenLink->getHref( ), ss, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string respBuf = response->getStream( )->str( ); + boost::shared_ptr< xmlDoc > doc( xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, XML_PARSE_NOERROR ), xmlFreeDoc ); + if ( !doc ) + { + // We may not have the created document entry in the response body: this is + // the behaviour of some servers, but the standard says we need to look for + // the Location header. + map< string, string >& headers = response->getHeaders( ); + map< string, string >::iterator it = headers.find( "Location" ); + + // Some servers like Lotus Live aren't sending Location header, but Content-Location + if ( it == headers.end( ) ) + it = headers.find( "Content-Location" ); + + if ( it != headers.end() ) + { + try + { + response = getSession( )->httpGetRequest( it->second ); + respBuf = response->getStream( )->str( ); + doc.reset( xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, XML_PARSE_NOERROR ), xmlFreeDoc ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + } + + // if doc is still NULL after that, then throw an exception + if ( !doc ) + throw libcmis::Exception( "Missing expected response from server" ); + } + + libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc.get(), AtomPubSession::RESULT_DOCUMENT ); + + libcmis::DocumentPtr newDocument = boost::dynamic_pointer_cast< libcmis::Document >( created ); + if ( !newDocument.get( ) ) + throw libcmis::Exception( string( "Created object is not a document: " ) + created->getId( ) ); + + return newDocument; +} + +vector< string > AtomFolder::removeTree( bool allVersions, libcmis::UnfileObjects::Type unfile, + bool continueOnError ) +{ + AtomLink* treeLink = getLink( "down", "application/cmistree+xml" ); + if ( NULL == treeLink ) + treeLink = getLink( "http://docs.oasis-open.org/ns/cmis/link/200908/foldertree", "application/cmistree+xml" ); + + if ( ( NULL == treeLink ) || ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::DeleteTree ) ) ) + throw libcmis::Exception( string( "DeleteTree not allowed on folder " ) + getId() ); + + try + { + string deleteUrl = treeLink->getHref( ); + if ( deleteUrl.find( '?' ) != string::npos ) + deleteUrl += "&"; + else + deleteUrl += "?"; + + // Add the all versions parameter + string allVersionsStr = "TRUE"; + if ( !allVersions ) + allVersionsStr = "FALSE"; + deleteUrl += "allVersions=" + allVersionsStr; + + // Add the unfileObjects parameter + string unfileStr; + switch ( unfile ) + { + case libcmis::UnfileObjects::Delete: + unfileStr = "delete"; + break; + case libcmis::UnfileObjects::DeleteSingleFiled: + unfileStr = "deletesinglefiled"; + break; + case libcmis::UnfileObjects::Unfile: + unfileStr = "unfile"; + break; + default: + break; + } + deleteUrl += "&unfileObjects=" + unfileStr; + + // Add the continueOnFailure parameter + string continueOnErrorStr = "TRUE"; + if ( !continueOnError ) + continueOnErrorStr = "FALSE"; + deleteUrl += "&continueOnFailure=" + continueOnErrorStr; + + getSession( )->httpDeleteRequest( deleteUrl ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // TODO Implement getting the failedIDs using a GET request on the same URL + return vector< string >( ); +} diff --git a/src/libcmis/atom-folder.hxx b/src/libcmis/atom-folder.hxx new file mode 100644 index 0000000..8137487 --- /dev/null +++ b/src/libcmis/atom-folder.hxx @@ -0,0 +1,56 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_FOLDER_HXX_ +#define _ATOM_FOLDER_HXX_ + +#include <string> + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "atom-object.hxx" + +class AtomFolder : public libcmis::Folder, public AtomObject +{ + public: + AtomFolder( AtomPubSession* session, xmlNodePtr entryNd ); + ~AtomFolder( ); + + // virtual pure methods from Folder + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( const std::map< std::string, libcmis::PropertyPtr >& properties ); + virtual libcmis::DocumentPtr createDocument( const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType, std::string fileName ); + + virtual std::vector< std::string > removeTree( bool allVersion = true, + libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/atom-object-type.cxx b/src/libcmis/atom-object-type.cxx new file mode 100644 index 0000000..8f50d04 --- /dev/null +++ b/src/libcmis/atom-object-type.cxx @@ -0,0 +1,167 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-object-type.hxx" + +#include <sstream> + +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace boost; + + +AtomObjectType::AtomObjectType( AtomPubSession* session, string id ) : + libcmis::ObjectType( ), + m_session( session ), + m_selfUrl( ), + m_childrenUrl( ) +{ + m_id = id; + refresh( ); +} + +AtomObjectType::AtomObjectType( AtomPubSession* session, xmlNodePtr entryNd ) : + libcmis::ObjectType( ), + m_session( session ), + m_selfUrl( ), + m_childrenUrl( ) +{ + xmlDocPtr doc = libcmis::wrapInDoc( entryNd ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + +AtomObjectType::AtomObjectType( const AtomObjectType& copy ) : + libcmis::ObjectType( copy ), + m_session( copy.m_session ), + m_selfUrl( copy.m_selfUrl ), + m_childrenUrl( copy.m_childrenUrl ) +{ +} + +AtomObjectType::~AtomObjectType( ) +{ +} + +AtomObjectType& AtomObjectType::operator=( const AtomObjectType& copy ) +{ + if ( this != © ) + { + libcmis::ObjectType::operator=( copy ); + m_session = copy.m_session; + m_selfUrl = copy.m_selfUrl; + m_childrenUrl = copy.m_childrenUrl; + } + + return *this; +} + +libcmis::ObjectTypePtr AtomObjectType::getParentType( ) +{ + return m_session->getType( m_parentTypeId ); +} + +libcmis::ObjectTypePtr AtomObjectType::getBaseType( ) +{ + return m_session->getType( m_baseTypeId ); +} + +vector< libcmis::ObjectTypePtr > AtomObjectType::getChildren( ) +{ + return m_session->getChildrenTypes( m_childrenUrl ); +} + +void AtomObjectType::refreshImpl( xmlDocPtr doc ) +{ + bool createdDoc = ( NULL == doc ); + if ( createdDoc ) + { + string pattern = m_session->getAtomRepository()->getUriTemplate( UriTemplate::TypeById ); + map< string, string > vars; + vars[URI_TEMPLATE_VAR_ID] = getId( ); + string url = m_session->createUrl( pattern, vars ); + + string buf; + try + { + buf = m_session->httpGetRequest( url )->getStream()->str(); + } + catch ( const CurlException& e ) + { + if ( ( e.getErrorCode( ) == CURLE_HTTP_RETURNED_ERROR ) && + ( e.getHttpStatus( ) == 404 ) ) + { + string msg = "No such type: "; + msg += getId( ); + throw libcmis::Exception( msg, "objectNotFound" ); + } + else + throw e.getCmisException( ); + } + + doc = xmlReadMemory( buf.c_str(), buf.size(), m_selfUrl.c_str(), NULL, 0 ); + + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + } + + extractInfos( doc ); + + if ( createdDoc ) + xmlFreeDoc( doc ); +} + +void AtomObjectType::extractInfos( xmlDocPtr doc ) +{ + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + + // Register the Service Document namespaces + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + // Get the self URL + string selfUrlReq( "//atom:link[@rel='self']/attribute::href" ); + m_selfUrl = libcmis::getXPathValue( xpathCtx, selfUrlReq ); + + // Get the children URL + string childrenUrlReq( "//atom:link[@rel='down' and @type='application/atom+xml;type=feed']/attribute::href" ); + m_childrenUrl = libcmis::getXPathValue( xpathCtx, childrenUrlReq ); + + // Get the cmisra:type node + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:type" ), xpathCtx ); + if ( NULL != xpathObj && NULL != xpathObj->nodesetval && xpathObj->nodesetval->nodeNr ) + { + xmlNodePtr typeNode = xpathObj->nodesetval->nodeTab[0]; + initializeFromNode( typeNode ); + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); +} diff --git a/src/libcmis/atom-object-type.hxx b/src/libcmis/atom-object-type.hxx new file mode 100644 index 0000000..c4c80dd --- /dev/null +++ b/src/libcmis/atom-object-type.hxx @@ -0,0 +1,63 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_OBJECT_TYPE_HXX_ +#define _ATOM_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "atom-session.hxx" + +class AtomObjectType : public libcmis::ObjectType +{ + private: + AtomPubSession* m_session; + + std::string m_selfUrl; + std::string m_childrenUrl; + + public: + AtomObjectType( AtomPubSession* session, std::string id ); + AtomObjectType( AtomPubSession* session, xmlNodePtr node ); + AtomObjectType( const AtomObjectType& copy ); + virtual ~AtomObjectType( ); + + AtomObjectType& operator=( const AtomObjectType& copy ); + + virtual void refresh( ) { refreshImpl( NULL ); } + + virtual libcmis::ObjectTypePtr getParentType( ); + virtual libcmis::ObjectTypePtr getBaseType( ); + virtual std::vector< libcmis::ObjectTypePtr > getChildren( ); + + private: + + void refreshImpl( xmlDocPtr doc ); + void extractInfos( xmlDocPtr doc ); +}; + +#endif diff --git a/src/libcmis/atom-object.cxx b/src/libcmis/atom-object.cxx new file mode 100644 index 0000000..65332f6 --- /dev/null +++ b/src/libcmis/atom-object.cxx @@ -0,0 +1,486 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-object.hxx" + +#include <algorithm> +#include <locale> +#include <sstream> + +#include <boost/algorithm/string.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <libcmis/xml-utils.hxx> + +#include "atom-document.hxx" +#include "atom-folder.hxx" +#include "atom-object-type.hxx" +#include "atom-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace +{ + class MatchLink + { + private: + string m_rel; + string m_type; + + public: + MatchLink( string rel, string type ) : m_rel( rel ), m_type( type ) { } + bool operator() ( const AtomLink &link ) + { + bool matchesRel = link.getRel( ) == m_rel; + + // Some implementations (xcmis) put extra spaces into the type attribute + // (e.g. "application/atom+xml; type=feed" instead of "application/atom+xml;type=feed") + string linkType = link.getType( ); + linkType.erase( remove_if( linkType.begin(), linkType.end(), boost::is_space() ), linkType.end() ); + + // Some implementation (SharePoint) are omitting the type attribute + bool matchesType = m_type.empty( ) || linkType.empty() || ( linkType == m_type ); + return matchesRel && matchesType; + } + }; +} + +AtomObject::AtomObject( AtomPubSession* session ) : + libcmis::Object( session ), + m_links( ) +{ +} + +AtomObject::AtomObject( const AtomObject& copy ) : + libcmis::Object( copy ), + m_links( copy.m_links ) +{ +} + +AtomObject& AtomObject::operator=( const AtomObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + m_links = copy.m_links; + } + + return *this; +} + +AtomObject::~AtomObject( ) +{ +} + +libcmis::ObjectPtr AtomObject::updateProperties( const PropertyPtrMap& properties ) +{ + if ( getAllowableActions().get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::UpdateProperties ) ) + throw libcmis::Exception( string( "UpdateProperties is not allowed on object " ) + getId() ); + + // No need to send HTTP request if there is nothing to update + if ( properties.empty( ) ) + { + libcmis::ObjectPtr object; + if ( getBaseType( ) == "cmis:document" ) + { + const AtomDocument& thisDoc = dynamic_cast< const AtomDocument& >( *this ); + object.reset( new AtomDocument( thisDoc ) ); + } + else if ( getBaseType( ) == "cmis:folder" ) + { + const AtomFolder& thisFolder = dynamic_cast< const AtomFolder& >( *this ); + object.reset( new AtomFolder( thisFolder ) ); + } + return object; + } + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + // Copy and remove the readonly properties before serializing + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, properties, stream, string( ) ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/atom+xml;type=entry" ); + response = getSession( )->httpPutRequest( getInfosUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + libcmis::ObjectPtr updated = getSession( )->createObjectFromEntryDoc( doc ); + if ( updated->getId( ) == getId( ) ) + refreshImpl( doc ); + xmlFreeDoc( doc ); + + return updated; +} + +libcmis::AllowableActionsPtr AtomObject::getAllowableActions( ) +{ + if ( !m_allowableActions ) + { + // For some reason we had no allowable actions before, get them now. + AtomLink* link = getLink( "http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions", "application/cmisallowableactions+xml" ); + if ( link ) + { + try + { + libcmis::HttpResponsePtr response = getSession()->httpGetRequest( link->getHref() ); + string buf = response->getStream()->str(); + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), link->getHref().c_str(), NULL, 0 ); + xmlNodePtr actionsNode = xmlDocGetRootElement( doc ); + if ( actionsNode ) + m_allowableActions.reset( new libcmis::AllowableActions( actionsNode ) ); + + xmlFreeDoc( doc ); + } + catch ( CurlException& ) + { + } + } + } + + return libcmis::Object::getAllowableActions(); +} + +void AtomObject::refreshImpl( xmlDocPtr doc ) +{ + bool createdDoc = ( NULL == doc ); + if ( createdDoc ) + { + string buf; + try + { + buf = getSession()->httpGetRequest( getInfosUrl() )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + doc = xmlReadMemory( buf.c_str(), buf.size(), getInfosUrl().c_str(), NULL, 0 ); + + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + } + + // Cleanup the structures before setting them again + m_typeDescription.reset( ); + m_properties.clear( ); + m_allowableActions.reset( ); + m_links.clear( ); + m_renditions.clear( ); + + extractInfos( doc ); + + if ( createdDoc ) + xmlFreeDoc( doc ); +} + +void AtomObject::remove( bool allVersions ) +{ + if ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::DeleteObject ) ) + throw libcmis::Exception( string( "DeleteObject not allowed on object " ) + getId() ); + + try + { + string deleteUrl = getInfosUrl( ); + if ( deleteUrl.find( '?' ) != string::npos ) + deleteUrl += "&"; + else + deleteUrl += "?"; + + string allVersionsStr = "TRUE"; + if ( !allVersions ) + allVersionsStr = "FALSE"; + deleteUrl += "allVersions=" + allVersionsStr; + + getSession( )->httpDeleteRequest( deleteUrl ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +void AtomObject::move( boost::shared_ptr< libcmis::Folder > source, boost::shared_ptr< libcmis::Folder > destination ) +{ + AtomFolder* atomDestination = dynamic_cast< AtomFolder* > ( destination.get() ); + + if ( NULL == atomDestination ) + throw libcmis::Exception( string( "Destination is not an AtomFolder" ) ); + + AtomLink* destChildrenLink = atomDestination->getLink( "down", "application/atom+xml;type=feed" ); + + if ( ( NULL == destChildrenLink ) || ( getAllowableActions().get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::MoveObject ) ) ) + throw libcmis::Exception( string( "MoveObject not allowed on object " ) + getId() ); + + // create object xml + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, getProperties( ), stream, string( ) ); + xmlTextWriterEndDocument( writer ); + + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + // create post url + string postUrl = destChildrenLink->getHref(); + if ( postUrl.find( '?' ) != string::npos ) + postUrl += "&"; + else + postUrl += "?"; + postUrl += "sourceFolderId={sourceFolderId}"; + // Session::CreateUrl is used to properly escape the id + map< string, string > params; + params[ "sourceFolderId" ] = source->getId(); + postUrl = getSession( )->createUrl( postUrl, params ); + + // post it + libcmis::HttpResponsePtr response; + try + { + response = getSession( )->httpPostRequest( postUrl, is, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // refresh self from response + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + +string AtomObject::getInfosUrl( ) +{ + AtomLink* selfLink = getLink( "self", "application/atom+xml;type=entry" ); + if ( NULL != selfLink ) + return selfLink->getHref( ); + return string( ); +} + +void AtomObject::extractInfos( xmlDocPtr doc ) +{ + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + m_links.clear( ); + m_renditions.clear( ); + + // Get all the atom links + string linksReq( "//atom:link" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( linksReq.c_str() ), xpathCtx ); + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + try + { + AtomLink link( node ); + // Add to renditions if alternate link + if ( link.getRel( ) == "alternate" ) + { + string kind; + map< string, string >::iterator it = link.getOthers().find( "renditionKind" ); + if ( it != link.getOthers( ).end() ) + kind = it->second; + + string title; + it = link.getOthers().find( "title" ); + if ( it != link.getOthers( ).end( ) ) + title = it->second; + + long length = -1; + it = link.getOthers( ).find( "length" ); + if ( it != link.getOthers( ).end( ) ) + length = libcmis::parseInteger( it->second ); + + libcmis::RenditionPtr rendition( new libcmis::Rendition( + string(), link.getType(), kind, + link.getHref( ), title, length ) ); + + m_renditions.push_back( rendition ); + } + else + m_links.push_back( node ); + } + catch ( const libcmis::Exception& ) + { + // Broken or incomplete link... don't add it + } + } + } + xmlXPathFreeObject( xpathObj ); + + + xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:object" ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[0]; + initializeFromNode( node ); + } + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); +} + +AtomPubSession* AtomObject::getSession( ) +{ + return dynamic_cast< AtomPubSession* >( m_session ); +} + +void AtomObject::writeAtomEntry( xmlTextWriterPtr writer, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType ) +{ + AtomObject tmp( NULL ); + PropertyPtrMap propertiesCopy( properties ); + tmp.m_properties.swap( propertiesCopy ); + + xmlTextWriterStartElement( writer, BAD_CAST( "atom:entry" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:atom" ), BAD_CAST( NS_ATOM_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmisra" ), BAD_CAST( NS_CMISRA_URL ) ); + + if ( !tmp.getCreatedBy( ).empty( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "atom:author" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "atom:name" ), BAD_CAST( tmp.getCreatedBy( ).c_str( ) ) ); + xmlTextWriterEndElement( writer ); + } + + xmlTextWriterWriteElement( writer, BAD_CAST( "atom:title" ), BAD_CAST( tmp.getName( ).c_str( ) ) ); + + boost::posix_time::ptime now( boost::posix_time::second_clock::universal_time( ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "atom:updated" ), BAD_CAST( libcmis::writeDateTime( now ).c_str( ) ) ); + + if ( os.get( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "cmisra:content" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmisra:mediatype" ), BAD_CAST( contentType.c_str() ) ); + xmlTextWriterStartElement(writer, BAD_CAST( "cmisra:base64" ) ); + + libcmis::EncodedData encoder( writer ); + encoder.setEncoding( "base64" ); + istream is( os->rdbuf( ) ); + int bufLength = 1000; + char* buf = new char[ bufLength ]; + do + { + is.read( buf, bufLength ); + int size = is.gcount( ); + encoder.encode( buf, 1, size ); + } while ( !is.eof( ) && !is.fail( ) ); + delete[] buf; + encoder.finish( ); + xmlTextWriterEndElement( writer ); // "cmisra:base64" + + xmlTextWriterEndElement( writer ); + } + + xmlTextWriterStartElement( writer, BAD_CAST( "cmisra:object" ) ); + + tmp.toXml( writer ); + + xmlTextWriterEndElement( writer ); // cmisra:object + + xmlTextWriterEndElement( writer ); // atom:entry +} + +AtomLink* AtomObject::getLink( std::string rel, std::string type ) +{ + AtomLink* link = NULL; + vector< AtomLink >::iterator it = find_if( m_links.begin(), m_links.end(), MatchLink( rel, type ) ); + if ( it != m_links.end() ) + link = &( *it ); + return link; +} + +AtomLink::AtomLink( xmlNodePtr node ): + m_rel( ), m_type( ), m_id( ), m_href( ), m_others( ) +{ + xmlAttrPtr prop = node->properties; + while ( prop != NULL ) + { + xmlChar* xmlStr = xmlGetProp( node, prop->name ); + string value( ( char * ) xmlStr ); + + if ( xmlStrEqual( prop->name, BAD_CAST( "id" ) ) ) + m_id = value; + else if ( xmlStrEqual( prop->name, BAD_CAST( "type" ) ) ) + m_type = value; + else if ( xmlStrEqual( prop->name, BAD_CAST( "rel" ) ) ) + m_rel = value; + else if ( xmlStrEqual( prop->name, BAD_CAST( "href" ) ) ) + m_href = value; + else + m_others[ string( ( char * ) prop->name ) ] = value; + + free( xmlStr ); + prop = prop->next; + } +} diff --git a/src/libcmis/atom-object.hxx b/src/libcmis/atom-object.hxx new file mode 100644 index 0000000..5d54dab --- /dev/null +++ b/src/libcmis/atom-object.hxx @@ -0,0 +1,106 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_OBJECT_HXX_ +#define _ATOM_OBJECT_HXX_ + +#include <map> +#include <ostream> + +#include <libcmis/object.hxx> + +class AtomPubSession; + +class AtomLink +{ + private: + std::string m_rel; + std::string m_type; + std::string m_id; + std::string m_href; + std::map< std::string, std::string > m_others; + + public: + AtomLink( xmlNodePtr node ); + + std::string getRel( ) const { return m_rel; } + std::string getType( ) const { return m_type; } + std::string getId( ) const { return m_id; } + bool hasId( ) const { return !m_id.empty( ); } + std::string getHref( ) const { return m_href; } + std::map< std::string, std::string >& getOthers( ) { return m_others; } +}; + +class AtomObject : public virtual libcmis::Object +{ + private: + + std::vector< AtomLink > m_links; + + public: + AtomObject( AtomPubSession* session ); + AtomObject( const AtomObject& copy ); + ~AtomObject( ); + + AtomObject& operator=( const AtomObject& copy ); + + // Overridden methods from libcmis::Object + virtual libcmis::ObjectPtr updateProperties( + const std::map< std::string, libcmis::PropertyPtr >& properties ); + + virtual libcmis::AllowableActionsPtr getAllowableActions( ); + + /** Reload the data from the server. + */ + virtual void refresh( ) { refreshImpl( NULL ); } + + virtual void remove( bool allVersion = true ); + + virtual void move( boost::shared_ptr< libcmis::Folder > source, boost::shared_ptr< libcmis::Folder > destination ); + + static void writeAtomEntry( xmlTextWriterPtr writer, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType ); + + protected: + + std::string getInfosUrl( ); + virtual void refreshImpl( xmlDocPtr doc ); + virtual void extractInfos( xmlDocPtr doc ); + + AtomPubSession* getSession( ); + + /** Get the atom link corresponding to the given relation and type or NULL + if no link matched those criteria. + + \param rel the relation to match + \param type the type to match or the empty string to match all types. + */ + AtomLink* getLink( std::string rel, std::string type ); +}; + +#endif diff --git a/src/libcmis/atom-session.cxx b/src/libcmis/atom-session.cxx new file mode 100644 index 0000000..7478c5f --- /dev/null +++ b/src/libcmis/atom-session.cxx @@ -0,0 +1,349 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-session.hxx" + +#include <string> + +#include <boost/algorithm/string.hpp> +#include <boost/shared_ptr.hpp> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/xml-utils.hxx> + +#include "atom-document.hxx" +#include "atom-folder.hxx" +#include "atom-object-type.hxx" + +using namespace std; + +AtomPubSession::AtomPubSession( string atomPubUrl, string repositoryId, + string username, string password, bool noSslCheck, + libcmis::OAuth2DataPtr oauth2, bool verbose ) : + BaseSession( atomPubUrl, repositoryId, username, password, noSslCheck, oauth2, verbose ), + m_repository( ) +{ + libcmis::HttpResponsePtr response; + initialize( response ); +} + +AtomPubSession::AtomPubSession( string atomPubUrl, string repositoryId, + const HttpSession& httpSession, libcmis::HttpResponsePtr response ) : + BaseSession( atomPubUrl, repositoryId, httpSession ), + m_repository( ) +{ + initialize( response ); +} + +AtomPubSession::AtomPubSession( ) : + BaseSession( ), + m_repository( ) +{ +} + +AtomPubSession::~AtomPubSession( ) +{ +} + +void AtomPubSession::parseServiceDocument( const string& buf ) +{ + // parse the content + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( buf.c_str(), buf.size(), m_bindingUrl.c_str(), NULL, 0 ), xmlFreeDoc ); + + if ( bool( doc ) ) + { + // Check that we have an AtomPub service document + xmlNodePtr root = xmlDocGetRootElement( doc.get() ); + if ( !xmlStrEqual( root->name, BAD_CAST( "service" ) ) ) + throw libcmis::Exception( "Not an atompub service document" ); + + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc.get() ); + + // Register the Service Document namespaces + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + string workspacesXPath( "//app:workspace" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( workspacesXPath.c_str() ), xpathCtx ); + + if ( xpathObj != NULL ) + { + int nbWorkspaces = 0; + if ( xpathObj->nodesetval ) + nbWorkspaces = xpathObj->nodesetval->nodeNr; + + for ( int i = 0; i < nbWorkspaces; i++ ) + { + try + { + AtomRepositoryPtr ws( new AtomRepository( xpathObj->nodesetval->nodeTab[i] ) ); + + // Check if we have a repository set + if ( m_repositoryId.empty( ) && i == 0 ) + m_repositoryId = ws->getId( ); + + // SharePoint is case insensitive for the id... + if ( boost::to_lower_copy( ws->getId( ) ) == boost::to_lower_copy( m_repositoryId ) ) + m_repository = ws; + + m_repositories.push_back( ws ); + } + catch ( const libcmis::Exception& ) + { + // Invalid repository, don't take care of this + } + } + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + else + throw libcmis::Exception( "Failed to parse service document" ); +} + +void AtomPubSession::initialize( libcmis::HttpResponsePtr response ) +{ + if ( m_repositories.empty() ) + { + // Pull the content from sAtomPubUrl + string buf; + if ( response ) + { + buf = response->getStream( )->str( ); + } + else + { + try + { + buf = httpGetRequest( m_bindingUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + } + + parseServiceDocument( buf ); + } +} + +AtomRepositoryPtr AtomPubSession::getAtomRepository( ) +{ + return m_repository; +} + +libcmis::RepositoryPtr AtomPubSession::getRepository( ) +{ + return getAtomRepository( ); +} + +bool AtomPubSession::setRepository( string repositoryId ) +{ + vector< libcmis::RepositoryPtr > repos = getRepositories( ); + bool found = false; + for ( vector< libcmis::RepositoryPtr >::iterator it = repos.begin(); + it != repos.end() && !found; ++it ) + { + libcmis::RepositoryPtr repo = *it; + if ( repo->getId() == repositoryId ) + { + AtomRepositoryPtr atomRepo = boost::dynamic_pointer_cast< AtomRepository >( repo ); + m_repository = atomRepo; + m_repositoryId = repositoryId; + found = true; + } + } + return found; +} + +libcmis::ObjectPtr AtomPubSession::createObjectFromEntryDoc( xmlDocPtr doc, ResultObjectType res ) +{ + libcmis::ObjectPtr cmisObject; + + if ( NULL != doc ) + { + // Get the atom:entry node + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval && ( 0 < xpathObj->nodesetval->nodeNr ) ) + { + // Get the entry's base type + string baseTypeReq = "//atom:entry[1]//cmis:propertyId[@propertyDefinitionId='cmis:baseTypeId']/cmis:value/text()"; + string baseType = libcmis::getXPathValue( xpathCtx, baseTypeReq ); + + xmlNodePtr node = xpathObj->nodesetval->nodeTab[0]; + if ( res == RESULT_FOLDER || baseType == "cmis:folder" ) + { + cmisObject.reset( new AtomFolder( this, node ) ); + } + else if ( res == RESULT_DOCUMENT || baseType == "cmis:document" ) + { + cmisObject.reset( new AtomDocument( this, node ) ); + } + else + { + // Not a valid CMIS atom entry... weird + } + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + + return cmisObject; +} + +libcmis::ObjectPtr AtomPubSession::getObject( string id ) +{ + string pattern = getAtomRepository()->getUriTemplate( UriTemplate::ObjectById ); + map< string, string > vars; + vars[URI_TEMPLATE_VAR_ID] = id; + vars[string( "includeAllowableActions" )] = string( "true" ); + string url = createUrl( pattern, vars ); + + try + { + string buf = httpGetRequest( url )->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), url.c_str(), NULL, 0 ); + libcmis::ObjectPtr cmisObject = createObjectFromEntryDoc( doc ); + xmlFreeDoc( doc ); + return cmisObject; + } + catch ( const CurlException& e ) + { + if ( ( e.getErrorCode( ) == CURLE_HTTP_RETURNED_ERROR ) && + ( e.getHttpStatus( ) == 404 ) ) + { + string msg = "No such node: "; + msg += id; + throw libcmis::Exception( msg, "objectNotFound" ); + } + else + throw e.getCmisException(); + } +} + +libcmis::ObjectPtr AtomPubSession::getObjectByPath( string path ) +{ + string pattern = getAtomRepository()->getUriTemplate( UriTemplate::ObjectByPath ); + map< string, string > vars; + vars[URI_TEMPLATE_VAR_PATH] = path; + vars[string( "includeAllowableActions" )] = string( "true" ); + string url = createUrl( pattern, vars ); + + try + { + string buf = httpGetRequest( url )->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), url.c_str(), NULL, 0 ); + libcmis::ObjectPtr cmisObject = createObjectFromEntryDoc( doc ); + xmlFreeDoc( doc ); + return cmisObject; + } + catch ( const CurlException& e ) + { + if ( ( e.getErrorCode( ) == CURLE_HTTP_RETURNED_ERROR ) && + ( e.getHttpStatus( ) == 404 ) ) + { + string msg = "No node corresponding to path: "; + msg += path; + throw libcmis::Exception( msg, "objectNotFound" ); + } + else + throw e.getCmisException(); + } +} + +libcmis::ObjectTypePtr AtomPubSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new AtomObjectType( this, id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > AtomPubSession::getBaseTypes( ) +{ + string url = getAtomRepository( )->getCollectionUrl( Collection::Types ); + return getChildrenTypes( url ); +} + +vector< libcmis::ObjectTypePtr > AtomPubSession::getChildrenTypes( string url ) +{ + vector< libcmis::ObjectTypePtr > children; + string buf; + try + { + buf = httpGetRequest( url )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), url.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + libcmis::ObjectTypePtr type( new AtomObjectType( this, node ) ); + children.push_back( type ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse type children infos" ); + } + xmlFreeDoc( doc ); + + return children; +} diff --git a/src/libcmis/atom-session.hxx b/src/libcmis/atom-session.hxx new file mode 100644 index 0000000..6220591 --- /dev/null +++ b/src/libcmis/atom-session.hxx @@ -0,0 +1,90 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_SESSION_HXX_ +#define _ATOM_SESSION_HXX_ + +#include "base-session.hxx" +#include "atom-workspace.hxx" + +class AtomPubSession : public BaseSession +{ + private: + AtomRepositoryPtr m_repository; + + public: + enum ResultObjectType { RESULT_DYNAMIC, RESULT_FOLDER, RESULT_DOCUMENT }; + AtomPubSession( std::string sAtomPubUrl, std::string repositoryId, + std::string username, std::string password, bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), + bool verbose =false ); + + /** This constructor uses the response of an HTTP request made + before to spare some HTTP request. This constructor has mostly + been designed for the SessionFactory use. + */ + AtomPubSession( std::string sAtomPubUrl, std::string repositoryId, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ); + ~AtomPubSession( ); + + AtomRepositoryPtr getAtomRepository( ); + + // Utility methods + + libcmis::ObjectPtr createObjectFromEntryDoc( xmlDocPtr doc, ResultObjectType res=RESULT_DYNAMIC ); + + std::vector< libcmis::ObjectTypePtr > getChildrenTypes( std::string url ); + + // Override session methods + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string repositoryId ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + protected: + + /** Defaults constructor shouldn't be used + */ + AtomPubSession( ); + AtomPubSession( const AtomPubSession& copy ) = delete; + AtomPubSession& operator=( const AtomPubSession& copy ) = delete; + + void parseServiceDocument( const std::string& buf ); + + void initialize( libcmis::HttpResponsePtr response ); +}; + +#endif diff --git a/src/libcmis/atom-workspace.cxx b/src/libcmis/atom-workspace.cxx new file mode 100644 index 0000000..fb9af8d --- /dev/null +++ b/src/libcmis/atom-workspace.cxx @@ -0,0 +1,228 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Cédric Bosdonnat <cbosdo@users.sourceforge.net> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-workspace.hxx" + +#include <libcmis/xml-utils.hxx> + +using namespace std; + +AtomRepository::AtomRepository( xmlNodePtr wsNode ): + Repository( ), + m_collections( ), + m_uriTemplates( ) +{ + if ( wsNode != NULL ) + { + xmlDocPtr doc = libcmis::wrapInDoc( wsNode ); + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + // Get the collections + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//app:collection" ), xpathCtx ); + if ( NULL != xpathObj ) + readCollections( xpathObj->nodesetval ); + xmlXPathFreeObject( xpathObj ); + + // Get the URI templates + xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:uritemplate" ), xpathCtx ); + if ( NULL != xpathObj ) + readUriTemplates( xpathObj->nodesetval ); + xmlXPathFreeObject( xpathObj ); + + // Get the repository infos + xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:repositoryInfo" ), xpathCtx ); + if ( NULL != xpathObj ) + initializeFromNode( xpathObj->nodesetval->nodeTab[0] ); + xmlXPathFreeObject( xpathObj ); + + } + xmlXPathFreeContext( xpathCtx ); + xmlFreeDoc( doc ); + } +} + +AtomRepository::AtomRepository( const AtomRepository& rCopy ) : + Repository( rCopy ), + m_collections( rCopy.m_collections ), + m_uriTemplates( rCopy.m_uriTemplates ) +{ +} + +AtomRepository::~AtomRepository( ) +{ + m_collections.clear( ); + m_uriTemplates.clear( ); +} + +AtomRepository& AtomRepository::operator= ( const AtomRepository& rCopy ) +{ + if ( this != &rCopy ) + { + m_collections = rCopy.m_collections; + m_uriTemplates = rCopy.m_uriTemplates; + } + + return *this; +} + +string AtomRepository::getCollectionUrl( Collection::Type type ) +{ + return m_collections[ type ]; +} + +string AtomRepository::getUriTemplate( UriTemplate::Type type ) +{ + return m_uriTemplates[ type ]; +} + +void AtomRepository::readCollections( xmlNodeSetPtr nodeSet ) +{ + int size = 0; + if ( nodeSet ) + size = nodeSet->nodeNr; + + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = nodeSet->nodeTab[i]; + + // Look for the href property + xmlChar* href = xmlGetProp( node, BAD_CAST( "href" ) ); + if ( href ) + { + string collectionRef( ( char* )href ); + xmlFree( href ); + + // Look for the cmisra:collectionType child + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + // SharePoint CMIS implementation doesn't follow the spec: + // the cmisra namespace is omitted + bool isCollectionType = xmlStrEqual( child->name, BAD_CAST( "collectionType" ) ); + if ( isCollectionType ) + { + xmlChar* content = xmlNodeGetContent( child ); + Collection::Type type = Collection::Root; + bool typeDefined = false; + + if ( xmlStrEqual( content, BAD_CAST( "root" ) ) ) + { + type = Collection::Root; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "types" ) ) ) + { + type = Collection::Types; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "query" ) ) ) + { + type = Collection::Query; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "checkedout" ) ) ) + { + type = Collection::CheckedOut; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "unfiled" ) ) ) + { + type = Collection::Unfiled; + typeDefined = true; + } + + if ( typeDefined ) + m_collections[ type ] = collectionRef; + + xmlFree( content ); + } + } + } + } +} + +void AtomRepository::readUriTemplates( xmlNodeSetPtr nodeSet ) +{ + int size = 0; + if ( nodeSet ) + size = nodeSet->nodeNr; + + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = nodeSet->nodeTab[i]; + + string templateUri; + UriTemplate::Type type = UriTemplate::ObjectById; + bool typeDefined = false; + + // Look for the cmisra:template and cmisra:type children + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + bool isTemplate = xmlStrEqual( child->name, BAD_CAST( "template" ) ); + bool isType = xmlStrEqual( child->name, BAD_CAST( "type" ) ); + + if ( isTemplate ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + templateUri = string( ( char * )content ); + xmlFree( content ); + } + else if ( isType ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( xmlStrEqual( content, BAD_CAST( "objectbyid" ) ) ) + { + type = UriTemplate::ObjectById; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "objectbypath" ) ) ) + { + type = UriTemplate::ObjectByPath; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "query" ) ) ) + { + type = UriTemplate::Query; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "typebyid" ) ) ) + { + type = UriTemplate::TypeById; + typeDefined = true; + } + xmlFree( content ); + } + } + + if ( !templateUri.empty() && typeDefined ) + m_uriTemplates[ type ] = templateUri; + } +} diff --git a/src/libcmis/atom-workspace.hxx b/src/libcmis/atom-workspace.hxx new file mode 100644 index 0000000..fa4582c --- /dev/null +++ b/src/libcmis/atom-workspace.hxx @@ -0,0 +1,91 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Cédric Bosdonnat <cbosdo@users.sourceforge.net> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_WORKSPACE_HXX_ +#define _ATOM_WORKSPACE_HXX_ + +#include <map> +#include <string> + +#include <boost/shared_ptr.hpp> +#include <curl/curl.h> +#include <libxml/xpath.h> + +#include <libcmis/exception.hxx> +#include <libcmis/repository.hxx> + +#define URI_TEMPLATE_VAR_ID std::string( "id" ) +#define URI_TEMPLATE_VAR_PATH std::string( "path" ) + +struct Collection { + enum Type + { + Root, + Types, + Query, + CheckedOut, + Unfiled + }; +}; + +struct UriTemplate { + enum Type + { + ObjectById, + ObjectByPath, + TypeById, + Query + }; +}; + +class AtomRepository : public libcmis::Repository +{ + private: + /// Collections URLs + std::map< Collection::Type, std::string > m_collections; + + /// URI templates + std::map< UriTemplate::Type, std::string > m_uriTemplates; + + public: + AtomRepository( xmlNodePtr wsNode = NULL ); + AtomRepository( const AtomRepository& rCopy ); + ~AtomRepository( ); + + AtomRepository& operator= ( const AtomRepository& rCopy ); + + std::string getCollectionUrl( Collection::Type ); + std::string getUriTemplate( UriTemplate::Type ); + + private: + void readCollections( xmlNodeSetPtr pNodeSet ); + void readUriTemplates( xmlNodeSetPtr pNodeSet ); +}; + +typedef boost::shared_ptr< AtomRepository > AtomRepositoryPtr; + +#endif diff --git a/src/libcmis/base-session.cxx b/src/libcmis/base-session.cxx new file mode 100644 index 0000000..a4311ca --- /dev/null +++ b/src/libcmis/base-session.cxx @@ -0,0 +1,146 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "base-session.hxx" + +#include <cctype> +#include <string> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/session-factory.hxx> +#include <libcmis/xml-utils.hxx> + +#include "oauth2-handler.hxx" + +using namespace std; + +BaseSession::BaseSession( string bindingUrl, string repositoryId, string username, + string password, bool noSslCheck, libcmis::OAuth2DataPtr oauth2, bool verbose ) : + Session( ), + HttpSession( username, password, noSslCheck, oauth2, verbose ), + m_bindingUrl( bindingUrl ), + m_repositoryId( repositoryId ), + m_repositories( ) +{ +} + +BaseSession::BaseSession( string sBindingUrl, string repository, + const HttpSession& httpSession ) : + Session( ), + HttpSession( httpSession ), + m_bindingUrl( sBindingUrl ), + m_repositoryId( repository ), + m_repositories( ) +{ +} + +BaseSession::BaseSession( ) : + Session( ), + HttpSession( ), + m_bindingUrl( ), + m_repositoryId( ), + m_repositories( ) +{ +} + +BaseSession::~BaseSession( ) +{ +} + +string BaseSession::createUrl( const string& pattern, map< string, string > variables ) +{ + string url( pattern ); + + // Decompose the pattern and replace the variables by their values + map< string, string >::iterator it = variables.begin( ); + while ( it != variables.end( ) ) + { + string name = "{"; + name += it->first; + name += "}"; + string value = it->second; + + // Search and replace the variable + size_t pos = url.find( name ); + if ( pos != string::npos ) + { + // Escape the URL by chunks + url = url.replace( pos, name.size(), libcmis::escape( value ) ); + } + + ++it; + } + + // Cleanup the remaining unset variables + size_t pos1 = url.find( '{' ); + while ( pos1 != string::npos ) + { + // look for the closing bracket + size_t pos2 = url.find( '}', pos1 ); + if ( pos2 != string::npos ) + url.erase( pos1, pos2 - pos1 + 1 ); + + pos1 = url.find( '{', pos1 - 1 ); + } + + return url; +} + + +void BaseSession::setNoSSLCertificateCheck( bool noCheck ) +{ + HttpSession::setNoSSLCertificateCheck( noCheck ); +} + +void BaseSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); + + oauth2Authenticate( ); +} + +vector< libcmis::RepositoryPtr > BaseSession::getRepositories( ) +{ + return m_repositories; +} + +libcmis::FolderPtr BaseSession::getRootFolder() +{ + return getFolder( getRootId() ); +} + +libcmis::FolderPtr BaseSession::getFolder( string id ) +{ + libcmis::ObjectPtr object = getObject( id ); + libcmis::FolderPtr folder = boost::dynamic_pointer_cast< libcmis::Folder >( object ); + return folder; +} diff --git a/src/libcmis/base-session.hxx b/src/libcmis/base-session.hxx new file mode 100644 index 0000000..60976d4 --- /dev/null +++ b/src/libcmis/base-session.hxx @@ -0,0 +1,104 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _BASE_SESSION_HXX_ +#define _BASE_SESSION_HXX_ + +#include <istream> +#include <sstream> +#include <vector> +#include <map> +#include <string> + +#include <curl/curl.h> +#include <libxml/xmlstring.h> +#include <libxml/xpath.h> + +#include <libcmis/exception.hxx> +#include <libcmis/oauth2-data.hxx> +#include <libcmis/session.hxx> +#include <libcmis/xml-utils.hxx> + +#include "http-session.hxx" + +class OAuth2Handler; + +class BaseSession : public libcmis::Session, + public HttpSession +{ + protected: + std::string m_bindingUrl; + std::string m_repositoryId; + + std::vector< libcmis::RepositoryPtr > m_repositories; + public: + BaseSession( std::string sBindingUrl, std::string repository, + std::string username, std::string password, + bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), bool verbose = false ); + + /** This constructor copies an existing http session. + This has been mostly designed for SessionFactory to save + a few HTTP requests when guessing the binding to use. + */ + BaseSession( std::string sBindingUrl, std::string repository, + const HttpSession& httpSession ); + + ~BaseSession( ); + + std::string& getRepositoryId( ) { return m_repositoryId; } + + // Utility methods + + std::string getRootId( ) { return getRepository()->getRootId( ); } + + std::string createUrl( const std::string& pattern, std::map< std::string, std::string > variables ); + + std::string getBindingUrl( ) { return m_bindingUrl; } + + // HttpSession overridden methods + + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + // Session methods + + virtual void setNoSSLCertificateCheck( bool noCheck ); + + virtual std::vector< libcmis::RepositoryPtr > getRepositories( ); + + virtual libcmis::FolderPtr getRootFolder(); + + virtual libcmis::FolderPtr getFolder( std::string id ); + + protected: + BaseSession( ); + + BaseSession( const BaseSession& copy ) = delete; + BaseSession& operator=( const BaseSession& copy ) = delete; +}; + +#endif diff --git a/src/libcmis/document.cxx b/src/libcmis/document.cxx new file mode 100644 index 0000000..b8a0d0d --- /dev/null +++ b/src/libcmis/document.cxx @@ -0,0 +1,107 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/document.hxx> + +#include <libcmis/folder.hxx> + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace libcmis +{ + vector< string > Document::getPaths( ) + { + vector< string > paths; + try + { + vector< libcmis::FolderPtr > parents = getParents( ); + for ( vector< libcmis::FolderPtr >::iterator it = parents.begin( ); + it != parents.end(); ++it ) + { + string path = ( *it )->getPath( ); + if ( path.empty() ) + continue; + if ( path[path.size() - 1] != '/' ) + path += "/"; + path += getName( ); + paths.push_back( path ); + } + } + catch ( const libcmis::Exception& ) + { + // We may not have the permission to get the parents + } + return paths; + } + + string Document::getContentType( ) + { + return getStringProperty( "cmis:contentStreamMimeType" ); + } + + string Document::getContentFilename( ) + { + return getStringProperty( "cmis:contentStreamFileName" ); + } + + long Document::getContentLength( ) + { + long contentLength = 0; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:contentStreamLength" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getLongs( ).empty( ) ) + contentLength = it->second->getLongs( ).front( ); + return contentLength; + } + + // LCOV_EXCL_START + string Document::toString( ) + { + stringstream buf; + + buf << "Document Object:" << endl << endl; + buf << Object::toString(); + try + { + vector< libcmis::FolderPtr > parents = getParents( ); + buf << "Parents ids: "; + for ( vector< libcmis::FolderPtr >::iterator it = parents.begin(); it != parents.end(); ++it ) + buf << "'" << ( *it )->getId( ) << "' "; + buf << endl; + } + catch ( const libcmis::Exception& ) + { + } + buf << "Content Type: " << getContentType( ) << endl; + buf << "Content Length: " << getContentLength( ) << endl; + buf << "Content Filename: " << getContentFilename( ) << endl; + + return buf.str(); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/dummy.cxx b/src/libcmis/dummy.cxx new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/libcmis/dummy.cxx diff --git a/src/libcmis/folder.cxx b/src/libcmis/folder.cxx new file mode 100644 index 0000000..d4a5acd --- /dev/null +++ b/src/libcmis/folder.cxx @@ -0,0 +1,92 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/folder.hxx> + +#include <libcmis/session.hxx> + +using namespace std; + +namespace libcmis +{ + vector< string > Folder::getPaths( ) + { + vector< string > paths; + paths.push_back( getPath( ) ); + return paths; + } + + libcmis::FolderPtr Folder::getFolderParent( ) + { + if ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetFolderParent ) ) + throw libcmis::Exception( string( "GetFolderParent not allowed on node " ) + getId() ); + + if ( m_session == NULL ) + throw libcmis::Exception( string( "Session not defined on the object... weird!" ) ); + + return m_session->getFolder( getParentId( ) ); + } + + string Folder::getParentId( ) + { + return getStringProperty( "cmis:parentId" ); + } + + string Folder::getPath( ) + { + return getStringProperty( "cmis:path" ); + } + + bool Folder::isRootFolder( ) + { + return getParentId( ).empty( ); + } + + // LCOV_EXCL_START + string Folder::toString( ) + { + stringstream buf; + + buf << "Folder Object:" << endl << endl; + buf << Object::toString(); + buf << "Path: " << getPath() << endl; + buf << "Folder Parent Id: " << getParentId( ) << endl; + buf << "Children [Name (Id)]:" << endl; + + vector< libcmis::ObjectPtr > children = getChildren( ); + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end(); ++it ) + { + libcmis::ObjectPtr child = *it; + buf << " " << child->getName() << " (" << child->getId() << ")" << endl; + } + + return buf.str(); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/gdrive-allowable-actions.hxx b/src/libcmis/gdrive-allowable-actions.hxx new file mode 100644 index 0000000..1a22d5d --- /dev/null +++ b/src/libcmis/gdrive-allowable-actions.hxx @@ -0,0 +1,102 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_ALLOWABLE_ACTIONS_HXX_ +#define _GDRIVE_ALLOWABLE_ACTIONS_HXX_ + +#include <libcmis/allowable-actions.hxx> + +class GdriveAllowableActions: public libcmis::AllowableActions +{ + public: + GdriveAllowableActions( bool isFolder ) : AllowableActions( ) + { + m_states.clear( ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::UpdateProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectRelationships, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectParents, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::MoveObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateRelationship, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyPolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAppliedPolicies, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemovePolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetACL, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyACL, true ) ); + + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderTree, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderParent, isFolder) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetDescendants, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CancelCheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckIn, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::SetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAllVersions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::AddObjectToFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemoveObjectFromFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetRenditions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetChildren, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateDocument, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateFolder, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteTree, isFolder ) ); + } +}; + +#endif diff --git a/src/libcmis/gdrive-document.cxx b/src/libcmis/gdrive-document.cxx new file mode 100644 index 0000000..ecb13d6 --- /dev/null +++ b/src/libcmis/gdrive-document.cxx @@ -0,0 +1,250 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-document.hxx" + +#include <libcmis/rendition.hxx> + +#include "gdrive-folder.hxx" +#include "gdrive-session.hxx" +#include "json-utils.hxx" +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveDocument::GDriveDocument( GDriveSession* session ) : + libcmis::Object( session), + libcmis::Document( session ), + GDriveObject( session ), + m_isGoogleDoc( false ) +{ +} + +GDriveDocument::GDriveDocument( GDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session), + libcmis::Document( session ), + GDriveObject( session, json, id, name ), + m_isGoogleDoc( false ) +{ + m_isGoogleDoc = getContentType( ).find( "google" ) != string::npos; + getRenditions( ); +} + +GDriveDocument::~GDriveDocument( ) +{ +} + +string GDriveDocument::getDownloadUrl( string streamId ) +{ + string streamUrl; + vector< RenditionPtr > renditions = getRenditions( ); + + if ( renditions.empty( ) ) + return streamUrl; + + if ( !streamId.empty( ) ) + { + // Find the rendition associated with the streamId + for ( vector< RenditionPtr >::iterator it = renditions.begin( ) ; + it != renditions.end(); ++it ) + { + if ( (*it)->getStreamId( ) == streamId ) + { + streamUrl = (*it)->getUrl( ); + break; + } + } + } + else + { + // Automatically find the rendition + + // Prefer ODF format + for ( vector< RenditionPtr >::iterator it = renditions.begin( ) ; + it != renditions.end(); ++it ) + if ( (*it)->getMimeType( ).find( "opendocument") != string::npos ) + return (*it)->getUrl( ); + + // Then MS format + for ( vector< RenditionPtr >::iterator it = renditions.begin( ) ; + it != renditions.end(); ++it ) + if ( (*it)->getMimeType( ).find( "officedocument") != string::npos ) + return (*it)->getUrl( ); + + // If not found, take the first one + streamUrl = renditions.front( )->getUrl( ); + + } + + return streamUrl; +} + +vector< libcmis::FolderPtr > GDriveDocument::getParents( ) +{ + vector< libcmis::FolderPtr > parents; + + vector< string > parentsId = getMultiStringProperty( "cmis:parentId" ); + + // Create folder objects from parent IDs + for ( vector< string >::iterator it = parentsId.begin( ); it != parentsId.end( ); ++it) + { + string parentId = ( *it ); + libcmis::ObjectPtr obj = getSession( )->getObject( parentId ); + libcmis::FolderPtr parent = boost::dynamic_pointer_cast< libcmis::Folder >( obj ); + parents.push_back( parent ); + } + return parents; +} + +boost::shared_ptr< istream > GDriveDocument::getContentStream( string streamId ) +{ + boost::shared_ptr< istream > stream; + string streamUrl = getDownloadUrl( streamId ); + if ( streamUrl.empty( ) ) + throw libcmis::Exception( "can not found stream url" ); + + try + { + stream = getSession( )->httpGetRequest( streamUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return stream; +} + +void GDriveDocument::uploadStream( boost::shared_ptr< ostream > os, + string contentType ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + string putUrl = GDRIVE_UPLOAD_LINK + getId( ) + "?uploadType=media"; + + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector <string> headers; + headers.push_back( string( "Content-Type: " ) + contentType ); + string res; + try + { + res = getSession()->httpPatchRequest( putUrl, *is, headers )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + throw libcmis::Exception( "Document content wasn't set for" + "some reason" ); + refresh( ); +} + +void GDriveDocument::setContentStream( boost::shared_ptr< ostream > os, + string contentType, + string fileName, + bool /*overwrite*/ ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + // TODO: when would the filename need an update? + if (!fileName.empty() && fileName != getContentFilename()) + std::cout << "filename change is not implemented in setContentStream" << std::endl; + + // Upload stream + uploadStream( os, contentType ); +} + +libcmis::DocumentPtr GDriveDocument::checkOut( ) +{ + // GDrive doesn't have CheckOut, so just return the same document here + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkout = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkout; +} + +void GDriveDocument::cancelCheckout( ) +{ + // Don't do anything since we don't have CheckOut +} + +libcmis::DocumentPtr GDriveDocument::checkIn( + bool /*isMajor*/, + std::string /*comment*/, + const PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ) +{ + // GDrive doesn't have CheckIn, so just upload the properties, + // the content stream and fetch the new document resource. + updateProperties( properties ); + setContentStream( stream, contentType, fileName ); + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkin = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkin; +} + + +vector< libcmis::DocumentPtr > GDriveDocument::getAllVersions( ) +{ + vector< libcmis::DocumentPtr > revisions; + string versionUrl = GDRIVE_METADATA_LINK + getId( ) + "/revisions"; + // Run the http request to get the properties definition + string res; + try + { + res = getSession()->httpGetRequest( versionUrl )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["revisions"].getList( ); + + string parentId = getStringProperty( "cmis:parentId" ); + + // Create document objects from Json objects + for(unsigned int i = 0; i < objs.size(); i++) + { + objs[i].add( "parents", GdriveUtils::createJsonFromParentId( parentId ) ); + libcmis::DocumentPtr revision( + new GDriveDocument( getSession(), objs[i], getId( ), getName( ) ) ); + + revisions.push_back( revision ); + } + return revisions; +} + diff --git a/src/libcmis/gdrive-document.hxx b/src/libcmis/gdrive-document.hxx new file mode 100644 index 0000000..aecf270 --- /dev/null +++ b/src/libcmis/gdrive-document.hxx @@ -0,0 +1,90 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_DOCUMENT_HXX_ +#define _GDRIVE_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/rendition.hxx> + +#include "gdrive-object.hxx" +#include "json-utils.hxx" + +class GDriveDocument : public libcmis::Document, public GDriveObject +{ + public: + GDriveDocument( GDriveSession* session ); + + // Create a GDrive document from Json properties. + // In case it's a revision, keep the ID and the name of the original file. + GDriveDocument( GDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + ~GDriveDocument( ); + + std::string getType( ) { return std::string( "cmis:document" );} + std::string getBaseType( ) { return std::string( "cmis:document" );} + + bool isGoogleDoc( ) { return m_isGoogleDoc; } + + /* Get the download Url associated to streamId, + automatically find ODF then MS format if no streamId is specified. + */ + std::string getDownloadUrl( std::string streamId = std::string( ) ); + + void uploadStream( boost::shared_ptr< std::ostream > os, + std::string contentType ); + + virtual std::vector< libcmis::FolderPtr > getParents( ); + virtual boost::shared_ptr< std::istream > getContentStream( + std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName, + bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + virtual libcmis::DocumentPtr checkIn( + bool isMajor, + std::string comment, + const std::map< std::string,libcmis::PropertyPtr >& + properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); + + private: + bool m_isGoogleDoc; +}; + +#endif diff --git a/src/libcmis/gdrive-folder.cxx b/src/libcmis/gdrive-folder.cxx new file mode 100644 index 0000000..26de89b --- /dev/null +++ b/src/libcmis/gdrive-folder.cxx @@ -0,0 +1,192 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-folder.hxx" + +#include "gdrive-session.hxx" +#include "gdrive-document.hxx" +#include "gdrive-property.hxx" +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveFolder::GDriveFolder( GDriveSession* session ): + libcmis::Object( session ), + libcmis::Folder( session ), + GDriveObject( session ) +{ +} + +GDriveFolder::GDriveFolder( GDriveSession* session, Json json ): + libcmis::Object( session ), + libcmis::Folder( session ), + GDriveObject( session, json ) +{ +} + +GDriveFolder::~GDriveFolder( ) +{ +} + +vector< libcmis::ObjectPtr > GDriveFolder::getChildren( ) +{ + vector< libcmis::ObjectPtr > children; + + // GDrive doesn't support fetch all the children in one query. + // Instead of sending multiple queries for children, + // we send a single query to search for objects where parents + // include the folderID. + string query = GDRIVE_METADATA_LINK + "?q=\"" + getId( ) + "\"+in+parents+and+trashed+=+false" + + "&fields=files(kind,id,name,parents,mimeType,createdTime,modifiedTime,thumbnailLink,size)"; + + string res; + try + { + res = getSession( )->httpGetRequest( query )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["files"].getList( ); + + // Create children objects from Json objects + for(unsigned int i = 0; i < objs.size(); i++) + { + ObjectPtr child; + if ( objs[i]["mimeType"].toString( ) == GDRIVE_FOLDER_MIME_TYPE ) + child.reset( new GDriveFolder( getSession( ), objs[i] ) ); + else + child.reset( new GDriveDocument( getSession( ), objs[i] ) ); + children.push_back( child ); + } + + return children; +} + +string GDriveFolder::uploadProperties( Json properties ) +{ + // URL for uploading meta data + string metaUrl = GDRIVE_METADATA_LINK + "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime"; + + // add parents to the properties + properties.add( "parents", GdriveUtils::createJsonFromParentId( getId( ) ) ); + + //upload metadata + std::istringstream is( properties.toString( ) ); + string response; + try + { + response = getSession()->httpPostRequest( metaUrl, is, "application/json" ) + ->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + return response; +} + +libcmis::FolderPtr GDriveFolder::createFolder( + const PropertyPtrMap& properties ) +{ + Json propsJson = GdriveUtils::toGdriveJson( properties ); + + // GDrive folder is a file with a different mime type. + string mimeType = GDRIVE_FOLDER_MIME_TYPE; + + // Add mimetype to the propsJson + Json jsonMimeType( mimeType.c_str( ) ); + propsJson.add( "mimeType", jsonMimeType ); + + // Upload meta-datas + string response = uploadProperties( propsJson ); + + Json jsonRes = Json::parse( response ); + libcmis::FolderPtr folderPtr( new GDriveFolder( getSession( ), jsonRes ) ); + + return folderPtr; +} + +libcmis::DocumentPtr GDriveFolder::createDocument( + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, + string contentType, string fileName ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + Json propsJson = GdriveUtils::toGdriveJson( properties ); + + if(!fileName.empty()) { + // use provided filename + Json jsonFilename( fileName.c_str( ) ); + + propsJson.add( "name", jsonFilename ); + } + if(!contentType.empty()) { + propsJson.add( "mimeType", Json(contentType.c_str())); + } + + // Upload meta-datas + string res = uploadProperties( propsJson); + + // parse the document + Json jsonRes = Json::parse( res ); + + boost::shared_ptr< GDriveDocument > + gDocument( new GDriveDocument( getSession( ), jsonRes ) ); + + // Upload stream + gDocument->uploadStream( os, contentType); + + return gDocument; +} + +vector< string > GDriveFolder::removeTree( + bool /*allVersions*/, + libcmis::UnfileObjects::Type /*unfile*/, + bool /*continueOnError*/ ) +{ + try + { + getSession( )->httpDeleteRequest( GDRIVE_METADATA_LINK + getId( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // Nothing to return here + return vector< string >( ); +} + diff --git a/src/libcmis/gdrive-folder.hxx b/src/libcmis/gdrive-folder.hxx new file mode 100644 index 0000000..aff4737 --- /dev/null +++ b/src/libcmis/gdrive-folder.hxx @@ -0,0 +1,66 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_FOLDER_HXX_ +#define _GDRIVE_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "gdrive-object.hxx" +#include "json-utils.hxx" + +class GDriveFolder : public libcmis::Folder, public GDriveObject +{ + public: + GDriveFolder( GDriveSession* session ); + GDriveFolder( GDriveSession* session, Json json ); + ~GDriveFolder( ); + + std::string getType( ) { return std::string( "cmis:folder" );} + std::string getBaseType( ) { return std::string( "cmis:folder" );} + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( + const libcmis::PropertyPtrMap& properties ); + + virtual libcmis::DocumentPtr createDocument( + const libcmis::PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName ); + + virtual std::vector< std::string > removeTree( + bool allVersion = true, + libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); + + std::string uploadProperties( Json properties ); +}; + +#endif diff --git a/src/libcmis/gdrive-object-type.cxx b/src/libcmis/gdrive-object-type.cxx new file mode 100644 index 0000000..75df2a2 --- /dev/null +++ b/src/libcmis/gdrive-object-type.cxx @@ -0,0 +1,141 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-object-type.hxx" + +GdriveObjectType::GdriveObjectType( const std::string& id ): ObjectType( ) +{ + m_id = id; + m_localName = "GoogleDrive Object Type"; + m_localNamespace = "GoogleDrive Object Type"; + m_displayName = "GoogleDrive Object Type"; + m_queryName = "GoogleDrive Object Type"; + m_description = "GoogleDrive Object Type"; + m_parentTypeId = id; + m_baseTypeId = id; + m_creatable = true; + m_versionable = true; + m_fulltextIndexed = true; + + libcmis::PropertyTypePtr idType(new libcmis::PropertyType( ) ); + idType->setId( "cmis:objectTypeId" ); + idType->setType( libcmis::PropertyType::String ); + m_propertiesTypes[ idType->getId( ) ] = idType; + + // create PropertyTypes which are updatable. + + // title + libcmis::PropertyTypePtr nameType( new libcmis::PropertyType( ) ); + nameType->setId( "cmis:name" ); + nameType->setType( libcmis::PropertyType::String ); + nameType->setUpdatable( true ); + m_propertiesTypes[ nameType->getId( ) ] = nameType; + + // mimeType + libcmis::PropertyTypePtr mimeType( new libcmis::PropertyType( ) ); + mimeType->setId( "cmis:contentStreamMimeType" ); + mimeType->setType( libcmis::PropertyType::String ); + mimeType->setUpdatable( false ); + m_propertiesTypes[ mimeType->getId( ) ] = mimeType; + + // parents + libcmis::PropertyTypePtr parentsType( new libcmis::PropertyType( ) ); + parentsType->setId( "cmis:parentId" ); + parentsType->setType( libcmis::PropertyType::String ); + parentsType->setUpdatable( false ); + parentsType->setMultiValued( true ); + m_propertiesTypes[ parentsType->getId( ) ] = parentsType; + + // labels + libcmis::PropertyTypePtr labelsType( new libcmis::PropertyType( ) ); + labelsType->setId( "labels" ); + labelsType->setType( libcmis::PropertyType::String ); + labelsType->setUpdatable( false ); + labelsType->setMultiValued( true ); + m_propertiesTypes[ labelsType->getId( ) ] = labelsType; + + // ownerNames + libcmis::PropertyTypePtr ownerNamesType( new libcmis::PropertyType( ) ); + ownerNamesType->setId( "ownerNames" ); + ownerNamesType->setType( libcmis::PropertyType::String ); + ownerNamesType->setUpdatable( false ); + ownerNamesType->setMultiValued( true ); + m_propertiesTypes[ ownerNamesType->getId( ) ] = ownerNamesType; + + // owners + libcmis::PropertyTypePtr ownersType( new libcmis::PropertyType( ) ); + ownersType->setId( "owners" ); + ownersType->setType( libcmis::PropertyType::String ); + ownersType->setUpdatable( false ); + ownersType->setMultiValued( true ); + m_propertiesTypes[ ownersType->getId( ) ] = ownersType; + + // export links + libcmis::PropertyTypePtr exportLinksType( new libcmis::PropertyType( ) ); + exportLinksType->setId( "exportLinks" ); + exportLinksType->setType( libcmis::PropertyType::String ); + exportLinksType->setUpdatable( false ); + exportLinksType->setMultiValued( true ); + m_propertiesTypes[ exportLinksType->getId( ) ] = exportLinksType; + + // description + libcmis::PropertyTypePtr descriptionType( new libcmis::PropertyType( ) ); + descriptionType->setId( "cmis:description" ); + descriptionType->setType( libcmis::PropertyType::String ); + descriptionType->setUpdatable( true ); + m_propertiesTypes[ descriptionType->getId( ) ] = descriptionType; + + // modifiedDate + libcmis::PropertyTypePtr modifiedDateType( new libcmis::PropertyType( ) ); + modifiedDateType->setId( "cmis:lastModificationDate" ); + modifiedDateType->setType( libcmis::PropertyType::DateTime ); + modifiedDateType->setUpdatable( true ); + m_propertiesTypes[ modifiedDateType->getId( ) ] = modifiedDateType; + + // lastViewedByMeDate + libcmis::PropertyTypePtr lastViewedByMeDateType( new libcmis::PropertyType( ) ); + lastViewedByMeDateType->setId( "lastViewedByMeDate" ); + lastViewedByMeDateType->setType( libcmis::PropertyType::DateTime ); + lastViewedByMeDateType->setUpdatable( true ); + m_propertiesTypes[ lastViewedByMeDateType->getId( ) ] = lastViewedByMeDateType; + +} + + +libcmis::ObjectTypePtr GdriveObjectType::getParentType( ) +{ + libcmis::ObjectTypePtr parentTypePtr( new GdriveObjectType( m_parentTypeId ) ); + return parentTypePtr; +} + +libcmis::ObjectTypePtr GdriveObjectType::getBaseType( ) +{ + libcmis::ObjectTypePtr baseTypePtr( new GdriveObjectType( m_baseTypeId ) ); + return baseTypePtr; +} + diff --git a/src/libcmis/gdrive-object-type.hxx b/src/libcmis/gdrive-object-type.hxx new file mode 100644 index 0000000..3f1a258 --- /dev/null +++ b/src/libcmis/gdrive-object-type.hxx @@ -0,0 +1,46 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_OBJECT_TYPE_HXX_ +#define _GDRIVE_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "json-utils.hxx" + +class GdriveObjectType: public libcmis::ObjectType +{ + public: + GdriveObjectType( const std::string& id ); + + virtual libcmis::ObjectTypePtr getParentType( ); + + virtual libcmis::ObjectTypePtr getBaseType( ); +}; + +#endif diff --git a/src/libcmis/gdrive-object.cxx b/src/libcmis/gdrive-object.cxx new file mode 100644 index 0000000..b472e2f --- /dev/null +++ b/src/libcmis/gdrive-object.cxx @@ -0,0 +1,276 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 SUSE <cbosdonnat@suse.com> + * 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-object.hxx" + +#include "gdrive-property.hxx" +#include "gdrive-allowable-actions.hxx" +#include "gdrive-repository.hxx" +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveObject::GDriveObject( GDriveSession* session ) : + libcmis::Object( session ) +{ +} + +GDriveObject::GDriveObject( GDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session ) +{ + initializeFromJson( json, id, name ); +} + +GDriveObject::GDriveObject( const GDriveObject& copy ) : + libcmis::Object( copy ) +{ +} + +GDriveObject& GDriveObject::operator=( const GDriveObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + return *this; +} + +void GDriveObject::initializeFromJson ( Json json, string id, string name ) +{ + Json::JsonObject objs = json.getObjects( ); + Json::JsonObject::iterator it; + for ( it = objs.begin( ); it != objs.end( ); ++it) + { + PropertyPtr property; + + // in case of a revision, get the ID and name of the original file + if ( !id.empty( ) && it->first == "id" ) + { + Json idJson( id.c_str( ) ); + property.reset( new GDriveProperty( "id", idJson ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + property.reset( new GDriveProperty( "revisionId", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + + Json nameJson( name.c_str( ) ); + property.reset( new GDriveProperty( "cmis:name", nameJson) ); + m_properties[ property->getPropertyType( )->getId()] = property; + property.reset( new GDriveProperty( "cmis:contentStreamFileName", nameJson) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + else + { + property.reset( new GDriveProperty( it->first, it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + + // we map "name" to both "cmis:name" and "cmis:getContentStreamFileName" + if ( it->first == "name" ) + { + property.reset( new GDriveProperty( "cmis:name", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + + // some revision keep the original file name. + if ( it->first == "originalFilename" ) + { + property.reset( new GDriveProperty( "cmis:name", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + property.reset( new GDriveProperty( "cmis:contentStreamFileName", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + + // In case of a revision, get the modified date as creation date + if ( it->first == "modifiedDate" && !id.empty( ) ) + { + property.reset( new GDriveProperty( "cmis:creationDate", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + // In case of a revision, get the last modifying user and the creator + if ( it->first == "lastModifyingUserName" && !id.empty( ) ) + { + property.reset( new GDriveProperty( "cmis:createdBy", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + } + } + m_refreshTimestamp = time( NULL ); + + // Create AllowableActions + bool isFolder = json["mimeType"].toString( ) == GDRIVE_FOLDER_MIME_TYPE; + m_allowableActions.reset( new GdriveAllowableActions( isFolder ) ); +} + +GDriveSession* GDriveObject::getSession( ) +{ + return dynamic_cast< GDriveSession* > ( m_session ); +} + +void GDriveObject::refreshImpl( Json json ) +{ + m_typeDescription.reset( ); + m_properties.clear( ); + initializeFromJson( json ); +} + +vector< RenditionPtr> GDriveObject::getRenditions( string /* filter */ ) +{ + if ( m_renditions.empty( ) ) + { + string downloadUrl = GDRIVE_METADATA_LINK + getId( ) + "?alt=media"; + string mimeType = getStringProperty( "cmis:contentStreamMimeType" ); + if ( !mimeType.empty( ) ) + { + RenditionPtr rendition( + new Rendition( mimeType, mimeType, mimeType, downloadUrl )); + m_renditions.push_back( rendition ); + } + + vector< string > exportLinks = getMultiStringProperty( "exportLinks" ); + for ( vector<string>::iterator it = exportLinks.begin( ); it != exportLinks.end( ); ++it) + { + int pos = (*it).find(":\""); + if ( pos == -1 ) continue; + mimeType = (*it).substr( 0, pos ); + string url = (*it).substr( pos + 2, (*it).length( ) - pos - 3 ); + RenditionPtr rendition( + new Rendition( mimeType, mimeType, mimeType, url ) ); + m_renditions.push_back( rendition ); + } + + // thumbnail link + string thumbnailLink = getStringProperty( "thumbnailLink" ); + if ( !thumbnailLink.empty( ) ) + { + mimeType = "cmis:thumbnail"; + RenditionPtr rendition( + new Rendition( mimeType, mimeType, mimeType, thumbnailLink )); + m_renditions.push_back( rendition ); + } + } + return m_renditions; +} + +libcmis::ObjectPtr GDriveObject::updateProperties( + const PropertyPtrMap& properties ) +{ + // Make Json object from properties + Json json = GdriveUtils::toGdriveJson( properties ); + + istringstream is( json.toString( )); + + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/json" ); + response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + libcmis::ObjectPtr updated( new GDriveObject ( getSession( ), jsonRes ) ); + + if ( updated->getId( ) == getId( ) ) + refreshImpl( jsonRes ); + + return updated; +} + +void GDriveObject::refresh( ) +{ + string res; + try + { + res = getSession()->httpGetRequest( getUrl( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json json = Json::parse( res ); + refreshImpl( json ); +} + +void GDriveObject::remove( bool /*allVersions*/ ) +{ + try + { + getSession( )->httpDeleteRequest( GDRIVE_METADATA_LINK + getId( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +void GDriveObject::move( FolderPtr /*source*/, FolderPtr destination ) +{ + Json parentsJson; + parentsJson.add( "addParents", Json(destination->getId( ).c_str()) ); + parentsJson.add( "removeParents", Json(getStringProperty( "cmis:parentId" ).c_str()) ); + + istringstream is( parentsJson.toString( ) ); + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/json" ); + response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + + refreshImpl( jsonRes ); +} + +string GDriveObject::getUrl( ) +{ + // thumbnailLink causes some operations to fail with internal server error, + // see https://issuetracker.google.com/issues/36760667 + return GDRIVE_METADATA_LINK + getId( ) + + "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime,size"; +} + +vector< string> GDriveObject::getMultiStringProperty( const string& propertyName ) +{ + vector< string > values; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + values = it->second->getStrings( ); + return values; +} + diff --git a/src/libcmis/gdrive-object.hxx b/src/libcmis/gdrive-object.hxx new file mode 100644 index 0000000..4a2225f --- /dev/null +++ b/src/libcmis/gdrive-object.hxx @@ -0,0 +1,87 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _GDRIVE_OBJECT_HXX_ +#define _GDRIVE_OBJECT_HXX_ + +#include <libcmis/object.hxx> + +#include "gdrive-session.hxx" +#include "json-utils.hxx" + +/** Class representing an object for Google Drive protocol. + + This class overrides quite a number of its parent class methods to + git the Google Drive API into the libcmis API. + + In order to reuse more of the base Object class, this class needs + to map the main properties (like id, name, etc) to CMIS property + ids (cmis:id, cmis:name, etc). + */ +class GDriveObject : public virtual libcmis::Object +{ + public: + GDriveObject( GDriveSession* session ); + + // Create a GDrive document from Json properties. + // In case it's a revision, keep the ID and the name of the original file. + GDriveObject( GDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + GDriveObject( const GDriveObject& copy ); + virtual ~GDriveObject( ) { } + + GDriveObject& operator=( const GDriveObject& copy ); + + void initializeFromJson( Json json, std::string id = std::string( ), + std::string name = std::string( ) ); + void refreshImpl( Json json ); + Json createJsonFromParentId( const std::string& parentId ); + + std::string getUrl( ); + std::string getUploadUrl( ); + std::vector< std::string > getMultiStringProperty( + const std::string& propertyName ); + + virtual std::vector< libcmis::RenditionPtr> getRenditions( std::string filter = std::string( ) ); + + virtual boost::shared_ptr< Object > updateProperties( + const libcmis::PropertyPtrMap& properties ); + + virtual void refresh( ); + + virtual void remove( bool allVersions = true ); + + virtual void move( boost::shared_ptr< libcmis::Folder > source, + boost::shared_ptr< libcmis::Folder > destination ); + + protected: + GDriveSession* getSession( ); + +}; + +#endif diff --git a/src/libcmis/gdrive-property.cxx b/src/libcmis/gdrive-property.cxx new file mode 100644 index 0000000..f75c2ed --- /dev/null +++ b/src/libcmis/gdrive-property.cxx @@ -0,0 +1,79 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-property.hxx" + +#include <libcmis/property-type.hxx> + +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveProperty::GDriveProperty( ) +{ +} + +GDriveProperty::~GDriveProperty( ) +{ +} + +GDriveProperty::GDriveProperty( const string& key, Json json ): + Property( ) +{ + PropertyTypePtr propertyType( new PropertyType( ) ); + string convertedKey = GdriveUtils::toCmisKey( key ); + propertyType->setId( convertedKey ); + propertyType->setLocalName( convertedKey ); + propertyType->setLocalNamespace( convertedKey ); + propertyType->setQueryName( convertedKey ); + propertyType->setDisplayName( key ); + propertyType->setTypeFromJsonType( json.getStrType( ) ); + propertyType->setUpdatable( GdriveUtils::checkUpdatable( key ) ); + propertyType->setMultiValued( GdriveUtils::checkMultiValued( key ) ); + + setPropertyType( propertyType ); + + vector< string > values = GdriveUtils::parseGdriveProperty( key, json ); + setValues( values ); +} + +GDriveProperty::GDriveProperty( const GDriveProperty& copy ) : + libcmis::Property( copy ) +{ +} + +GDriveProperty& GDriveProperty::operator=( const GDriveProperty& copy ) +{ + if ( this != © ) + { + libcmis::Property::operator=( copy ); + } + return *this; +} + diff --git a/src/libcmis/gdrive-property.hxx b/src/libcmis/gdrive-property.hxx new file mode 100644 index 0000000..4edab78 --- /dev/null +++ b/src/libcmis/gdrive-property.hxx @@ -0,0 +1,51 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_PROPERTY_HXX_ +#define _GDRIVE_PROPERTY_HXX_ + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +class GDriveProperty : public libcmis::Property +{ + public : + // Create a GDrive Property from a Json property with its key + GDriveProperty( const std::string& key, Json json); + ~GDriveProperty( ); + GDriveProperty( const GDriveProperty& copy); + GDriveProperty& operator=( const GDriveProperty& copy ); + + // Check if the property is updatable + bool checkUpdatable( const std::string& key ); + private : + // Avoid calling default constructor + GDriveProperty( ); +}; +#endif /* _GDRIVE_PROPERTY_HXX_ */ diff --git a/src/libcmis/gdrive-repository.cxx b/src/libcmis/gdrive-repository.cxx new file mode 100644 index 0000000..24b42b5 --- /dev/null +++ b/src/libcmis/gdrive-repository.cxx @@ -0,0 +1,55 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-repository.hxx" + +GdriveRepository::GdriveRepository( ) : + Repository( ) +{ + m_id = "GoogleDrive"; + m_name = "Google Drive"; + m_description = "Google Drive repository"; + m_productName = "Google Drive"; + m_productVersion = "v3"; + m_rootId = "root"; + + m_capabilities[ ACL ] = "discover"; + m_capabilities[ AllVersionsSearchable ] = "true"; + m_capabilities[ Changes ] = "all"; + m_capabilities[ GetDescendants ] = "true"; + m_capabilities[ GetFolderTree ] = "true"; + m_capabilities[ OrderBy ] = "custom"; + m_capabilities[ Multifiling ] = "true"; + m_capabilities[ PWCSearchable ] = "true"; + m_capabilities[ PWCUpdatable ] = "true"; + m_capabilities[ Query ] = "bothcombined"; + m_capabilities[ Renditions ] = "read"; + m_capabilities[ Unfiling ] = "false"; + m_capabilities[ VersionSpecificFiling ] = "false"; + m_capabilities[ Join ] = "none"; +} diff --git a/src/libcmis/gdrive-repository.hxx b/src/libcmis/gdrive-repository.hxx new file mode 100644 index 0000000..215a13c --- /dev/null +++ b/src/libcmis/gdrive-repository.hxx @@ -0,0 +1,41 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_REPOSITORY_HXX_ +#define _GDRIVE_REPOSITORY_HXX_ + +#include <libcmis/repository.hxx> + +class GdriveRepository: public libcmis::Repository +{ + public: + GdriveRepository( ); +}; + +#endif + diff --git a/src/libcmis/gdrive-session.cxx b/src/libcmis/gdrive-session.cxx new file mode 100644 index 0000000..1ee748e --- /dev/null +++ b/src/libcmis/gdrive-session.cxx @@ -0,0 +1,254 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-session.hxx" + +#include <libcmis/object-type.hxx> + +#include "oauth2-handler.hxx" +#include "gdrive-document.hxx" +#include "gdrive-folder.hxx" +#include "gdrive-repository.hxx" +#include "gdrive-object-type.hxx" +#include "gdrive-utils.hxx" + +using namespace std; + +GDriveSession::GDriveSession ( string baseUrl, + string username, + string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose ) : + BaseSession( baseUrl, string(), username, password, false, + libcmis::OAuth2DataPtr(), verbose ) + +{ + // Add the dummy repository, even if we don't have OAuth2 + m_repositories.push_back( getRepository( ) ); + + if ( oauth2 && oauth2->isComplete( ) ){ + setOAuth2Data( oauth2 ); + } +} + +GDriveSession::GDriveSession() : + BaseSession() +{ +} + +GDriveSession::~GDriveSession() +{ +} + + +void GDriveSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); + + oauth2Authenticate( ); +} + +void GDriveSession::oauth2Authenticate() +{ + // treat the supplied password as refresh token + if (!m_password.empty()) + { + try + { + m_inOAuth2Authentication = true; + + m_oauth2Handler->setRefreshToken(m_password); + // Try to get new access tokens using the stored refreshtoken + m_oauth2Handler->refresh(); + m_inOAuth2Authentication = false; + } + catch (const CurlException &e) + { + m_inOAuth2Authentication = false; + // refresh token expired or invalid, trigger initial auth (that in turn will hit the fallback with copy'n'paste method) + BaseSession::oauth2Authenticate(); + } + } + else + { + BaseSession::oauth2Authenticate(); + } +} + +string GDriveSession::getRefreshToken() { + return HttpSession::getRefreshToken(); +} + +libcmis::RepositoryPtr GDriveSession::getRepository( ) +{ + // Return a dummy repository since GDrive doesn't have that notion + libcmis::RepositoryPtr repo( new GdriveRepository( ) ); + return repo; +} + +bool GDriveSession::setRepository( std::string ) +{ + return true; +} + +libcmis::ObjectPtr GDriveSession::getObject( string objectId ) +{ + if(objectId == "root") { + return getRootFolder(); + } + // Run the http request to get the properties definition + string res; + string objectLink = GDRIVE_METADATA_LINK + objectId + + "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime,thumbnailLink,size"; + try + { + res = httpGetRequest( objectLink )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + + // If we have a folder, then convert the object + // into a GDriveFolder otherwise, convert it + // into a GDriveDocument + libcmis::ObjectPtr object; + string kind = jsonRes["kind"].toString( ); + if ( kind == "drive#file" ) + { + string mimeType = jsonRes["mimeType"].toString( ); + + // Folder is a file with a special mimeType + if ( mimeType == GDRIVE_FOLDER_MIME_TYPE ) + object.reset( new GDriveFolder( this, jsonRes ) ); + else + object.reset( new GDriveDocument( this, jsonRes ) ); + } else if ( kind == "drive#revision" ) // A revision is a document too + { + object.reset( new GDriveDocument( this, jsonRes ) ); + } + else // not a folder nor file, maybe a permission or changes,... + object.reset( new GDriveObject( this, jsonRes ) ); + + return object; +} + +libcmis::ObjectPtr GDriveSession::getObjectByPath( string path ) +{ + size_t pos = 0; + size_t endpos = 0; + string objectId; + libcmis::ObjectPtr object; + + do + { + endpos = path.find( "/", pos ); + size_t len = path.length( ) - pos; + if ( endpos != string::npos ) + len = endpos - pos; + + string segment = path.substr( pos, len ); + if ( segment.empty( ) ) + { + // Root case or ignore double slashes + if ( pos == 0 ) + objectId = "root"; + else + continue; + } + else + { + // Normal child case + // Ask for the ID of the child if there is any + // somewhat flawed as names are not necessarily unique in GDrive... + string query = libcmis::escape("'" + objectId + "' in parents and trashed = false and name='" + segment + "'"); + + string childIdUrl = m_bindingUrl + "/files/?q=" + query + "&fields=files(id)"; + + string res; + try + { + res = httpGetRequest( childIdUrl )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + + // Did we get an id? + Json::JsonVector items = jsonRes["files"].getList(); + if ( items.empty( ) ) + throw libcmis::Exception( "Object not found: " + path, "objectNotFound" ); + + objectId = items[0]["id"].toString( ); + if ( objectId.empty( ) ) + throw libcmis::Exception( "Object not found: " + path, "objectNotFound" ); + } + + pos = endpos + 1; + } while ( endpos != string::npos ); + + return getObject( objectId ); +} + +libcmis::FolderPtr GDriveSession::getRootFolder() +{ + // permissions/scope with just drive.file don't allow to get it with the "root" alias/by its actual object-ID + Json propsJson; + + // GDrive folder is a file with a different mime type. + string mimeType = GDRIVE_FOLDER_MIME_TYPE; + + // Add mimetype to the propsJson + Json jsonMimeType( mimeType.c_str( ) ); + propsJson.add( "mimeType", jsonMimeType ); + propsJson.add( "id", "root" ); + + // Upload meta-datas + propsJson.add("cmis:name", "VirtualRoot"); + + libcmis::FolderPtr folderPtr( new GDriveFolder( this, propsJson ) ); + + return folderPtr; +} + +libcmis::ObjectTypePtr GDriveSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new GdriveObjectType( id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > GDriveSession::getBaseTypes( ) +{ + vector< libcmis::ObjectTypePtr > types; + // TODO Implement me + return types; +} diff --git a/src/libcmis/gdrive-session.hxx b/src/libcmis/gdrive-session.hxx new file mode 100644 index 0000000..d29d454 --- /dev/null +++ b/src/libcmis/gdrive-session.hxx @@ -0,0 +1,72 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _GDRIVE_SESSION_HXX_ +#define _GDRIVE_SESSION_HXX_ + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" + +class GDriveSession : public BaseSession +{ + public: + GDriveSession( std::string baseUrl, + std::string username, + std::string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose = false ); + + ~GDriveSession ( ); + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + virtual libcmis::FolderPtr getRootFolder(); + + virtual std::string getRefreshToken(); + + private: + GDriveSession( ); + GDriveSession( const GDriveSession& copy ) = delete; + GDriveSession& operator=( const GDriveSession& copy ) = delete; + + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + void oauth2Authenticate( ); +}; + +#endif /* _GDRIVE_SESSION_HXX_ */ diff --git a/src/libcmis/gdrive-utils.cxx b/src/libcmis/gdrive-utils.cxx new file mode 100644 index 0000000..3cc0288 --- /dev/null +++ b/src/libcmis/gdrive-utils.cxx @@ -0,0 +1,221 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-utils.hxx" + +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +string GdriveUtils::toCmisKey( const string& key ) +{ + string convertedKey; + if ( key == "id") + convertedKey = "cmis:objectId"; + else if ( key == "ownerNames" ) + convertedKey = "cmis:createdBy"; + else if ( key == "description" ) + convertedKey = "cmis:description"; + else if ( key == "createdTime" ) + convertedKey = "cmis:creationDate"; + else if ( key == "lastModifyingUserName" ) + convertedKey = "cmis:lastModifiedBy"; + else if ( key == "modifiedTime" ) + convertedKey = "cmis:lastModificationDate"; + else if ( key == "name" ) + convertedKey = "cmis:contentStreamFileName"; + else if ( key == "mimeType" ) + convertedKey = "cmis:contentStreamMimeType"; + else if ( key == "size" ) + convertedKey = "cmis:contentStreamLength"; + else if ( key == "editable" ) + convertedKey = "cmis:isImmutable"; + else if ( key == "parents" ) + convertedKey = "cmis:parentId"; + else convertedKey = key; + return convertedKey; +} + +string GdriveUtils::toGdriveKey( const string& key ) +{ + string convertedKey; + if ( key == "cmis:objectId") + convertedKey = "id"; + else if ( key == "cmis:createdBy" ) + convertedKey = "ownerNames"; + else if ( key == "cmis:creationDate" ) + convertedKey = "createdTime"; + else if ( key == "cmis:description" ) + convertedKey = "description"; + else if ( key == "cmis:lastModifiedBy" ) + convertedKey = "lastModifyingUserName"; + else if ( key == "cmis:lastModificationDate" ) + convertedKey = "modifiedTime"; + else if ( key == "cmis:contentStreamFileName" ) + convertedKey = "name"; + else if ( key == "cmis:name" ) + convertedKey = "name"; + else if ( key == "cmis:contentStreamMimeType" ) + convertedKey = "mimeType"; + else if ( key == "cmis:contentStreamLength" ) + convertedKey = "size"; + else if ( key == "cmis:isImmutable" ) + convertedKey = "editable"; + else if ( key == "cmis:parentId" ) + convertedKey = "parents"; + else convertedKey = key; + return convertedKey; +} + +Json GdriveUtils::toGdriveJson( const PropertyPtrMap& properties ) +{ + Json propsJson; + + // check if cmis:name and cmis:contentStreamFileName has been duplicated + bool duplicated = false; + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + string key = it->first; + Json value( it->second ); + + // Convert the key back to the gdrive key + // take one of the two: cmis:name and cmis:contentStreamFileName + const bool isName = key == "cmis:name" || key == "cmis:contentStreamFileName"; + + if ( !isName || !duplicated ) + propsJson.add( toGdriveKey( key ), value ); + + if ( isName ) + duplicated = true; + } + + return propsJson; +} + +bool GdriveUtils::checkUpdatable( const string& key ) +{ + // taken from https://developers.google.com/drive/v2/reference/files + bool updatable = ( key == "name" || + key == "description" || + key == "modifiedTime" || + key == "lastViewedByMeDate" ); + return updatable; +} + +bool GdriveUtils::checkMultiValued( const string& key ) +{ + bool bMultiValued = ( key == "parents" || + key == "exportLinks" || + key == "labels" || + key == "ownersName" || + key == "owners"); + return bMultiValued; +} + +Json GdriveUtils::createJsonFromParentId( const string& parentId ) +{ + // parents is a Json array + Json firstParent; + firstParent.add( Json( parentId.c_str() ) ); + + return firstParent; +} + +vector< string > GdriveUtils::parseGdriveProperty( string key, Json json ) +{ + vector< string > values; + if ( key == "owners" ) + { + Json::JsonVector owners = json.getList( ); + for ( Json::JsonVector::iterator it = owners.begin( ); + it != owners.end( ); ++it ) + { + string ownerName = ( *it )["displayName"].toString( ); + values.push_back( ownerName); + } + } + else if ( key == "lastModifyingUser" ) + { + string ownerName = json["displayName"].toString( ); + values.push_back( ownerName); + } + else if ( key == "userPermission" ) + { + string ownerName = json["role"].toString( ); + values.push_back( ownerName); + } + else if ( key == "ownerNames" ) + { + Json::JsonVector owners = json.getList( ); + for ( Json::JsonVector::iterator it = owners.begin( ); + it != owners.end( ); ++it ) + { + string ownerName = ( *it )[""].toString( ); + values.push_back( ownerName); + } + } + else if ( key == "parents" ) + { + Json::JsonVector owners = json.getList( ); + for ( Json::JsonVector::iterator it = owners.begin( ); + it != owners.end( ); ++it ) + { + string ownerName = ( *it )["id"].toString( ); + values.push_back( ownerName); + } + } + else if ( key == "exportLinks" ) + { + Json::JsonObject exportLinks = json.getObjects( ); + for ( Json::JsonObject::iterator it = exportLinks.begin( ); + it != exportLinks.end( ); ++it ) + { + string mimeType = it->first; + string link = it->second.toString( ); + values.push_back( mimeType + ":\"" + link +"\""); + } + } + else if ( key == "labels" ) + { + Json::JsonObject labels = json.getObjects( ); + for ( Json::JsonObject::iterator it = labels.begin( ); + it != labels.end( ); ++it ) + { + string label = it->first; + string isSet = it->second.toString( ); + values.push_back( label + ": " + isSet ); + } + } + else values.push_back( json.toString( ) ); + return values; +} + diff --git a/src/libcmis/gdrive-utils.hxx b/src/libcmis/gdrive-utils.hxx new file mode 100644 index 0000000..06ad568 --- /dev/null +++ b/src/libcmis/gdrive-utils.hxx @@ -0,0 +1,67 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _GDRIVE_UTILS_HXX_ +#define _GDRIVE_UTILS_HXX_ + +#include <string> + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +static const std::string GDRIVE_FOLDER_MIME_TYPE = "application/vnd.google-apps.folder" ; +static const std::string GDRIVE_UPLOAD_LINK = "https://www.googleapis.com/upload/drive/v3/files/"; +static const std::string GDRIVE_METADATA_LINK = "https://www.googleapis.com/drive/v3/files/"; + +class GdriveUtils +{ + public : + + // Convert a GDrive Property key to a CMIS key + static std::string toCmisKey( const std::string& key); + + // Convert a CMIS key to GDrive key + static std::string toGdriveKey( const std::string& key ); + + // Convert CMIS properties to GDrive properties + static Json toGdriveJson( const libcmis::PropertyPtrMap& properties ); + + // Check if a property is updatable + static bool checkUpdatable( const std::string& key); + + // Check if a property has multiple values + static bool checkMultiValued( const std::string& key); + + // Create a Json array from a ParentId + static Json createJsonFromParentId( const std::string& parentId ); + + // Parse a Gdrive property value to CMIS values + static std::vector< std::string > parseGdriveProperty( std::string key, Json jsonValue ); +}; + +#endif diff --git a/src/libcmis/http-session.cxx b/src/libcmis/http-session.cxx new file mode 100644 index 0000000..f690914 --- /dev/null +++ b/src/libcmis/http-session.cxx @@ -0,0 +1,994 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "http-session.hxx" + +#include <cctype> +#include <memory> +#include <string> +#include <assert.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/session-factory.hxx> +#include <libcmis/xml-utils.hxx> + +#include "oauth2-handler.hxx" + +using namespace std; + +namespace +{ + size_t lcl_getHeaders( void *ptr, size_t size, size_t nmemb, void *userdata ) + { + libcmis::HttpResponse* response = static_cast< libcmis::HttpResponse* >( userdata ); + + string buf( ( const char* ) ptr, size * nmemb ); + + size_t sepPos = buf.find( ':' ); + if ( sepPos != string::npos ) + { + string name( buf, 0, sepPos ); + string value = buf.substr( sepPos + 1 ); + value = libcmis::trim( value ); + + response->getHeaders()[name] = value; + + if ( "Content-Transfer-Encoding" == name ) + response->getData( )->setEncoding( value ); + } + + return nmemb; + } + + size_t lcl_bufferData( void* buffer, size_t size, size_t nmemb, void* data ) + { + libcmis::EncodedData* encoded = static_cast< libcmis::EncodedData* >( data ); + encoded->decode( buffer, size, nmemb ); + return nmemb; + } + + size_t lcl_readStream( void* buffer, size_t size, size_t nmemb, void* data ) + { + istream& is = *( static_cast< istream* >( data ) ); + char* out = ( char * ) buffer; + is.read( out, size * nmemb ); + + return is.gcount( ) / size; + } + +#if (LIBCURL_VERSION_MAJOR < 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 85) + curlioerr lcl_ioctlStream( CURL* /*handle*/, int cmd, void* data ) + { + curlioerr errCode = CURLIOE_OK; + + switch ( cmd ) + { + case CURLIOCMD_RESTARTREAD: + { + istream& is = *( static_cast< istream* >( data ) ); + is.clear( ); + is.seekg( 0, ios::beg ); + + if ( !is.good() ) + { + fprintf ( stderr, "rewind failed\n" ); + errCode = CURLIOE_FAILRESTART; + } + } + break; + case CURLIOCMD_NOP: + break; + default: + errCode = CURLIOE_UNKNOWNCMD; + } + return errCode; + } +#endif + + int lcl_seekStream(void* data, curl_off_t offset, int origin) + { + std::ios_base::seekdir dir = {}; + switch (origin) + { + case SEEK_SET: dir = std::ios_base::beg; break; + case SEEK_CUR: dir = std::ios_base::cur; break; + case SEEK_END: dir = std::ios_base::end; break; + default: assert(false); break; + } + istream& is = *(static_cast<istream*>(data)); + is.clear(); + is.seekg(offset, dir); + if (!is.good()) + { + fprintf(stderr, "rewind failed\n"); + return CURL_SEEKFUNC_FAIL; + } + return CURL_SEEKFUNC_OK; + } + + template<typename T> + class ScopeGuard + { + public: + ScopeGuard(T &var, T newValue) + : m_var(var) + , m_origValue(var) + { + m_var = newValue; + } + + ~ScopeGuard() + { + m_var = m_origValue; + } + + private: + T& m_var; + const T m_origValue; + }; +} + +HttpSession::HttpSession( string username, string password, bool noSslCheck, + libcmis::OAuth2DataPtr oauth2, bool verbose, + libcmis::CurlInitProtocolsFunction initProtocolsFunction) : + m_curlHandle( NULL ), + m_CurlInitProtocolsFunction(initProtocolsFunction), + m_no100Continue( false ), + m_oauth2Handler( NULL ), + m_username( username ), + m_password( password ), + m_authProvided( false ), + m_verbose( verbose ), + m_noHttpErrors( false ), + m_noSSLCheck( noSslCheck ), + m_refreshedToken( false ), + m_inOAuth2Authentication( false ), + m_authMethod( CURLAUTH_ANY ) +{ + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); + + if ( oauth2 && oauth2->isComplete( ) ){ + setOAuth2Data( oauth2 ); + } +} + +HttpSession::HttpSession( const HttpSession& copy ) : + m_curlHandle( NULL ), + m_no100Continue( copy.m_no100Continue ), + m_oauth2Handler( copy.m_oauth2Handler ), + m_username( copy.m_username ), + m_password( copy.m_password ), + m_authProvided( copy.m_authProvided ), + m_verbose( copy.m_verbose ), + m_noHttpErrors( copy.m_noHttpErrors ), + m_noSSLCheck( copy.m_noSSLCheck ), + m_refreshedToken( false ), + m_inOAuth2Authentication( false ), + m_authMethod( copy.m_authMethod ) +{ + // Not sure how sharing curl handles is safe. + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); +} + +HttpSession::HttpSession( ) : + m_curlHandle( NULL ), + m_no100Continue( false ), + m_oauth2Handler( NULL ), + m_username( ), + m_password( ), + m_authProvided( false ), + m_verbose( false ), + m_noHttpErrors( false ), + m_noSSLCheck( false ), + m_refreshedToken( false ), + m_inOAuth2Authentication( false ), + m_authMethod( CURLAUTH_ANY ) +{ + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); +} + +HttpSession& HttpSession::operator=( const HttpSession& copy ) +{ + if ( this != © ) + { + curl_easy_cleanup( m_curlHandle ); + m_curlHandle = NULL; + m_no100Continue = copy.m_no100Continue; + m_oauth2Handler = copy.m_oauth2Handler; + m_username = copy.m_username; + m_password = copy.m_password; + m_authProvided = copy.m_authProvided; + m_verbose = copy.m_verbose; + m_noHttpErrors = copy.m_noHttpErrors; + m_noSSLCheck = copy.m_noSSLCheck; + m_refreshedToken = copy.m_refreshedToken; + m_inOAuth2Authentication = copy.m_inOAuth2Authentication; + m_authMethod = copy.m_authMethod; + + // Not sure how sharing curl handles is safe. + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); + } + + return *this; +} + +HttpSession::~HttpSession( ) +{ + if ( NULL != m_curlHandle ) + curl_easy_cleanup( m_curlHandle ); + delete( m_oauth2Handler ); +} + +string& HttpSession::getUsername( ) +{ + checkCredentials( ); + return m_username; +} + +string& HttpSession::getPassword( ) +{ + checkCredentials( ); + return m_password; +} + +libcmis::HttpResponsePtr HttpSession::httpGetRequest( string url ) +{ + checkOAuth2( url ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + // fix Cloudoku too many redirects error + // note: though curl doc says -1 is the default for MAXREDIRS, the error i got + // said it was 0 + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + try + { + httpRunRequest( url ); + response->getData( )->finish( ); + } + catch ( const CurlException& ) + { + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( getHttpStatus( ) == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpGetRequest( url ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + throw; + } + m_refreshedToken = false; + } + else throw; + } + m_refreshedToken = false; + + return response; +} + +libcmis::HttpResponsePtr HttpSession::httpPatchRequest( string url, istream& is, vector< string > headers ) +{ + checkOAuth2( url ); + + // Duplicate istream in case we need to retry + string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) ); + + istringstream isOriginal( isStr ), isBackup( isStr ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + // Get the stream length + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + curl_easy_setopt( m_curlHandle, CURLOPT_INFILESIZE, size ); + curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal ); + curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_UPLOAD, 1 ); + curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "PATCH" ); +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKFUNCTION, lcl_seekStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKDATA, &isOriginal ); +#else + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal ); +#endif + + // If we know for sure that 100-Continue won't be accepted, + // don't even try with it to save one HTTP request. + if ( m_no100Continue ) + headers.push_back( "Expect:" ); + try + { + httpRunRequest( url, headers ); + response->getData( )->finish(); + } + catch ( const CurlException& ) + { + long status = getHttpStatus( ); + /** If we had a HTTP 417 response, this is likely to be due to some + HTTP 1.0 proxy / server not accepting the "Expect: 100-continue" + header. Try to disable this header and try again. + */ + if ( status == 417 && !m_no100Continue) + { + // Remember that we don't want 100-Continue for the future requests + m_no100Continue = true; + response = httpPutRequest( url, isBackup, headers ); + } + + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpPutRequest( url, isBackup, headers ); + m_refreshedToken = false; + } + catch (const CurlException&) + { + m_refreshedToken = false; + throw; + } + } + // Has tried but failed + if ( ( status != 417 || m_no100Continue ) && + ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw; + } + m_refreshedToken = false; + return response; +} + +libcmis::HttpResponsePtr HttpSession::httpPutRequest( string url, istream& is, vector< string > headers ) +{ + checkOAuth2( url ); + + // Duplicate istream in case we need to retry + string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) ); + + istringstream isOriginal( isStr ), isBackup( isStr ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + // Get the stream length + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + curl_easy_setopt( m_curlHandle, CURLOPT_INFILESIZE, size ); + curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal ); + curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_UPLOAD, 1 ); +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKFUNCTION, lcl_seekStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKDATA, &isOriginal ); +#else + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal ); +#endif + + // If we know for sure that 100-Continue won't be accepted, + // don't even try with it to save one HTTP request. + if ( m_no100Continue ) + headers.push_back( "Expect:" ); + try + { + httpRunRequest( url, headers ); + response->getData( )->finish(); + } + catch ( const CurlException& ) + { + long status = getHttpStatus( ); + /** If we had a HTTP 417 response, this is likely to be due to some + HTTP 1.0 proxy / server not accepting the "Expect: 100-continue" + header. Try to disable this header and try again. + */ + if ( status == 417 && !m_no100Continue) + { + // Remember that we don't want 100-Continue for the future requests + m_no100Continue = true; + response = httpPutRequest( url, isBackup, headers ); + } + + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpPutRequest( url, isBackup, headers ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + m_refreshedToken = false; + throw; + } + } + // Has tried but failed + if ( ( status != 417 || m_no100Continue ) && + ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw; + } + m_refreshedToken = false; + return response; +} + +libcmis::HttpResponsePtr HttpSession::httpPostRequest( const string& url, istream& is, + const string& contentType, bool redirect ) +{ + checkOAuth2( url ); + + // Duplicate istream in case we need to retry + string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) ); + + istringstream isOriginal( isStr ), isBackup( isStr ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + // Get the stream length + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + curl_easy_setopt( m_curlHandle, CURLOPT_POSTFIELDSIZE, size ); + curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal ); + curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_POST, 1 ); +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKFUNCTION, lcl_seekStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKDATA, &isOriginal ); +#else + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal ); +#endif + + vector< string > headers; + headers.push_back( string( "Content-Type:" ) + contentType ); + + // If we know for sure that 100-Continue won't be accepted, + // don't even try with it to save one HTTP request. + if ( m_no100Continue ) + headers.push_back( "Expect:" ); + try + { + httpRunRequest( url, headers, redirect ); + response->getData( )->finish(); + } + catch ( const CurlException& ) + { + + long status = getHttpStatus( ); + /** If we had a HTTP 417 response, this is likely to be due to some + HTTP 1.0 proxy / server not accepting the "Expect: 100-continue" + header. Try to disable this header and try again. + */ + if ( status == 417 && !m_no100Continue ) + { + // Remember that we don't want 100-Continue for the future requests + m_no100Continue = true; + response = httpPostRequest( url, isBackup, contentType, redirect ); + } + + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpPostRequest( url, isBackup, contentType, redirect ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + m_refreshedToken = false; + throw; + } + } + + // Has tried but failed + if ( ( status != 417 || m_no100Continue ) && + ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw; + } + m_refreshedToken = false; + + return response; +} + +void HttpSession::httpDeleteRequest( string url ) +{ + checkOAuth2( url ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "DELETE" ); + try + { + httpRunRequest( url ); + } + catch ( const CurlException& ) + { + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( getHttpStatus( ) == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + httpDeleteRequest( url ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + m_refreshedToken = false; + throw; + } + } + else throw; + } + m_refreshedToken = false; +} + +void HttpSession::checkCredentials( ) +{ + // Check that we have the complete credentials + libcmis::AuthProviderPtr authProvider = libcmis::SessionFactory::getAuthenticationProvider(); + if ( authProvider && !m_authProvided && ( m_username.empty() || m_password.empty() ) ) + { + m_authProvided = authProvider->authenticationQuery( m_username, m_password ); + if ( !m_authProvided ) + { + throw CurlException( "User cancelled authentication request" ); + } + } +} + +void HttpSession::httpRunRequest( string url, vector< string > headers, bool redirect ) +{ + // Redirect + curl_easy_setopt( m_curlHandle, CURLOPT_FOLLOWLOCATION, redirect); + + // Activate the cookie engine + curl_easy_setopt( m_curlHandle, CURLOPT_COOKIEFILE, "" ); + + // Grab something from the web + curl_easy_setopt( m_curlHandle, CURLOPT_URL, url.c_str() ); + + // Set the headers + struct curl_slist *headers_slist = NULL; + for ( vector< string >::iterator it = headers.begin( ); it != headers.end( ); ++it ) + headers_slist = curl_slist_append( headers_slist, it->c_str( ) ); + + // If we are using OAuth2, then add the proper header with token to authenticate + // Otherwise, just set the credentials normally using in libcurl options + if ( m_oauth2Handler != NULL && !m_oauth2Handler->getHttpHeader( ).empty() ) + { + headers_slist = curl_slist_append( headers_slist, + m_oauth2Handler->getHttpHeader( ).c_str( ) ); + } + else if ( !getUsername().empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPAUTH, m_authMethod ); +#if LIBCURL_VERSION_VALUE >= 0x071301 + curl_easy_setopt( m_curlHandle, CURLOPT_USERNAME, getUsername().c_str() ); + curl_easy_setopt( m_curlHandle, CURLOPT_PASSWORD, getPassword().c_str() ); +#else + string userpwd = getUsername() + ":" + getPassword(); + curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, userpwd.c_str( ) ); +#endif + } + + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, headers_slist ); + + // Set the proxy configuration if any + if ( !libcmis::SessionFactory::getProxy( ).empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, libcmis::SessionFactory::getProxy( ).c_str() ); +#if LIBCURL_VERSION_VALUE >= 0x071304 + curl_easy_setopt( m_curlHandle, CURLOPT_NOPROXY, libcmis::SessionFactory::getNoProxy( ).c_str() ); +#endif + const string& proxyUser = libcmis::SessionFactory::getProxyUser( ); + const string& proxyPass = libcmis::SessionFactory::getProxyPass( ); + if ( !proxyUser.empty( ) && !proxyPass.empty( ) ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERNAME, proxyUser.c_str( ) ); + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYPASSWORD, proxyPass.c_str( ) ); +#else + string userpwd = proxyUser + ":" + proxyPass; + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, userpwd.c_str( ) ); +#endif + } + } + + // Get some feedback when something wrong happens + char errBuff[CURL_ERROR_SIZE]; + errBuff[0] = 0; + curl_easy_setopt( m_curlHandle, CURLOPT_ERRORBUFFER, errBuff ); + + // We want to get the response even if there is an Http error + if ( !m_noHttpErrors ) + curl_easy_setopt( m_curlHandle, CURLOPT_FAILONERROR, 1 ); + + if ( m_verbose ) + curl_easy_setopt( m_curlHandle, CURLOPT_VERBOSE, 1 ); + + // We want to get the certificate infos in error cases +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_CERTINFO, 1 ); +#endif + + if ( m_noSSLCheck ) + { +#if LIBCURL_VERSION_VALUE >= 0x070801 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); +#endif +#if LIBCURL_VERSION_VALUE >= 0x070402 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); +#endif + } + + // Perform the query + CURLcode errCode = curl_easy_perform( m_curlHandle ); + + // Free the headers list + curl_slist_free_all( headers_slist ); + + // Process the response + bool isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + if ( CURLE_OK != errCode && !( m_noHttpErrors && isHttpError ) ) + { + long httpError = 0; + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + + bool errorFixed = false; +#if LIBCURL_VERSION_VALUE >= 0X071301 + // If we had a bad certificate, then try to get more details + if ( CURLE_SSL_CACERT == errCode ) + { + vector< string > certificates; + + // We somehow need to rerun the request to get the certificate + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); + errCode = curl_easy_perform( m_curlHandle ); + + union { + struct curl_slist *to_info; + struct curl_certinfo *to_certinfo; + } ptr; + + ptr.to_info = NULL; + + CURLcode res = curl_easy_getinfo(m_curlHandle, CURLINFO_CERTINFO, &ptr.to_info); + + if ( !res && ptr.to_info ) + { + // We need the first certificate in the chain only + if ( ptr.to_certinfo->num_of_certs > 0 ) + { + struct curl_slist *slist; + + string certStart( "Cert:" ); + for ( slist = ptr.to_certinfo->certinfo[0]; slist; slist = slist->next ) + { + string data( slist->data ); + size_t startPos = data.find( certStart ); + if ( startPos == 0 ) + { + startPos = certStart.length(); + data = data.substr( startPos ); + certificates.push_back( data ); + } + } + } + } + + if ( !certificates.empty() ) + { + libcmis::CertValidationHandlerPtr validationHandler = + libcmis::SessionFactory::getCertificateValidationHandler( ); + bool ignoreCert = validationHandler && validationHandler->validateCertificate( certificates ); + if ( ignoreCert ) + { + m_noSSLCheck = true; + + isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + errorFixed = ( CURLE_OK == errCode || ( m_noHttpErrors && isHttpError ) ); + if ( !errorFixed ) + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + } + else + { + throw CurlException( "Invalid SSL certificate" ); + } + } + } +#endif + + if ( !errorFixed ) + throw CurlException( string( errBuff ), errCode, url, httpError ); + } +} + + +void HttpSession::checkOAuth2( string url ) +try +{ + if ( m_oauth2Handler ) + { + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( url ) ); + if ( m_oauth2Handler->getAccessToken().empty() && !m_inOAuth2Authentication ) + oauth2Authenticate( ); + } +} +catch ( const libcmis::Exception& e ) +{ + throw CurlException( e.what( ) ); +} + +long HttpSession::getHttpStatus( ) +{ + long status = 0; + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &status ); + + return status; +} + +void HttpSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); +} + +void HttpSession::oauth2Authenticate( ) +{ + string authCode; + + const ScopeGuard<bool> inOauth2Guard(m_inOAuth2Authentication, true); + + try + { + // Try to get the authentication code using the given provider. + authCode = m_oauth2Handler->oauth2Authenticate( ); + + // If that didn't work, call the fallback provider from SessionFactory + if ( authCode.empty( ) ) + { + libcmis::OAuth2AuthCodeProvider fallbackProvider = libcmis::SessionFactory::getOAuth2AuthCodeProvider( ); + if ( fallbackProvider != NULL ) + { + unique_ptr< char, void (*)( void * ) > code{ + fallbackProvider( m_oauth2Handler->getAuthURL().c_str(), getUsername().c_str(), getPassword().c_str() ), + free }; + if ( code ) + authCode = string( code.get() ); + } + } + } + catch ( const CurlException& e ) + { + // Thrown by getUsername() and getPassword() if user cancels the credentials request + throw e.getCmisException( ); + } + + // If still no auth code, then raise an exception + if ( authCode.empty( ) ) + throw libcmis::Exception( "Couldn't get OAuth authentication code", "permissionDenied" ); + + m_oauth2Handler->fetchTokens( string( authCode ) ); +} + +void HttpSession::setNoSSLCertificateCheck( bool noCheck ) +{ + m_noSSLCheck = noCheck; +} + +string HttpSession::getRefreshToken( ) +{ + string refreshToken; + if ( m_oauth2Handler ) + refreshToken = m_oauth2Handler->getRefreshToken( ); + return refreshToken; +} + +void HttpSession::oauth2Refresh( ) +try +{ + const ScopeGuard<bool> inOauth2Guard(m_inOAuth2Authentication, true); + m_oauth2Handler->refresh( ); +} +catch ( const libcmis::Exception& e ) +{ + throw CurlException( e.what() ); +} + +void HttpSession::initProtocols( ) +{ +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + auto const protocols = "https,http"; + curl_easy_setopt(m_curlHandle, CURLOPT_PROTOCOLS_STR, protocols); + curl_easy_setopt(m_curlHandle, CURLOPT_REDIR_PROTOCOLS_STR, protocols); +#else + const unsigned long protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS; + curl_easy_setopt(m_curlHandle, CURLOPT_PROTOCOLS, protocols); + curl_easy_setopt(m_curlHandle, CURLOPT_REDIR_PROTOCOLS, protocols); +#endif + if (m_CurlInitProtocolsFunction) + { + (*m_CurlInitProtocolsFunction)(m_curlHandle); + } +} + +const char* CurlException::what( ) const noexcept +{ + if ( !isCancelled( ) ) + { + stringstream buf; + buf << "CURL error - " << ( unsigned int ) m_code << ": "; + buf << m_message; + m_errorMessage = buf.str( ); + + return m_errorMessage.c_str( ); + } + + return m_message.c_str( ); +} + +libcmis::Exception CurlException::getCmisException( ) const +{ + string msg; + string type( "runtime" ); + + switch ( m_httpStatus ) + { + case 400: + msg = string( what() ) + string( ": " ) + m_url; + type = "invalidArgument"; + break; + case 401: + msg = "Authentication failure"; + type = "permissionDenied"; + break; + case 403: + msg = "Invalid credentials"; + type = "permissionDenied"; + break; + case 404: + msg = "Invalid URL: " + m_url; + type = "objectNotFound"; + break; + case 405: + msg = string( what() ) + string( ": " ) + m_url; + type = "notSupported"; + break; + case 409: + msg = "Editing conflict error"; + type = "updateConflict"; + break; + default: + msg = what(); + if ( !isCancelled( ) ) + msg += ": " + m_url; + else + type = "permissionDenied"; + break; + } + + return libcmis::Exception( msg, type ); +} diff --git a/src/libcmis/http-session.hxx b/src/libcmis/http-session.hxx new file mode 100644 index 0000000..34223b2 --- /dev/null +++ b/src/libcmis/http-session.hxx @@ -0,0 +1,179 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _HTTP_SESSION_HXX_ +#define _HTTP_SESSION_HXX_ + +#include <istream> +#include <sstream> +#include <vector> +#include <string> + +#include <curl/curl.h> +#include <libxml/xmlstring.h> +#include <libxml/xpath.h> + +#include <libcmis/exception.hxx> +#include <libcmis/oauth2-data.hxx> +#include <libcmis/xml-utils.hxx> + +class OAuth2Handler; + +namespace libcmis { + typedef void(*CurlInitProtocolsFunction)(CURL *); +} + +class CurlException : public std::exception +{ + private: + std::string m_message; + CURLcode m_code; + std::string m_url; + long m_httpStatus; + + bool m_cancelled; + + mutable std::string m_errorMessage; + + public: + CurlException( std::string message, CURLcode code, std::string url, long httpStatus ) : + exception( ), + m_message( message ), + m_code( code ), + m_url( url ), + m_httpStatus( httpStatus ), + m_cancelled( false ), + m_errorMessage( ) + { + } + + CurlException( std::string message ) : + exception( ), + m_message( message ), + m_code( CURLE_OK ), + m_url( ), + m_httpStatus( 0 ), + m_cancelled( true ), + m_errorMessage( ) + { + } + + ~CurlException( ) noexcept { } + virtual const char* what( ) const noexcept; + + CURLcode getErrorCode( ) const { return m_code; } + std::string getErrorMessage( ) const { return m_message; } + bool isCancelled( ) const { return m_cancelled; } + long getHttpStatus( ) const { return m_httpStatus; } + + libcmis::Exception getCmisException ( ) const; +}; + +class HttpSession +{ + protected: + CURL* m_curlHandle; + libcmis::CurlInitProtocolsFunction m_CurlInitProtocolsFunction = nullptr; + private: + bool m_no100Continue; + protected: + OAuth2Handler* m_oauth2Handler; + std::string m_username; + std::string m_password; + bool m_authProvided; + + bool m_verbose; + bool m_noHttpErrors; + bool m_noSSLCheck; + bool m_refreshedToken; + bool m_inOAuth2Authentication; + unsigned long m_authMethod; + public: + HttpSession( std::string username, std::string password, + bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), + bool verbose = false, + libcmis::CurlInitProtocolsFunction = nullptr); + + HttpSession( const HttpSession& copy ); + virtual ~HttpSession( ); + + HttpSession& operator=( const HttpSession& copy ); + + std::string& getUsername( ); + + std::string& getPassword( ); + + /** Don't throw the HTTP errors as CurlExceptions. + */ + void setNoHttpErrors( bool noHttpErrors ) { m_noHttpErrors = noHttpErrors; } + + + /** Set the OAuth2 data and get the access / refresh tokens. + */ + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + libcmis::HttpResponsePtr httpGetRequest( std::string url ); + libcmis::HttpResponsePtr httpPatchRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ); + libcmis::HttpResponsePtr httpPutRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ); + libcmis::HttpResponsePtr httpPostRequest( const std::string& url, + std::istream& is, + const std::string& contentType, + bool redirect = true ); + void httpDeleteRequest( std::string url ); + + long getHttpStatus( ); + + void setNoSSLCertificateCheck( bool noCheck ); + + virtual std::string getRefreshToken( ); + + protected: + HttpSession( ); + + /** Helper function actually handling the oauth2 process. + This function is provided for BaseSession to customize + the OAuth2 login parser. + */ + void oauth2Authenticate( ); + void setAuthMethod( unsigned long authMethod ) { m_authMethod = authMethod; } + virtual void httpRunRequest( std::string url, + std::vector< std::string > headers = std::vector< std::string > ( ), + bool redirect = true ); + + private: + void checkCredentials( ); + void checkOAuth2( std::string url ); + void oauth2Refresh( ); + void initProtocols( ); +}; + +#endif diff --git a/src/libcmis/json-utils.cxx b/src/libcmis/json-utils.cxx new file mode 100644 index 0000000..52b036a --- /dev/null +++ b/src/libcmis/json-utils.cxx @@ -0,0 +1,306 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "json-utils.hxx" + +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/json_parser.hpp> + +#include <libcmis/exception.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace libcmis; +using namespace boost; +using boost::property_tree::ptree; +using boost::property_tree::json_parser::read_json; +using boost::property_tree::json_parser::write_json; + +Json::Json( ) : + m_tJson( ptree( ) ), + m_type( json_object ) +{ +} + +Json::Json( const char *str ) : + m_tJson( ptree( ) ), + m_type( json_string ) +{ + m_tJson.put_value( str ); + m_type = parseType( ); +} + +Json::Json( ptree tJson ) : + m_tJson( tJson ), + m_type( json_object ) +{ + m_type = parseType( ); +} + +Json::Json( const PropertyPtr& property ): + m_tJson( ), + m_type( json_object ) +{ + string str = property->toString( ); + m_tJson.put("", str ); +} + +Json::Json( const PropertyPtrMap& properties ) : + m_tJson( ptree( ) ), + m_type( json_array ) +{ + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + string key = it->first; + string value = it->second->toString( ); + m_tJson.put( key, value ); + } +} + +Json::Json( const Json& copy ) : + m_tJson( copy.m_tJson ), + m_type( copy.m_type ) +{ +} + +Json::Json( const JsonObject& obj ) : + m_tJson( ptree( ) ), + m_type( json_array ) +{ + for ( JsonObject::const_iterator i = obj.begin() ; i != obj.end() ; ++i ) + add( i->first, i->second ) ; +} + +Json::Json( const JsonVector& arr ) : + m_tJson( ptree( ) ), + m_type( json_array ) +{ + for ( std::vector<Json>::const_iterator i = arr.begin(); i != arr.end(); ++i ) + add( *i ) ; +} + + +Json::~Json( ) +{ +} + +Json& Json::operator=( const Json& rhs ) +{ + if ( this != &rhs ) + { + m_tJson = rhs.m_tJson; + m_type = rhs.m_type; + } + return *this ; +} + +void Json::swap( Json& rhs ) +{ + std::swap( m_tJson, rhs.m_tJson ); + std::swap( m_type, rhs.m_type ); +} + +Json Json::operator[]( string key ) const +{ + ptree tJson; + try + { + tJson = m_tJson.get_child( key ); + } + catch ( boost::exception const& ) + { + return Json( "" ); + } + + Json childJson( tJson ); + + return childJson ; +} + +void Json::add( const std::string& key, const Json& json ) +{ + try + { + m_tJson.add_child( key, json.getTree( ) ); + } + catch ( boost::exception const& ) + { + throw libcmis::Exception( "Couldn't add Json object" ); + } +} + +Json::JsonVector Json::getList() +{ + JsonVector list; + for ( const auto &v: m_tJson.get_child("") ) + { + list.push_back( Json( v.second ) ); + } + return list ; +} + +void Json::add( const Json& json ) +{ + try + { + m_tJson.push_back( ptree::value_type( "", json.getTree( )) ); + } + catch ( boost::exception const& ) + { + throw libcmis::Exception( "Couldn't add Json object" ); + } +} + +Json Json::parse( const string& str ) +{ + ptree pTree; + std::stringstream ss( str ); + if ( ss.good( ) ) + { + try + { + property_tree::json_parser::read_json( ss, pTree ); + } + catch ( boost::exception const& ) + { + return Json( str.c_str( ) ); + } + } + return Json( pTree ); +} + +Json::JsonObject Json::getObjects( ) +{ + JsonObject objs; + for ( const auto &v: m_tJson.get_child("") ) + { + Json jsonValue( v.second ); + objs.insert( JsonObject::value_type(v.first, jsonValue ) ); + } + return objs ; +} + +Json::Type Json::parseType( ) +{ + Type type = json_string; + string str = toString( ); + if ( str.empty( ) ) + return type; + try + { + boost::posix_time::ptime time = libcmis::parseDateTime( str ); + if ( !time.is_not_a_date_time( ) ) + return json_datetime; + } + catch (...) + { + // Try other types + } + Type backupType = type; + type = json_bool; + try + { + parseBool( str ); + } + catch (...) + { + type = backupType; + } + if ( type != json_bool ) + { + if ( str.find('.') == string::npos ) + { + backupType = type; + type = json_int; + try + { + parseInteger( str ); + } + catch(...) + { + type = backupType; + } + } + else + { + backupType = type; + type = json_double; + try + { + parseDouble( str ); + } + catch(...) + { + type = backupType; + } + } + } + return type; +} + +Json::Type Json::getDataType( ) const +{ + return m_type; +} + +string Json::getStrType( ) const +{ + switch ( m_type ) + { + case json_null: return "json_null"; + case json_bool: return "json_bool"; + case json_int: return "json_int"; + case json_double: return "json_double"; + case json_string: return "json_string"; + case json_datetime: return "json_datetime"; + case json_object: return "json_object"; + case json_array: return "json_array"; + } + return "json_string"; +} + +string Json::toString( ) const +{ + string str; + try + { + stringstream ss; + write_json( ss, m_tJson ); + str = ss.str( ); + } + catch ( boost::exception const& ) + { + str = m_tJson.get_value<string>( ); + } + // empty json + if ( str == "{\n}\n" ) str = ""; + return str; +} + diff --git a/src/libcmis/json-utils.hxx b/src/libcmis/json-utils.hxx new file mode 100644 index 0000000..ef3ae55 --- /dev/null +++ b/src/libcmis/json-utils.hxx @@ -0,0 +1,87 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _JSON_UTILS_HXX_ +#define _JSON_UTILS_HXX_ + +#include <string> +#include <map> +#include <vector> + +#include <boost/property_tree/ptree.hpp> + +#include <libcmis/exception.hxx> +#include <libcmis/property.hxx> + +class Json +{ + public : + typedef std::map< std::string, Json > JsonObject ; + typedef std::vector< Json > JsonVector ; + enum Type { json_null, json_bool, json_double, json_int, json_object, + json_array, json_string, json_datetime } ; + + Json( ); + Json( const Json& copy ); + Json( const char *str ); + Json( const libcmis::PropertyPtr& property ); + Json( const libcmis::PropertyPtrMap& properties ); + Json( const JsonVector& arr ); + Json( const JsonObject& obj ); + + ~Json( ) ; + + Json operator[]( std::string key ) const; + + Json& operator=( const Json& rhs ) ; + + void swap( Json& other ) ; + + void add( const Json& json); + + void add( const std::string& key, const Json& json); + + static Json parse( const std::string& str ); + + std::string toString( ) const; + Type getDataType( ) const ; + std::string getStrType( ) const ; + + JsonObject getObjects(); + JsonVector getList(); + + boost::property_tree::ptree getTree( ) const{ return m_tJson; } + private : + Json( boost::property_tree::ptree tJson ) ; + boost::property_tree::ptree m_tJson ; + Type m_type; + Type parseType( ); +} ; + +#endif /* _JSON_UTILS_HXX_ */ + diff --git a/src/libcmis/oauth2-data.cxx b/src/libcmis/oauth2-data.cxx new file mode 100644 index 0000000..a56251f --- /dev/null +++ b/src/libcmis/oauth2-data.cxx @@ -0,0 +1,95 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/oauth2-data.hxx> + +using namespace std; + +namespace libcmis +{ + OAuth2Data::OAuth2Data( ) : + m_authUrl( ), + m_tokenUrl( ), + m_clientId( ), + m_clientSecret( ), + m_scope( ), + m_redirectUri( ) + { + } + + OAuth2Data::OAuth2Data( const string& authUrl, const string& tokenUrl, + const string& scope, const string& redirectUri, + const string& clientId, const string& clientSecret ): + m_authUrl( authUrl ), + m_tokenUrl( tokenUrl ), + m_clientId( clientId ), + m_clientSecret( clientSecret ), + m_scope( scope ), + m_redirectUri( redirectUri ) + { + } + + OAuth2Data::OAuth2Data( const OAuth2Data& copy ) : + m_authUrl( copy.m_authUrl ), + m_tokenUrl( copy.m_tokenUrl ), + m_clientId( copy.m_clientId ), + m_clientSecret( copy.m_clientSecret ), + m_scope( copy.m_scope ), + m_redirectUri( copy.m_redirectUri ) + { + } + + OAuth2Data::~OAuth2Data( ) + { + } + + OAuth2Data& OAuth2Data::operator=( const OAuth2Data& copy ) + { + if ( this != © ) + { + m_authUrl = copy.m_authUrl; + m_tokenUrl = copy.m_tokenUrl; + m_clientId = copy.m_clientId; + m_clientSecret = copy.m_clientSecret; + m_scope = copy.m_scope; + m_redirectUri = copy.m_redirectUri; + } + + return *this; + } + + bool OAuth2Data::isComplete() + { + return !m_authUrl.empty() && + !m_tokenUrl.empty() && + !m_clientId.empty() && + !m_clientSecret.empty() && + !m_scope.empty() && + !m_redirectUri.empty(); + } +} diff --git a/src/libcmis/oauth2-handler.cxx b/src/libcmis/oauth2-handler.cxx new file mode 100644 index 0000000..a340b80 --- /dev/null +++ b/src/libcmis/oauth2-handler.cxx @@ -0,0 +1,196 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <boost/algorithm/string.hpp> + +#include "oauth2-handler.hxx" + +#include <libcmis/session-factory.hxx> +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" +#include "oauth2-providers.hxx" + +using namespace std; + +OAuth2Handler::OAuth2Handler(HttpSession* session, libcmis::OAuth2DataPtr data) : + m_session( session ), + m_data( data ), + m_access( ), + m_refresh( ), + m_oauth2Parser( ) +{ + if ( !m_data ) + m_data.reset( new libcmis::OAuth2Data() ); + +} + +OAuth2Handler::OAuth2Handler( const OAuth2Handler& copy ) : + m_session( copy.m_session ), + m_data( copy.m_data ), + m_access( copy.m_access ), + m_refresh( copy.m_refresh ), + m_oauth2Parser( copy.m_oauth2Parser ) +{ +} + +OAuth2Handler::OAuth2Handler( ): + m_session( NULL ), + m_data( ), + m_access( ), + m_refresh( ), + m_oauth2Parser( ) +{ + m_data.reset( new libcmis::OAuth2Data() ); +} + +OAuth2Handler& OAuth2Handler::operator=( const OAuth2Handler& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_data = copy.m_data; + m_access = copy.m_access; + m_refresh = copy.m_refresh; + m_oauth2Parser = copy.m_oauth2Parser; + } + + return *this; +} + +OAuth2Handler::~OAuth2Handler( ) +{ + +} + +void OAuth2Handler::fetchTokens( string authCode ) +{ + string post = + "code=" + authCode + + "&client_id=" + m_data->getClientId() + + "&redirect_uri=" + m_data->getRedirectUri() + + "&grant_type=authorization_code" ; + if(boost::starts_with(m_data->getTokenUrl(), "https://oauth2.googleapis.com/")) + post += "&client_secret=" + m_data->getClientSecret(); + else + post += "&scope=" + libcmis::escape( m_data->getScope() ); + + istringstream is( post ); + + libcmis::HttpResponsePtr resp; + + try + { + resp = m_session->httpPostRequest ( m_data->getTokenUrl(), is, + "application/x-www-form-urlencoded" ); + } + catch ( const CurlException& e) + { + throw libcmis::Exception( + "Couldn't get tokens from the authorization code "); + } + + Json jresp = Json::parse( resp->getStream( )->str( ) ); + m_access = jresp[ "access_token" ].toString( ); + m_refresh = jresp[ "refresh_token" ].toString( ); +} + +void OAuth2Handler::refresh( ) +{ + m_access = string( ); + string post = + "refresh_token=" + m_refresh + + "&client_id=" + m_data->getClientId() + + "&grant_type=refresh_token" ; + if(boost::starts_with(m_data->getTokenUrl(), "https://oauth2.googleapis.com/")) + post += "&client_secret=" + m_data->getClientSecret(); + + istringstream is( post ); + libcmis::HttpResponsePtr resp; + try + { + resp = m_session->httpPostRequest( m_data->getTokenUrl( ), is, + "application/x-www-form-urlencoded" ); + } + catch (const CurlException& e ) + { + throw libcmis::Exception( "Couldn't refresh token "); + } + + Json jresp = Json::parse( resp->getStream( )->str( ) ); + m_access = jresp[ "access_token" ].toString(); +} + +string OAuth2Handler::getAuthURL( ) +{ + return m_data->getAuthUrl() + + "?scope=" + libcmis::escape( m_data->getScope( ) ) + + "&redirect_uri="+ m_data->getRedirectUri( ) + + "&response_type=code" + + "&client_id=" + m_data->getClientId( ); +} + +string OAuth2Handler::getAccessToken( ) +{ + return m_access; +} + +string OAuth2Handler::getRefreshToken( ) +{ + return m_refresh; +} + +void OAuth2Handler::setRefreshToken( string refreshToken ) +{ + m_refresh = refreshToken; +} + +string OAuth2Handler::getHttpHeader( ) +{ + string header; + if ( !m_access.empty() ) + header = "Authorization: Bearer " + m_access ; + return header; +} + +string OAuth2Handler::oauth2Authenticate( ) +{ + string code; + if ( m_oauth2Parser ) + { + code = m_oauth2Parser( m_session, getAuthURL( ), + m_session->getUsername( ), + m_session->getPassword( ) ); + } + return code; +} + +void OAuth2Handler::setOAuth2Parser( OAuth2Parser parser ) +{ + m_oauth2Parser = parser; +} diff --git a/src/libcmis/oauth2-handler.hxx b/src/libcmis/oauth2-handler.hxx new file mode 100644 index 0000000..bb9a394 --- /dev/null +++ b/src/libcmis/oauth2-handler.hxx @@ -0,0 +1,97 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _OAUTH2_HANDLER_HXX_ +#define _OAUTH2_HANDLER_HXX_ + +#include <string> +#include "http-session.hxx" +#include "oauth2-providers.hxx" + +namespace libcmis +{ + class OAuth2Data; +} + +class OAuth2Handler +{ + private: + HttpSession* m_session; + libcmis::OAuth2DataPtr m_data; + + std::string m_access; + std::string m_refresh; + + OAuth2Parser m_oauth2Parser; + + public: + + OAuth2Handler( HttpSession* session, libcmis::OAuth2DataPtr data ); + + OAuth2Handler( const OAuth2Handler& copy ); + ~OAuth2Handler( ); + + OAuth2Handler& operator=( const OAuth2Handler& copy ); + + std::string getAuthURL(); + + std::string getAccessToken( ) ; + std::string getRefreshToken( ) ; + void setRefreshToken( std::string refreshToken ) ; + + // adding HTTP auth header + std::string getHttpHeader( ) ; + + /** Exchange the previously obtained authentication code with the + access/refresh tokens. + + \param authCode + the authentication code normally obtained from authenticate + method. + */ + void fetchTokens( std::string authCode ); + void refresh( ); + + /** Get the authentication code given credentials. + + This method should be overridden to parse the authentication URL response, + authenticate using the form and get the token to avoid asking the user + to launch a web browser and do it himself. + + \return + the authentication code to transform into access/refresh tokens. + If no code is found, an empty string is returned. + */ + std::string oauth2Authenticate( ); + + void setOAuth2Parser( OAuth2Parser parser ); + + protected: + OAuth2Handler( ); +}; + +#endif /* _OAUTH2_HANDLER_HXX_ */ diff --git a/src/libcmis/oauth2-providers.cxx b/src/libcmis/oauth2-providers.cxx new file mode 100644 index 0000000..1c8d3cc --- /dev/null +++ b/src/libcmis/oauth2-providers.cxx @@ -0,0 +1,246 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "oauth2-providers.hxx" + +#include <memory> + +#include <boost/algorithm/string.hpp> + +#include <libxml/HTMLparser.h> +#include <libxml/xmlreader.h> + +#include <libcmis/session-factory.hxx> + +#include "http-session.hxx" + +#define CHALLENGE_PAGE_ACTION "/signin" +#define CHALLENGE_PAGE_ACTION_LEN sizeof( CHALLENGE_PAGE_ACTION ) - 1 +#define PIN_FORM_ACTION "/signin/challenge/ipp" +#define PIN_FORM_ACTION_LEN sizeof( PIN_FORM_ACTION ) - 1 +#define PIN_INPUT_NAME "Pin" + +using namespace std; + +string OAuth2Providers::OAuth2Dummy( HttpSession* /*session*/, const string& /*authUrl*/, + const string& /*username*/, const string& /*password*/ ) +{ + return string( ); +} + +string OAuth2Providers::OAuth2Alfresco( HttpSession* session, const string& authUrl, + const string& username, const string& password ) +{ + static const string CONTENT_TYPE( "application/x-www-form-urlencoded" ); + + // Log in + string res; + try + { + res = session->httpGetRequest( authUrl )->getStream( )->str( ); + } + catch ( const CurlException& ) + { + return string( ); + } + + string loginPost, loginLink; + + if ( !parseResponse( res.c_str( ), loginPost, loginLink ) ) + return string( ); + + loginPost += "username="; + loginPost += string( username ); + loginPost += "&password="; + loginPost += string( password ); + loginPost += "&action=Grant"; + + istringstream loginIs( loginPost ); + + libcmis::HttpResponsePtr resp; + + // Get the code + try + { + // Alfresco code is in the redirect link + resp = session->httpPostRequest( loginLink, loginIs, CONTENT_TYPE, false ); + } + catch ( const CurlException& ) + { + return string( ); + } + + string header = resp->getHeaders()["Location"]; + string code; + auto start = header.find("code="); + if ( start != string::npos ) + { + start += 5; + auto end = header.find("&"); + if ( end != string::npos ) + code = header.substr( start, end - start ); + else code = header.substr( start ); + } + + return code; +} + +OAuth2Parser OAuth2Providers::getOAuth2Parser( const std::string& url ) +{ + if ( boost::starts_with( url, "https://api.alfresco.com/" ) ) + // For Alfresco in the cloud, only match the hostname as there can be several + // binding URLs created with it. + return OAuth2Alfresco; + + return OAuth2Dummy; +} + +int OAuth2Providers::parseResponse ( const char* response, string& post, string& link ) +{ + xmlDoc *doc = htmlReadDoc ( BAD_CAST( response ), NULL, 0, + HTML_PARSE_NOWARNING | HTML_PARSE_RECOVER | HTML_PARSE_NOERROR ); + if ( doc == NULL ) return 0; + xmlTextReaderPtr reader = xmlReaderWalker( doc ); + if ( reader == NULL ) return 0; + + bool readInputField = false; + bool bIsRightForm = false; + bool bHasPinField = false; + + while ( true ) + { + // Go to the next node, quit if not found + if ( xmlTextReaderRead ( reader ) != 1) break; + xmlChar* nodeName = xmlTextReaderName ( reader ); + if ( nodeName == NULL ) continue; + // Find the redirect link + if ( xmlStrEqual( nodeName, BAD_CAST( "form" ) ) ) + { + // 2FA: Don't add fields form other forms not having pin field + if ( bIsRightForm && !bHasPinField ) + post = string( "" ); + if ( bIsRightForm && bHasPinField ) + break; + + xmlChar* action = xmlTextReaderGetAttribute( reader, + BAD_CAST( "action" )); + + // GDrive pin code page contains many forms. + // We have to parse only the form with pin field. + if ( action != NULL ) + { + bool bChallengePage = ( strncmp( (char*)action, + CHALLENGE_PAGE_ACTION, + CHALLENGE_PAGE_ACTION_LEN ) == 0 ); + bIsRightForm = ( strncmp( (char*)action, + PIN_FORM_ACTION, + PIN_FORM_ACTION_LEN ) == 0 ); + if ( ( xmlStrlen( action ) > 0 ) + && ( ( bChallengePage && bIsRightForm ) || !bChallengePage ) ) + { + link = string ( (char*) action); + readInputField = true; + } + else + readInputField = false; + xmlFree (action); + } + } + // Find input values + if ( readInputField && !xmlStrcmp( nodeName, BAD_CAST( "input" ) ) ) + { + xmlChar* name = xmlTextReaderGetAttribute( reader, + BAD_CAST( "name" )); + xmlChar* value = xmlTextReaderGetAttribute( reader, + BAD_CAST( "value" )); + if ( name != NULL && strcmp( (char*)name, PIN_INPUT_NAME ) == 0 ) + bHasPinField = true; + if ( ( name != NULL ) && ( value!= NULL ) ) + { + if ( ( xmlStrlen( name ) > 0) && ( xmlStrlen( value ) > 0) ) + { + post += libcmis::escape( ( char * ) name ); + post += string ( "=" ); + post += libcmis::escape( ( char * ) value ); + post += string ( "&" ); + } + } + xmlFree( name ); + xmlFree( value ); + } + xmlFree( nodeName ); + } + xmlFreeTextReader( reader ); + xmlFreeDoc( doc ); + if ( link.empty( ) || post.empty () ) + return 0; + return 1; +} + +string OAuth2Providers::parseCode( const char* response ) +{ + string authCode; + xmlDoc *doc = htmlReadDoc ( BAD_CAST( response ), NULL, 0, + HTML_PARSE_NOWARNING | HTML_PARSE_RECOVER | HTML_PARSE_NOERROR ); + if ( doc == NULL ) return authCode; + xmlTextReaderPtr reader = xmlReaderWalker( doc ); + if ( reader == NULL ) return authCode; + + while ( true ) + { + // Go to the next node, quit if not found + if ( xmlTextReaderRead ( reader ) != 1) break; + xmlChar* nodeName = xmlTextReaderName ( reader ); + if ( nodeName == NULL ) continue; + // Find the code + if ( xmlStrEqual( nodeName, BAD_CAST ( "input" ) ) ) + { + xmlChar* id = xmlTextReaderGetAttribute( reader, BAD_CAST( "id" )); + if ( id != NULL ) + { + if ( xmlStrEqual( id, BAD_CAST ( "code" ) ) ) + { + xmlChar* code = xmlTextReaderGetAttribute( + reader, BAD_CAST("value") ); + if ( code!= NULL ) + { + authCode = string ( (char*) code ); + xmlFree( code ); + } + } + xmlFree ( id ); + } + } + xmlFree( nodeName ); + } + xmlFreeTextReader( reader ); + xmlFreeDoc( doc ); + + return authCode; +} + diff --git a/src/libcmis/oauth2-providers.hxx b/src/libcmis/oauth2-providers.hxx new file mode 100644 index 0000000..eaeb1c4 --- /dev/null +++ b/src/libcmis/oauth2-providers.hxx @@ -0,0 +1,63 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _OAUTH2_PROVIDERS_HXX_ +#define _OAUTH2_PROVIDERS_HXX_ + +#include <string> + +class HttpSession; + +typedef std::string ( *OAuth2Parser ) ( HttpSession* session, const std::string& authUrl, + const std::string& username, const std::string& password ); + +class OAuth2Providers +{ + public : + static std::string OAuth2Dummy( HttpSession* session, const std::string& authUrl, + const std::string& username, const std::string& password ); + static std::string OAuth2Alfresco( HttpSession* session, const std::string& authUrl, + const std::string& username, const std::string& password ); + + static OAuth2Parser getOAuth2Parser( const std::string& baseUrl ); + + /* + * Parse the authorization code from the response page + * in the input tag, with id = code + */ + static std::string parseCode ( const char* response ); + + /* + * Parse input values and redirect link from the response page + */ + static int parseResponse ( const char* response, + std::string& post, + std::string& link ); +}; + +#endif /* _OAUTH2_PROVIDERS_HXX_ */ diff --git a/src/libcmis/object-type.cxx b/src/libcmis/object-type.cxx new file mode 100644 index 0000000..868ec56 --- /dev/null +++ b/src/libcmis/object-type.cxx @@ -0,0 +1,368 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. +*/ + +#include <libcmis/object-type.hxx> + +#include <libcmis/xml-utils.hxx> +#include <libcmis/property.hxx> + +using namespace std; + +namespace libcmis +{ + ObjectType::ObjectType( ) : + m_refreshTimestamp( 0 ), + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_description( ), + m_parentTypeId( ), + m_baseTypeId( ), + m_creatable( false ), + m_fileable( false ), + m_queryable( false ), + m_fulltextIndexed( false ), + m_includedInSupertypeQuery( false ), + m_controllablePolicy( false ), + m_controllableAcl( false ), + m_versionable( false ), + m_contentStreamAllowed( libcmis::ObjectType::Allowed ), + m_propertiesTypes( ) + { + } + + ObjectType::ObjectType( xmlNodePtr node ) : + m_refreshTimestamp( 0 ), + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_description( ), + m_parentTypeId( ), + m_baseTypeId( ), + m_creatable( false ), + m_fileable( false ), + m_queryable( false ), + m_fulltextIndexed( false ), + m_includedInSupertypeQuery( false ), + m_controllablePolicy( false ), + m_controllableAcl( false ), + m_versionable( false ), + m_contentStreamAllowed( libcmis::ObjectType::Allowed ), + m_propertiesTypes( ) + { + initializeFromNode( node ); + } + + ObjectType::ObjectType( const ObjectType& copy ) : + m_refreshTimestamp( copy.m_refreshTimestamp ), + m_id( copy.m_id ), + m_localName( copy.m_localName ), + m_localNamespace( copy.m_localNamespace ), + m_displayName( copy.m_displayName ), + m_queryName( copy.m_queryName ), + m_description( copy.m_description ), + m_parentTypeId( copy.m_parentTypeId ), + m_baseTypeId( copy.m_baseTypeId ), + m_creatable( copy.m_creatable ), + m_fileable( copy.m_fileable ), + m_queryable( copy.m_queryable ), + m_fulltextIndexed( copy.m_fulltextIndexed ), + m_includedInSupertypeQuery( copy.m_includedInSupertypeQuery ), + m_controllablePolicy( copy.m_controllablePolicy ), + m_controllableAcl( copy.m_controllableAcl ), + m_versionable( copy.m_versionable ), + m_contentStreamAllowed( copy.m_contentStreamAllowed ), + m_propertiesTypes( copy.m_propertiesTypes ) + { + } + + ObjectType& ObjectType::operator=( const ObjectType& copy ) + { + if ( this != © ) + { + m_refreshTimestamp = copy.m_refreshTimestamp; + m_id = copy.m_id; + m_localName = copy.m_localName; + m_localNamespace = copy.m_localNamespace; + m_displayName = copy.m_displayName; + m_queryName = copy.m_queryName; + m_description = copy.m_description; + m_parentTypeId = copy.m_parentTypeId; + m_baseTypeId = copy.m_baseTypeId; + m_creatable = copy.m_creatable; + m_fileable = copy.m_fileable; + m_queryable = copy.m_queryable; + m_fulltextIndexed = copy.m_fulltextIndexed; + m_includedInSupertypeQuery = copy.m_includedInSupertypeQuery; + m_controllablePolicy = copy.m_controllablePolicy; + m_controllableAcl = copy.m_controllableAcl; + m_versionable = copy.m_versionable; + m_contentStreamAllowed = copy.m_contentStreamAllowed; + m_propertiesTypes = copy.m_propertiesTypes; + } + return *this; + } + + time_t ObjectType::getRefreshTimestamp( ) const + { + return m_refreshTimestamp; + } + + string ObjectType::getId( ) const + { + return m_id; + } + + string ObjectType::getLocalName( ) const + { + return m_localName; + } + + string ObjectType::getLocalNamespace( ) const + { + return m_localNamespace; + } + + string ObjectType::getDisplayName( ) const + { + return m_displayName; + } + + string ObjectType::getQueryName( ) const + { + return m_queryName; + } + + string ObjectType::getDescription( ) const + { + return m_description; + } + + string ObjectType::getParentTypeId( ) const + { + return m_parentTypeId; + } + + string ObjectType::getBaseTypeId( ) const + { + return m_baseTypeId; + } + + bool ObjectType::isCreatable( ) const + { + return m_creatable; + } + + bool ObjectType::isFileable( ) const + { + return m_fileable; + } + + bool ObjectType::isQueryable( ) const + { + return m_queryable; + } + + bool ObjectType::isFulltextIndexed( ) const + { + return m_fulltextIndexed; + } + + bool ObjectType::isIncludedInSupertypeQuery( ) const + { + return m_includedInSupertypeQuery; + } + + bool ObjectType::isControllablePolicy( ) const + { + return m_controllablePolicy; + } + + bool ObjectType::isControllableACL( ) const + { + return m_controllableAcl; + } + + bool ObjectType::isVersionable( ) const + { + return m_versionable; + } + + ObjectType::ContentStreamAllowed ObjectType::getContentStreamAllowed( ) const + { + return m_contentStreamAllowed; + } + + map< string, PropertyTypePtr >& ObjectType::getPropertiesTypes( ) + { + return m_propertiesTypes; + } + + + void ObjectType::initializeFromNode( xmlNodePtr typeNode ) + { + if ( typeNode != NULL ) + { + for ( xmlNodePtr child = typeNode->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( const char * ) content, xmlStrlen( content ) ); + + if ( xmlStrEqual( child->name, BAD_CAST( "id" ) ) ) + m_id = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "localName" ) ) ) + m_localName = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "localNamespace" ) ) ) + m_localNamespace = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "displayName" ) ) ) + m_displayName = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "queryName" ) ) ) + m_queryName = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "description" ) ) ) + m_description = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "baseId" ) ) ) + m_baseTypeId = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "parentId" ) ) ) + m_parentTypeId = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "creatable" ) ) ) + m_creatable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "fileable" ) ) ) + m_fileable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "queryable" ) ) ) + m_queryable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "fulltextIndexed" ) ) ) + m_fulltextIndexed = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "includedInSupertypeQuery" ) ) ) + m_includedInSupertypeQuery = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "controllablePolicy" ) ) ) + m_controllablePolicy = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "controllableACL" ) ) ) + m_controllableAcl = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "versionable" ) ) ) + m_versionable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "contentStreamAllowed" ) ) ) + { + libcmis::ObjectType::ContentStreamAllowed streamAllowed = libcmis::ObjectType::Allowed; + if ( value == "notallowed" ) + streamAllowed = libcmis::ObjectType::NotAllowed; + else if ( value == "required" ) + streamAllowed = libcmis::ObjectType::Required; + + m_contentStreamAllowed = streamAllowed; + } + else + { + libcmis::PropertyTypePtr type( new libcmis::PropertyType( child ) ); + m_propertiesTypes[ type->getId() ] = type; + } + + xmlFree( content ); + } + } + m_refreshTimestamp = time( NULL ); + } + } + + void ObjectType::refresh( ) + { + throw Exception( "ObjectType::refresh() shouldn't be called" ); + } + + ObjectTypePtr ObjectType::getParentType( ) + { + throw Exception( "ObjectType::getParentType() shouldn't be called" ); + } + + ObjectTypePtr ObjectType::getBaseType( ) + { + throw Exception( "ObjectType::getBaseType() shouldn't be called" ); + } + + vector< ObjectTypePtr > ObjectType::getChildren( ) + { + throw Exception( "ObjectType::getChildren() shouldn't be called" ); + } + + // LCOV_EXCL_START + string ObjectType::toString( ) + { + stringstream buf; + + buf << "Type Description:" << endl << endl; + buf << "Id: " << getId( ) << endl; + buf << "Display name: " << getDisplayName( ) << endl; + + buf << "Parent type: " << m_parentTypeId << endl; + buf << "Base type: " << m_baseTypeId << endl; + buf << "Children types [(id) Name]: " << endl; + try + { + vector< libcmis::ObjectTypePtr > children = getChildren( ); + for ( vector< libcmis::ObjectTypePtr >::iterator it = children.begin(); it != children.end(); ++it ) + { + libcmis::ObjectTypePtr type = *it; + buf << " (" << type->getId( ) << ")\t" << type->getDisplayName( ) << endl; + } + } + catch ( const libcmis::Exception& ) + { + // Ignore it + } + + buf << "Creatable: " << isCreatable( ) << endl; + buf << "Fileable: " << isFileable( ) << endl; + buf << "Queryable: " << isQueryable( ) << endl; + buf << "Full text indexed: " << isFulltextIndexed( ) << endl; + buf << "Included in supertype query: " << isIncludedInSupertypeQuery( ) << endl; + buf << "Controllable policy: " << isControllablePolicy( ) << endl; + buf << "Controllable ACL: " << isControllableACL( ) << endl; + buf << "Versionable: " << isVersionable( ) << endl; + + buf << "Property Definitions [RO/RW (id) Name]: " << endl; + map< string, libcmis::PropertyTypePtr > propsTypes = getPropertiesTypes( ); + for ( map< string, libcmis::PropertyTypePtr >::iterator it = propsTypes.begin( ); it != propsTypes.end( ); ++it ) + { + libcmis::PropertyTypePtr propType = it->second; + string updatable( "RO" ); + if ( propType->isUpdatable( ) ) + updatable = string( "RW" ); + + buf << " " << updatable << "\t (" << propType->getId( ) << ")\t" + << propType->getDisplayName( ) << endl; + } + + return buf.str(); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/object.cxx b/src/libcmis/object.cxx new file mode 100644 index 0000000..cd67f76 --- /dev/null +++ b/src/libcmis/object.cxx @@ -0,0 +1,398 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/object.hxx> + +#include <algorithm> + +#include <libcmis/session.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + Object::Object( Session* session ) : + m_session( session ), + m_typeDescription( ), + m_refreshTimestamp( 0 ), + m_typeId( ), + m_properties( ), + m_allowableActions( ), + m_renditions( ) + { + } + + Object::Object( Session* session, xmlNodePtr node ) : + m_session( session ), + m_typeDescription( ), + m_refreshTimestamp( 0 ), + m_typeId( ), + m_properties( ), + m_allowableActions( ), + m_renditions( ) + { + initializeFromNode( node ); + } + + Object::Object( const Object& copy ) : + m_session( copy.m_session ), + m_typeDescription( copy.m_typeDescription ), + m_refreshTimestamp( copy.m_refreshTimestamp ), + m_typeId( copy.m_typeId ), + m_properties( copy.m_properties ), + m_allowableActions( copy.m_allowableActions ), + m_renditions( copy.m_renditions ) + { + } + + Object& Object::operator=( const Object& copy ) + { + if ( this != © ) + { + m_session = copy.m_session; + m_typeDescription = copy.m_typeDescription; + m_refreshTimestamp = copy.m_refreshTimestamp; + m_typeId = copy.m_typeId; + m_properties = copy.m_properties; + m_allowableActions = copy.m_allowableActions; + m_renditions = copy.m_renditions; + } + + return *this; + } + + void Object::initializeFromNode( xmlNodePtr node ) + { + // Even if node is NULL we'll have an empty doc, so no need + // to worry about it. + xmlDocPtr doc = wrapInDoc( node ); + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + // Get the allowableActions + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmis:allowableActions" ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlNodePtr actionsNode = xpathObj->nodesetval->nodeTab[0]; + m_allowableActions.reset( new libcmis::AllowableActions( actionsNode ) ); + } + xmlXPathFreeObject( xpathObj ); + + // TODO Get rid of this request: + // * Too time consuming + // * Makes secondary aspect properties annoying to create + // * Prevents from getting Alfresco additional properties + // First get the type id as it will give us the property definitions + string typeIdReq( "/*/cmis:properties/cmis:propertyId[@propertyDefinitionId='cmis:objectTypeId']/cmis:value/text()" ); + m_typeId = libcmis::getXPathValue( xpathCtx, typeIdReq ); + + string propertiesReq( "/*/cmis:properties/*" ); + xpathObj = xmlXPathEvalExpression( BAD_CAST( propertiesReq.c_str() ), xpathCtx ); + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr propertyNode = xpathObj->nodesetval->nodeTab[i]; + libcmis::PropertyPtr property = libcmis::parseProperty( propertyNode, getTypeDescription( ) ); + if ( property != NULL ) + m_properties[ property->getPropertyType( )->getId() ] = property; + } + } + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + xmlFreeDoc( doc ); + + m_refreshTimestamp = time( NULL ); + } + + string Object::getStringProperty( const string& propertyName ) + { + string name; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + name = it->second->getStrings( ).front( ); + return name; + } + + string Object::getId( ) + { + return getStringProperty( "cmis:objectId" ); + } + + string Object::getName( ) + { + return getStringProperty( "cmis:name" ); + } + + string Object::getBaseType( ) + { + return getStringProperty( "cmis:baseTypeId" ); + } + + string Object::getType( ) + { + string value = getStringProperty( "cmis:objectTypeId" ); + if ( value.empty( ) ) + value = m_typeId; + return value; + } + + string Object::getCreatedBy( ) + { + return getStringProperty( "cmis:createdBy" ); + } + + string Object::getLastModifiedBy( ) + { + return getStringProperty( "cmis:lastModifiedBy" ); + } + + string Object::getChangeToken( ) + { + return getStringProperty( "cmis:changeToken" ); + } + + vector< string > Object::getPaths( ) + { + return vector< string > ( ); + } + + boost::posix_time::ptime Object::getCreationDate( ) + { + boost::posix_time::ptime value; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:creationDate" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getDateTimes( ).empty( ) ) + value = it->second->getDateTimes( ).front( ); + return value; + } + + boost::posix_time::ptime Object::getLastModificationDate( ) + { + boost::posix_time::ptime value; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:lastModificationDate" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getDateTimes( ).empty( ) ) + value = it->second->getDateTimes( ).front( ); + return value; + } + + bool Object::isImmutable( ) + { + bool value = false; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:isImmutable" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getBools( ).empty( ) ) + value = it->second->getBools( ).front( ); + return value; + } + + vector< string > Object::getSecondaryTypes( ) + { + vector< string > types; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:secondaryObjectTypeIds" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL ) + types = it->second->getStrings( ); + + return types; + } + + ObjectPtr Object::addSecondaryType( string id, PropertyPtrMap properties ) + { + // First make sure the cmis:secondaryObjectTypeIds property can be defined + map< string, PropertyTypePtr >& propertyTypes = getTypeDescription( )-> + getPropertiesTypes(); + + map< string, PropertyTypePtr >::iterator it = propertyTypes.find( "cmis:secondaryObjectTypeIds" ); + if ( it == propertyTypes.end() ) + throw ( Exception( "Secondary Types not supported", "constraint" ) ); + + // Copy all the new properties without checking they are + // defined in the secondary type definition: that would + // require one more HTTP request and the server will complain + // anyway if it's not good. + PropertyPtrMap newProperties( properties ); + + // Prepare the new cmis:secondaryObjectTypeIds property + vector< string > secTypes = getSecondaryTypes( ); + if ( find( secTypes.begin(), secTypes.end(), id ) == secTypes.end( ) ) + { + secTypes.push_back( id ); + PropertyPtr newSecTypes( new Property( it->second, secTypes ) ); + newProperties["cmis:secondaryObjectTypeIds"] = newSecTypes; + } + return updateProperties( newProperties ); + } + + ObjectPtr Object::removeSecondaryType( string id ) + { + // First make sure the cmis:secondaryObjectTypeIds property can be defined + map< string, PropertyTypePtr >& propertyTypes = getTypeDescription( )-> + getPropertiesTypes(); + + map< string, PropertyTypePtr >::iterator it = propertyTypes.find( "cmis:secondaryObjectTypeIds" ); + if ( it == propertyTypes.end() ) + throw ( Exception( "Secondary Types not supported", "constraint" ) ); + + // Prepare the new cmis:secondaryObjectTypeIds property + PropertyPtrMap newProperties; + vector< string > secTypes = getSecondaryTypes( ); + vector< string > newSecTypes; + for ( vector< string >::iterator idIt = secTypes.begin( ); + idIt != secTypes.end( ); ++idIt ) + { + if ( *idIt != id ) + newSecTypes.push_back( *idIt ); + } + + // No need to update the property if it didn't change + if ( newSecTypes.size( ) != secTypes.size( ) ) + { + PropertyPtr property ( new Property( it->second, newSecTypes ) ); + newProperties["cmis:secondaryObjectTypeIds"] = property; + } + + return updateProperties( newProperties ); + } + + PropertyPtrMap& Object::getProperties( ) + { + return m_properties; + } + + libcmis::ObjectTypePtr Object::getTypeDescription( ) + { + if ( !m_typeDescription.get( ) && m_session != NULL ) + m_typeDescription = m_session->getType( getType( ) ); + + return m_typeDescription; + } + + vector< RenditionPtr> Object::getRenditions( string /*filter*/ ) + { + return m_renditions; + } + + string Object::getThumbnailUrl( ) + { + string url; + vector< RenditionPtr > renditions = getRenditions( ); + for ( vector< RenditionPtr >::iterator it = renditions.begin( ); + it != renditions.end( ); ++it) + + { + if ( (*it)->getKind( ) == "cmis:thumbnail" ) return (*it)->getUrl( ); + } + + return url; + } + + // LCOV_EXCL_START + string Object::toString( ) + { + stringstream buf; + + buf << "Id: " << getId() << endl; + buf << "Name: " << getName() << endl; + buf << "Type: " << getType() << endl; + buf << "Base type: " << getBaseType() << endl; + buf << "Created on " << boost::posix_time::to_simple_string( getCreationDate() ) + << " by " << getCreatedBy() << endl; + buf << "Last modified on " << boost::posix_time::to_simple_string( getLastModificationDate() ) + << " by " << getLastModifiedBy() << endl; + buf << "Change token: " << getChangeToken() << endl; + + // Write Allowable Actions + if ( getAllowableActions( ) ) + buf << endl << getAllowableActions( )->toString( ) << endl; + + // Write remaining properties + static const char* skippedProps[] = { + "cmis:name", "cmis:baseTypeId", "cmis:objectTypeId", "cmis:createdBy", + "cmis:creationDate", "cmis:lastModifiedBy", "cmis:lastModificationDate", + "cmis::changeToken" + }; + int skippedCount = sizeof( skippedProps ) / sizeof( char* ); + + for ( PropertyPtrMap::iterator it = getProperties( ).begin(); + it != getProperties( ).end( ); ++it ) + { + string propId = it->first; + bool toSkip = false; + for ( int i = 0; i < skippedCount && !toSkip; ++i ) + { + toSkip = propId == skippedProps[i]; + } + + if ( !toSkip ) + { + libcmis::PropertyPtr prop = it->second; + if ( prop != NULL && prop->getPropertyType( ) != NULL ) + { + buf << prop->getPropertyType( )->getDisplayName( ) << "( " << prop->getPropertyType()->getId( ) << " ): " << endl; + vector< string > strValues = prop->getStrings( ); + for ( vector< string >::iterator valueIt = strValues.begin( ); + valueIt != strValues.end( ); ++valueIt ) + { + buf << "\t" << *valueIt << endl; + } + } + } + } + + vector< libcmis::RenditionPtr > renditions = getRenditions( ); + if ( !renditions.empty() ) + { + buf << "Renditions: " << endl; + for ( vector< libcmis::RenditionPtr >::iterator it = renditions.begin(); + it != renditions.end(); ++it ) + { + buf << ( *it )->toString( ) << endl; + } + } + + return buf.str(); + } + // LCOV_EXCL_STOP + + void Object::toXml( xmlTextWriterPtr writer ) + { + // Output the properties + xmlTextWriterStartElement( writer, BAD_CAST( "cmis:properties" ) ); + for ( PropertyPtrMap::iterator it = getProperties( ).begin( ); + it != getProperties( ).end( ); ++it ) + { + it->second->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + } +} diff --git a/src/libcmis/onedrive-allowable-actions.hxx b/src/libcmis/onedrive-allowable-actions.hxx new file mode 100644 index 0000000..fbdba9c --- /dev/null +++ b/src/libcmis/onedrive-allowable-actions.hxx @@ -0,0 +1,102 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Varga Mihai <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_ALLOWABLE_ACTIONS_HXX_ +#define _ONEDRIVE_ALLOWABLE_ACTIONS_HXX_ + +#include <libcmis/allowable-actions.hxx> + +class OneDriveAllowableActions: public libcmis::AllowableActions +{ + public: + OneDriveAllowableActions( bool isFolder ) : AllowableActions( ) + { + m_states.clear( ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::UpdateProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectRelationships, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectParents, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::MoveObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateRelationship, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyPolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAppliedPolicies, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemovePolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetACL, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyACL, false ) ); + + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderTree, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderParent, isFolder) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetDescendants, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CancelCheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckIn, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::SetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAllVersions, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::AddObjectToFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemoveObjectFromFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetRenditions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetChildren, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateDocument, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateFolder, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteTree, isFolder ) ); + } +}; + +#endif diff --git a/src/libcmis/onedrive-document.cxx b/src/libcmis/onedrive-document.cxx new file mode 100644 index 0000000..863a92f --- /dev/null +++ b/src/libcmis/onedrive-document.cxx @@ -0,0 +1,177 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-document.hxx" + +#include <libcmis/rendition.hxx> + +#include "onedrive-folder.hxx" +#include "onedrive-session.hxx" +#include "onedrive-utils.hxx" +#include "json-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveDocument::OneDriveDocument( OneDriveSession* session ) : + libcmis::Object( session), + libcmis::Document( session ), + OneDriveObject( session ) +{ +} + +OneDriveDocument::OneDriveDocument( OneDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session), + libcmis::Document( session ), + OneDriveObject( session, json, id, name ) +{ +} + +OneDriveDocument::~OneDriveDocument( ) +{ +} + +vector< libcmis::FolderPtr > OneDriveDocument::getParents( ) +{ + vector< libcmis::FolderPtr > parents; + + string parentId = getStringProperty( "cmis:parentId" ); + + libcmis::ObjectPtr obj = getSession( )->getObject( parentId ); + libcmis::FolderPtr parent = boost::dynamic_pointer_cast< libcmis::Folder >( obj ); + parents.push_back( parent ); + return parents; +} + +boost::shared_ptr< istream > OneDriveDocument::getContentStream( string /*streamId*/ ) +{ + boost::shared_ptr< istream > stream; + string streamUrl = getStringProperty( "source" ); + if ( streamUrl.empty( ) ) + throw libcmis::Exception( "could not find stream url" ); + + try + { + stream = getSession( )->httpGetRequest( streamUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return stream; +} + +void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os, + string /*contentType*/, + string fileName, + bool bReplaceExisting ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + string metaUrl = getUrl( ); + + // Update file name meta information + if ( bReplaceExisting && !fileName.empty( ) && fileName != getContentFilename( ) ) + { + Json metaJson; + Json fileJson( fileName.c_str( ) ); + metaJson.add("name", fileJson ); + + std::istringstream is( metaJson.toString( ) ); + vector<string> headers; + headers.push_back( "Content-Type: application/json" ); + try + { + getSession()->httpPatchRequest( metaUrl, is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + } + + fileName = libcmis::escape( getStringProperty( "cmis:name" ) ); + string putUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + + getStringProperty( "cmis:parentId" ) + ":/" + + fileName + ":/content"; + + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector <string> headers; + try + { + getSession()->httpPutRequest( putUrl, *is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + throw libcmis::Exception( "Document content wasn't set for" + "some reason" ); + refresh( ); +} + +libcmis::DocumentPtr OneDriveDocument::checkOut( ) +{ + // OneDrive doesn't have CheckOut, so just return the same document here + // TODO: no longer true - onedrive now has checkout/checkin + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkout = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkout; +} + +void OneDriveDocument::cancelCheckout( ) +{ + // Don't do anything since we don't have CheckOut +} + +libcmis::DocumentPtr OneDriveDocument::checkIn( bool /*isMajor*/, + std::string /*comment*/, + const PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ) +{ + // OneDrive doesn't have CheckIn, so just upload the properties, + // the content stream and fetch the new document resource. + updateProperties( properties ); + setContentStream( stream, contentType, fileName ); + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkin = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkin; +} + +vector< libcmis::DocumentPtr > OneDriveDocument::getAllVersions( ) +{ + return vector< libcmis::DocumentPtr > ( ); +} diff --git a/src/libcmis/onedrive-document.hxx b/src/libcmis/onedrive-document.hxx new file mode 100644 index 0000000..d70cede --- /dev/null +++ b/src/libcmis/onedrive-document.hxx @@ -0,0 +1,75 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_DOCUMENT_HXX_ +#define _ONEDRIVE_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/rendition.hxx> + +#include "onedrive-object.hxx" +#include "json-utils.hxx" + +class OneDriveDocument : public libcmis::Document, public OneDriveObject +{ + public: + OneDriveDocument( OneDriveSession* session ); + + OneDriveDocument( OneDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + ~OneDriveDocument( ); + + std::string getType( ) { return std::string( "cmis:document" );} + std::string getBaseType( ) { return std::string( "cmis:document" );} + + virtual std::vector< libcmis::FolderPtr > getParents( ); + + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName, + bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + + virtual libcmis::DocumentPtr checkIn( bool isMajor, + std::string comment, + const std::map< std::string,libcmis::PropertyPtr >& + properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); +}; + +#endif diff --git a/src/libcmis/onedrive-folder.cxx b/src/libcmis/onedrive-folder.cxx new file mode 100644 index 0000000..c1980c8 --- /dev/null +++ b/src/libcmis/onedrive-folder.cxx @@ -0,0 +1,167 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-folder.hxx" + +#include "onedrive-document.hxx" +#include "onedrive-session.hxx" +#include "onedrive-property.hxx" +#include "onedrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveFolder::OneDriveFolder( OneDriveSession* session ): + libcmis::Object( session ), + libcmis::Folder( session ), + OneDriveObject( session ) +{ +} + +OneDriveFolder::OneDriveFolder( OneDriveSession* session, Json json ): + libcmis::Object( session ), + libcmis::Folder( session ), + OneDriveObject( session, json ) +{ +} + +OneDriveFolder::~OneDriveFolder( ) +{ +} + +vector< libcmis::ObjectPtr > OneDriveFolder::getChildren( ) +{ + vector< libcmis::ObjectPtr > children; + // TODO: limited to 200 items by default - to get more one would have to + // follow @odata.nextLink or change pagination size + string query = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children"; + + string res; + try + { + res = getSession( )->httpGetRequest( query )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["value"].getList( ); + + // Create children objects from Json objects + for(unsigned int i = 0; i < objs.size(); i++) + { + children.push_back( getSession( )->getObjectFromJson( objs[i] ) ); + } + + return children; +} + +libcmis::FolderPtr OneDriveFolder::createFolder( + const PropertyPtrMap& properties ) +{ + Json propsJson = OneDriveUtils::toOneDriveJson( properties ); + string uploadUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children"; + + std::istringstream is( propsJson.toString( ) ); + string response; + try + { + response = getSession()->httpPostRequest( uploadUrl, is, "application/json" ) + ->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( response ); + libcmis::FolderPtr folderPtr( new OneDriveFolder( getSession( ), jsonRes ) ); + + refresh( ); + return folderPtr; +} + +libcmis::DocumentPtr OneDriveFolder::createDocument( + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, + string /*contentType*/, string fileName ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + if (fileName.empty( ) ) + { + for ( PropertyPtrMap::const_iterator it = properties.begin( ) ; + it != properties.end( ) ; ++it ) + { + if ( it->first == "cmis:name" ) + { + fileName = it->second->toString( ); + } + } + } + + // TODO: limited to 4MB, larger uploads need dedicated UploadSession + fileName = libcmis::escape( fileName ); + string newDocUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + + getId( ) + ":/" + fileName + ":/content"; + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector< string > headers; + string res; + // this will only create the file and return it's id, name and source url + try + { + res = getSession( )->httpPutRequest( newDocUrl, *is, headers ) + ->getStream( )->str( ); + } + catch (const CurlException& e) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + DocumentPtr document( new OneDriveDocument( getSession( ), jsonRes ) ); + + // Upload the properties + ObjectPtr object = document->updateProperties( properties ); + document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + refresh( ); + return document; +} + +vector< string > OneDriveFolder::removeTree( + bool /*allVersions*/, + libcmis::UnfileObjects::Type /*unfile*/, + bool /*continueOnError*/ ) +{ + remove( ); + // Nothing to return here + return vector< string >( ); +} diff --git a/src/libcmis/onedrive-folder.hxx b/src/libcmis/onedrive-folder.hxx new file mode 100644 index 0000000..fc47ab3 --- /dev/null +++ b/src/libcmis/onedrive-folder.hxx @@ -0,0 +1,64 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_FOLDER_HXX_ +#define _ONEDRIVE_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "onedrive-object.hxx" +#include "json-utils.hxx" + +class OneDriveFolder : public libcmis::Folder, public OneDriveObject +{ + public: + OneDriveFolder( OneDriveSession* session ); + OneDriveFolder( OneDriveSession* session, Json json ); + ~OneDriveFolder( ); + + std::string getType( ) { return std::string( "cmis:folder" );} + std::string getBaseType( ) { return std::string( "cmis:folder" );} + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( + const libcmis::PropertyPtrMap& properties ); + + virtual libcmis::DocumentPtr createDocument( + const libcmis::PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName ); + + virtual std::vector< std::string > removeTree( + bool allVersion = true, + libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/onedrive-object-type.cxx b/src/libcmis/onedrive-object-type.cxx new file mode 100644 index 0000000..a88aef8 --- /dev/null +++ b/src/libcmis/onedrive-object-type.cxx @@ -0,0 +1,118 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-object-type.hxx" + +OneDriveObjectType::OneDriveObjectType( const std::string& id ): ObjectType( ) +{ + m_id = id; + m_localName = "OneDrive Object Type"; + m_localNamespace = "OneDrive Object Type"; + m_displayName = "OneDrive Object Type"; + m_queryName = id; + m_description = "OneDrive Object Type"; + m_parentTypeId = ""; + m_baseTypeId = id; + m_creatable = true; + m_versionable = false; + m_fileable = true; + m_fulltextIndexed = ( id == "cmis:document" ); + m_queryable = true; + if ( id == "cmis:document" ) + m_contentStreamAllowed = libcmis::ObjectType::Allowed; + else + m_contentStreamAllowed = libcmis::ObjectType::NotAllowed; + + libcmis::PropertyTypePtr idType(new libcmis::PropertyType( ) ); + idType->setId( "cmis:objectTypeId" ); + idType->setType( libcmis::PropertyType::String ); + m_propertiesTypes[ idType->getId( ) ] = idType; + + // create PropertyTypes which are updatable. + + // name + libcmis::PropertyTypePtr nameType( new libcmis::PropertyType( ) ); + nameType->setId( "cmis:name" ); + nameType->setType( libcmis::PropertyType::String ); + nameType->setUpdatable( true ); + m_propertiesTypes[ nameType->getId( ) ] = nameType; + + // description + libcmis::PropertyTypePtr descriptionType( new libcmis::PropertyType( ) ); + descriptionType->setId( "cmis:description" ); + descriptionType->setType( libcmis::PropertyType::String ); + descriptionType->setUpdatable( true ); + m_propertiesTypes[ descriptionType->getId( ) ] = descriptionType; + + // modifiedDate + libcmis::PropertyTypePtr modifiedDateType( new libcmis::PropertyType( ) ); + modifiedDateType->setId( "cmis:lastModificationDate" ); + modifiedDateType->setType( libcmis::PropertyType::DateTime ); + modifiedDateType->setUpdatable( false ); + m_propertiesTypes[ modifiedDateType->getId( ) ] = modifiedDateType; + + // creationDate + libcmis::PropertyTypePtr creationDateType( new libcmis::PropertyType( ) ); + creationDateType->setId( "cmis:creationDate" ); + creationDateType->setType( libcmis::PropertyType::DateTime ); + creationDateType->setUpdatable( false ); + m_propertiesTypes[ creationDateType->getId( ) ] = creationDateType; + + + if ( id == "cmis:document" ) + { + // size + libcmis::PropertyTypePtr contentStreamLength( new libcmis::PropertyType( ) ); + contentStreamLength->setId( "cmis:contentStreamLength" ); + contentStreamLength->setType( libcmis::PropertyType::Integer ); + contentStreamLength->setUpdatable( false ); + m_propertiesTypes[ contentStreamLength->getId( ) ] = contentStreamLength; + + // streamFileName + libcmis::PropertyTypePtr streamFileNameType( new libcmis::PropertyType( ) ); + streamFileNameType->setId( "cmis:contentStreamFileName" ); + streamFileNameType->setType( libcmis::PropertyType::String ); + streamFileNameType->setUpdatable( true ); + m_propertiesTypes[ streamFileNameType->getId( ) ] = streamFileNameType; + } +} + +libcmis::ObjectTypePtr OneDriveObjectType::getParentType( ) +{ + libcmis::ObjectTypePtr parentTypePtr; + if ( m_parentTypeId != "" ) { + parentTypePtr.reset( new OneDriveObjectType( m_parentTypeId ) ); + } + return parentTypePtr; +} + +libcmis::ObjectTypePtr OneDriveObjectType::getBaseType( ) +{ + libcmis::ObjectTypePtr baseTypePtr( new OneDriveObjectType( m_baseTypeId ) ); + return baseTypePtr; +} diff --git a/src/libcmis/onedrive-object-type.hxx b/src/libcmis/onedrive-object-type.hxx new file mode 100644 index 0000000..34b65f6 --- /dev/null +++ b/src/libcmis/onedrive-object-type.hxx @@ -0,0 +1,46 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_OBJECT_TYPE_HXX_ +#define _ONEDRIVE_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "json-utils.hxx" + +class OneDriveObjectType: public libcmis::ObjectType +{ + public: + OneDriveObjectType( const std::string& id ); + + virtual libcmis::ObjectTypePtr getParentType( ); + + virtual libcmis::ObjectTypePtr getBaseType( ); +}; + +#endif diff --git a/src/libcmis/onedrive-object.cxx b/src/libcmis/onedrive-object.cxx new file mode 100644 index 0000000..8deb591 --- /dev/null +++ b/src/libcmis/onedrive-object.cxx @@ -0,0 +1,198 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-object.hxx" + +#include "onedrive-allowable-actions.hxx" +#include "onedrive-property.hxx" +#include "onedrive-repository.hxx" +#include "onedrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveObject::OneDriveObject( OneDriveSession* session ) : + libcmis::Object( session ) +{ +} + +OneDriveObject::OneDriveObject( OneDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session ) +{ + initializeFromJson( json, id, name ); +} + +OneDriveObject::OneDriveObject( const OneDriveObject& copy ) : + libcmis::Object( copy ) +{ +} + +OneDriveObject& OneDriveObject::operator=( const OneDriveObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + return *this; +} + +void OneDriveObject::initializeFromJson ( Json json, string /*id*/, string /*name*/ ) +{ + Json::JsonObject objs = json.getObjects( ); + Json::JsonObject::iterator it; + PropertyPtr property; + bool isFolder = json["folder"].toString( ) != ""; + for ( it = objs.begin( ); it != objs.end( ); ++it) + { + property.reset( new OneDriveProperty( it->first, it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + if ( it->first == "name" && !isFolder ) + { + property.reset( new OneDriveProperty( "cmis:contentStreamFileName", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } else if ( it->first == "parentReference" ) { + if (it->second["id"].toString() != "") { + property.reset( new OneDriveProperty( "cmis:parentId", it->second["id"] ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + } + } + + m_refreshTimestamp = time( NULL ); + m_allowableActions.reset( new OneDriveAllowableActions( isFolder ) ); +} + +OneDriveSession* OneDriveObject::getSession( ) +{ + return dynamic_cast< OneDriveSession* > ( m_session ); +} + +void OneDriveObject::refreshImpl( Json json ) +{ + m_typeDescription.reset( ); + m_properties.clear( ); + initializeFromJson( json ); +} + +void OneDriveObject::refresh( ) +{ + string res; + try + { + res = getSession()->httpGetRequest( getUrl( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json json = Json::parse( res ); + refreshImpl( json ); +} + +void OneDriveObject::remove( bool /*allVersions*/ ) +{ + try + { + getSession( )->httpDeleteRequest( getUrl( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +string OneDriveObject::getUrl( ) +{ + return getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ); +} + +string OneDriveObject::getUploadUrl( ) +{ + return getUrl( ) + "/files"; +} + +vector< string> OneDriveObject::getMultiStringProperty( const string& propertyName ) +{ + vector< string > values; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + values = it->second->getStrings( ); + return values; +} + +libcmis::ObjectPtr OneDriveObject::updateProperties( + const PropertyPtrMap& properties ) +{ + // Make Json object from properties + Json json = OneDriveUtils::toOneDriveJson( properties ); + + istringstream is( json.toString( )); + + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/json" ); + response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + libcmis::ObjectPtr updated = getSession()->getObjectFromJson( jsonRes ); + + if ( updated->getId( ) == getId( ) ) + refreshImpl( jsonRes ); + + return updated; +} + +void OneDriveObject::move( FolderPtr /*source*/, FolderPtr destination ) +{ + Json destJson; + Json destId( destination->getId( ).c_str( ) ); + destJson.add( "destination", destId ); + + istringstream is( destJson.toString( ) ); + libcmis::HttpResponsePtr response; + try + { + string url = getUrl( ) + "?method=MOVE"; + response = getSession( )->httpPostRequest( url, is, "application/json" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + + refreshImpl( jsonRes ); +} diff --git a/src/libcmis/onedrive-object.hxx b/src/libcmis/onedrive-object.hxx new file mode 100644 index 0000000..8ad46b3 --- /dev/null +++ b/src/libcmis/onedrive-object.hxx @@ -0,0 +1,76 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_OBJECT_HXX_ +#define _ONEDRIVE_OBJECT_HXX_ + +#include <libcmis/object.hxx> + +#include "onedrive-session.hxx" +#include "json-utils.hxx" + +// Class representing an object for OneDrive protocol. +class OneDriveObject : public virtual libcmis::Object +{ + public: + OneDriveObject( OneDriveSession* session ); + + // Create a OneDrive document from Json properties. + OneDriveObject( OneDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + OneDriveObject( const OneDriveObject& copy ); + virtual ~OneDriveObject( ) { } + + OneDriveObject& operator=( const OneDriveObject& copy ); + + void initializeFromJson( Json json, std::string id = std::string( ), + std::string name = std::string( ) ); + + void refreshImpl( Json json ); + virtual void refresh( ); + virtual void remove( bool allVersions = true ); + + std::string getUrl( ); + std::string getUploadUrl( ); + std::vector< std::string > getMultiStringProperty( + const std::string& propertyName ); + + virtual boost::shared_ptr< Object > updateProperties( + const libcmis::PropertyPtrMap& properties ); + + virtual std::vector< libcmis::RenditionPtr> getRenditions( std::string /*filter = std::string( )*/ ) + {return std::vector< libcmis::RenditionPtr>( );} + + virtual void move( boost::shared_ptr< libcmis::Folder > source, + boost::shared_ptr< libcmis::Folder > destination ); + + protected: + OneDriveSession* getSession( ); + +}; +#endif diff --git a/src/libcmis/onedrive-property.cxx b/src/libcmis/onedrive-property.cxx new file mode 100644 index 0000000..a9aa72a --- /dev/null +++ b/src/libcmis/onedrive-property.cxx @@ -0,0 +1,78 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-property.hxx" + +#include <libcmis/property-type.hxx> + +#include "onedrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveProperty::OneDriveProperty( ) +{ +} + +OneDriveProperty::~OneDriveProperty( ) +{ +} + +OneDriveProperty::OneDriveProperty( const string& key, Json json ): + Property( ) +{ + PropertyTypePtr propertyType( new PropertyType( ) ); + string convertedKey = OneDriveUtils::toCmisKey( key ); + propertyType->setId( convertedKey ); + propertyType->setLocalName( convertedKey ); + propertyType->setLocalNamespace( convertedKey ); + propertyType->setQueryName( convertedKey ); + propertyType->setDisplayName( key ); + propertyType->setTypeFromJsonType( json.getStrType( ) ); + propertyType->setUpdatable( OneDriveUtils::checkUpdatable( key ) ); + propertyType->setMultiValued( OneDriveUtils::checkMultiValued( key ) ); + + setPropertyType( propertyType ); + + vector< string > values = OneDriveUtils::parseOneDriveProperty( key, json ); + setValues( values ); +} + +OneDriveProperty::OneDriveProperty( const OneDriveProperty& copy ) : + libcmis::Property( copy ) +{ +} + +OneDriveProperty& OneDriveProperty::operator=( const OneDriveProperty& copy ) +{ + if ( this != © ) + { + libcmis::Property::operator=( copy ); + } + return *this; +} diff --git a/src/libcmis/onedrive-property.hxx b/src/libcmis/onedrive-property.hxx new file mode 100644 index 0000000..9d45316 --- /dev/null +++ b/src/libcmis/onedrive-property.hxx @@ -0,0 +1,52 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_PROPERTY_HXX_ +#define _ONEDRIVE_PROPERTY_HXX_ + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +// reference: http://msdn.microsoft.com/en-us/library/hh243648.aspx +class OneDriveProperty : public libcmis::Property +{ + public : + // Create a OneDrive Property from a Json property with its key + OneDriveProperty( const std::string& key, Json json); + ~OneDriveProperty( ); + OneDriveProperty( const OneDriveProperty& copy); + OneDriveProperty& operator=( const OneDriveProperty& copy ); + + // Check if the property is updatable + bool checkUpdatable( const std::string& key ); + private : + // Avoid calling default constructor + OneDriveProperty( ); +}; +#endif diff --git a/src/libcmis/onedrive-repository.cxx b/src/libcmis/onedrive-repository.cxx new file mode 100644 index 0000000..b01f5c2 --- /dev/null +++ b/src/libcmis/onedrive-repository.cxx @@ -0,0 +1,54 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#include "onedrive-repository.hxx" + +OneDriveRepository::OneDriveRepository( ) : + Repository( ) +{ + m_id = "OneDrive"; + m_name = "One Drive"; + m_description = "One Drive repository"; + m_productName = "One Drive"; + m_productVersion = "v5"; + m_rootId = "/me/drive/root"; + + m_capabilities[ ACL ] = "discover"; + m_capabilities[ AllVersionsSearchable ] = "true"; + m_capabilities[ Changes ] = "all"; + m_capabilities[ GetDescendants ] = "true"; + m_capabilities[ GetFolderTree ] = "true"; + m_capabilities[ OrderBy ] = "custom"; + m_capabilities[ Multifiling ] = "true"; + m_capabilities[ PWCSearchable ] = "true"; + m_capabilities[ PWCUpdatable ] = "true"; + m_capabilities[ Query ] = "bothcombined"; + m_capabilities[ Renditions ] = "read"; + m_capabilities[ Unfiling ] = "false"; + m_capabilities[ VersionSpecificFiling ] = "false"; + m_capabilities[ Join ] = "none"; +} diff --git a/src/libcmis/onedrive-repository.hxx b/src/libcmis/onedrive-repository.hxx new file mode 100644 index 0000000..e1a8dc4 --- /dev/null +++ b/src/libcmis/onedrive-repository.hxx @@ -0,0 +1,40 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_REPOSITORY_HXX_ +#define _ONEDRIVE_REPOSITORY_HXX_ + +#include <libcmis/repository.hxx> + +class OneDriveRepository: public libcmis::Repository +{ + public: + OneDriveRepository( ); +}; + +#endif + diff --git a/src/libcmis/onedrive-session.cxx b/src/libcmis/onedrive-session.cxx new file mode 100644 index 0000000..375cd2e --- /dev/null +++ b/src/libcmis/onedrive-session.cxx @@ -0,0 +1,207 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-session.hxx" + +#include "oauth2-handler.hxx" +#include "onedrive-object-type.hxx" +#include "onedrive-document.hxx" +#include "onedrive-folder.hxx" +#include "onedrive-object.hxx" +#include "onedrive-repository.hxx" + +using namespace std; + +OneDriveSession::OneDriveSession ( string baseUrl, + string username, + string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose ) : + BaseSession( baseUrl, string(), username, password, false, + libcmis::OAuth2DataPtr(), verbose ) + +{ + // Add the dummy repository + m_repositories.push_back( getRepository( ) ); + + if ( oauth2 && oauth2->isComplete( ) ){ + setOAuth2Data( oauth2 ); + } +} + +OneDriveSession::OneDriveSession() : + BaseSession() +{ +} + +OneDriveSession::~OneDriveSession() +{ +} + +void OneDriveSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); + + oauth2Authenticate( ); +} + +void OneDriveSession::oauth2Authenticate() +{ + // treat the supplied password as refresh token + if (!m_password.empty()) + { + try + { + m_inOAuth2Authentication = true; + + m_oauth2Handler->setRefreshToken(m_password); + // Try to get new access tokens using the stored refreshtoken + m_oauth2Handler->refresh(); + m_inOAuth2Authentication = false; + } + catch (const CurlException &e) + { + m_inOAuth2Authentication = false; + // refresh token expired or invalid, trigger initial auth (that in turn will hit the fallback with copy'n'paste method) + BaseSession::oauth2Authenticate(); + } + } + else + { + BaseSession::oauth2Authenticate(); + } +} + +string OneDriveSession::getRefreshToken() { + return HttpSession::getRefreshToken(); +} + +libcmis::RepositoryPtr OneDriveSession::getRepository( ) +{ + // Return a dummy repository since OneDrive doesn't have that notion + libcmis::RepositoryPtr repo( new OneDriveRepository( ) ); + return repo; +} + +libcmis::ObjectPtr OneDriveSession::getObject( string objectId ) +{ + // Run the http request to get the properties definition + string res; + string objectLink = m_bindingUrl + "/me/drive/items/" + objectId; + if (objectId == getRootId()) + objectLink = m_bindingUrl + objectId; + try + { + res = httpGetRequest( objectLink )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + return getObjectFromJson( jsonRes ); +} + +libcmis::ObjectPtr OneDriveSession::getObjectFromJson( Json& jsonRes ) +{ + libcmis::ObjectPtr object; + if ( jsonRes["folder"].toString() != "" ) + { + object.reset( new OneDriveFolder( this, jsonRes ) ); + } + else if ( jsonRes["file"].toString() != "" ) + { + object.reset( new OneDriveDocument( this, jsonRes ) ); + } + else + { + object.reset( new OneDriveObject( this, jsonRes ) ); + } + return object; +} + +libcmis::ObjectPtr OneDriveSession::getObjectByPath( string path ) +{ + string res; + string objectQuery = m_bindingUrl + "/me/drive/root:" + libcmis::escape( path ); + try + { + res = httpGetRequest( objectQuery )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw libcmis::Exception( "No file could be found for path " + path + ": " + e.what() ); + } + Json jsonRes = Json::parse( res ); + return getObjectFromJson( jsonRes ); +} + +bool OneDriveSession::isAPathMatch( Json objectJson, string path ) +{ + string parentId = objectJson["parent_id"].toString( ); + string objectName = objectJson["name"].toString( ); + size_t pos = path.rfind("/"); + string pathName = path.substr( pos + 1, path.size( ) ); + string truncatedPath = path.substr( 0, pos ); + + if ( truncatedPath.empty( ) && parentId == "null" && objectName == pathName ) + { + // match + return true; + } + if ( truncatedPath.empty( ) || parentId == "null" || objectName != pathName ) + { + return false; + } + + string res; + string parentUrl = m_bindingUrl + "/" + parentId; + try + { + res = httpGetRequest( parentUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + return isAPathMatch( jsonRes, truncatedPath ); +} + +libcmis::ObjectTypePtr OneDriveSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new OneDriveObjectType( id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > OneDriveSession::getBaseTypes( ) +{ + vector< libcmis::ObjectTypePtr > types; + return types; +} diff --git a/src/libcmis/onedrive-session.hxx b/src/libcmis/onedrive-session.hxx new file mode 100644 index 0000000..3c30c04 --- /dev/null +++ b/src/libcmis/onedrive-session.hxx @@ -0,0 +1,75 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_SESSION_HXX_ +#define _ONEDRIVE_SESSION_HXX_ + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" +#include "json-utils.hxx" + +class OneDriveSession : public BaseSession +{ + public: + OneDriveSession( std::string baseUrl, + std::string username, + std::string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose = false ); + + ~OneDriveSession ( ); + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string ) { return true; } + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + libcmis::ObjectPtr getObjectFromJson( Json& jsonRes ); + + bool isAPathMatch( Json objectJson, std::string path ); + + virtual std::string getRefreshToken(); + + private: + OneDriveSession( ); + OneDriveSession( const OneDriveSession& copy ) = delete; + OneDriveSession& operator=( const OneDriveSession& copy ) = delete; + + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + void oauth2Authenticate( ); +}; + +#endif /* _ONEDRIVE_SESSION_HXX_ */ diff --git a/src/libcmis/onedrive-utils.cxx b/src/libcmis/onedrive-utils.cxx new file mode 100644 index 0000000..17ed324 --- /dev/null +++ b/src/libcmis/onedrive-utils.cxx @@ -0,0 +1,131 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-utils.hxx" + +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +string OneDriveUtils::toCmisKey( const string& key ) +{ + string convertedKey; + if ( key == "id") + convertedKey = "cmis:objectId"; + else if ( key == "from" ) + convertedKey = "cmis:createdBy"; + else if ( key == "description" ) + convertedKey = "cmis:description"; + else if ( key == "createdDateTime" ) + convertedKey = "cmis:creationDate"; + else if ( key == "lastModifiedDateTime" ) + convertedKey = "cmis:lastModificationDate"; + else if ( key == "name" ) + convertedKey = "cmis:name"; + else if ( key == "size" ) + convertedKey = "cmis:contentStreamLength"; + else if ( key == "@microsoft.graph.downloadUrl" ) + convertedKey = "source"; + else convertedKey = key; + return convertedKey; +} + +string OneDriveUtils::toOneDriveKey( const string& key ) +{ + string convertedKey; + if ( key == "cmis:objectId") + convertedKey = "id"; + else if ( key == "cmis:createdBy" ) + convertedKey = "from"; + else if ( key == "cmis:creationDate" ) + convertedKey = "created_time"; + else if ( key == "cmis:description" ) + convertedKey = "description"; + else if ( key == "cmis:lastModificationDate" ) + convertedKey = "updated_time"; + else if ( key == "cmis:name" ) + convertedKey = "name"; + else if ( key == "cmis:contentStreamLength" ) + convertedKey = "file_size"; + else convertedKey = key; + return convertedKey; +} + +bool OneDriveUtils::checkUpdatable( const std::string& key) +{ + bool updatable = ( key == "name" || + key == "description" ); + return updatable; +} + +bool OneDriveUtils::checkMultiValued( const string& key ) +{ + bool bMultiValued = ( key == "from" || + key == "shared_with" ); + return bMultiValued; +} + +vector< string > OneDriveUtils::parseOneDriveProperty( string key, Json json ) +{ + vector< string > values; + if ( key == "from" ) + { + string ownerName = json["name"].toString( ); + values.push_back( ownerName); + } + else if ( key == "shared_with" ) + { + string sharedWith = json["access"].toString( ); + values.push_back( sharedWith ); + } + else values.push_back( json.toString( ) ); + return values; +} + +Json OneDriveUtils::toOneDriveJson( const PropertyPtrMap& properties ) +{ + Json propsJson; + + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + string key = toOneDriveKey( it->first ); + Json value( it->second ); + + // Convert the key back to the onedrive key + if ( checkUpdatable( key ) ) + { + propsJson.add( key, value ); + } + } + + return propsJson; +} diff --git a/src/libcmis/onedrive-utils.hxx b/src/libcmis/onedrive-utils.hxx new file mode 100644 index 0000000..eb9fa6e --- /dev/null +++ b/src/libcmis/onedrive-utils.hxx @@ -0,0 +1,60 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_UTILS_HXX_ +#define _ONEDRIVE_UTILS_HXX_ + +#include <string> + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +class OneDriveUtils +{ + public : + + // Convert a OneDrive Property key to a CMIS key + static std::string toCmisKey( const std::string& key); + + // Convert a CMIS key to OneDrive key + static std::string toOneDriveKey( const std::string& key ); + + // Check if a property is updatable + static bool checkUpdatable( const std::string& key); + + // Check if a property has multiple values + static bool checkMultiValued( const std::string& key); + + // Parse a OneDrive property value to CMIS values + static std::vector< std::string > parseOneDriveProperty( std::string key, Json jsonValue ); + + // Convert CMIS properties to OneDrive properties + static Json toOneDriveJson( const libcmis::PropertyPtrMap& properties ); +}; + +#endif diff --git a/src/libcmis/property-type.cxx b/src/libcmis/property-type.cxx new file mode 100644 index 0000000..42af61d --- /dev/null +++ b/src/libcmis/property-type.cxx @@ -0,0 +1,254 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/property-type.hxx> + +#include <libcmis/object-type.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + PropertyType::PropertyType( ) : + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_type( String ), + m_xmlType( "String" ), + m_multiValued( false ), + m_updatable( false ), + m_inherited( false ), + m_required( false ), + m_queryable( false ), + m_orderable( false ), + m_openChoice( false ), + m_temporary( false ) + { + } + + PropertyType::PropertyType( xmlNodePtr node ) : + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_type( String ), + m_xmlType( "String" ), + m_multiValued( false ), + m_updatable( false ), + m_inherited( false ), + m_required( false ), + m_queryable( false ), + m_orderable( false ), + m_openChoice( false ), + m_temporary( false ) + { + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + string value( ( const char * ) content ); + xmlFree( content ); + + if ( xmlStrEqual( child->name, BAD_CAST( "id" ) ) ) + setId( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "localName" ) ) ) + setLocalName( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "localNamespace" ) ) ) + setLocalNamespace( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "displayName" ) ) ) + setDisplayName( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "queryName" ) ) ) + setQueryName( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "propertyType" ) ) ) + setTypeFromXml( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "cardinality" ) ) ) + setMultiValued( value == "multi" ); + else if ( xmlStrEqual( child->name, BAD_CAST( "updatability" ) ) ) + setUpdatable( value == "readwrite" ); + else if ( xmlStrEqual( child->name, BAD_CAST( "inherited" ) ) ) + setInherited( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "required" ) ) ) + setRequired( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "queryable" ) ) ) + setQueryable( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "orderable" ) ) ) + setOrderable( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "openChoice" ) ) ) + setOpenChoice( libcmis::parseBool( value ) ); + } + } + + PropertyType::PropertyType( const PropertyType& copy ) : + m_id ( copy.m_id ), + m_localName ( copy.m_localName ), + m_localNamespace ( copy.m_localNamespace ), + m_displayName ( copy.m_displayName ), + m_queryName ( copy.m_queryName ), + m_type ( copy.m_type ), + m_xmlType( copy.m_xmlType ), + m_multiValued ( copy.m_multiValued ), + m_updatable ( copy.m_updatable ), + m_inherited ( copy.m_inherited ), + m_required ( copy.m_required ), + m_queryable ( copy.m_queryable ), + m_orderable ( copy.m_orderable ), + m_openChoice ( copy.m_openChoice ), + m_temporary( copy.m_temporary ) + { + } + + PropertyType::PropertyType( string type, + string id, + string localName, + string displayName, + string queryName ) : + m_id ( id ), + m_localName ( localName ), + m_localNamespace ( ), + m_displayName ( displayName ), + m_queryName ( queryName ), + m_type ( ), + m_xmlType( type ), + m_multiValued( false ), + m_updatable( false ), + m_inherited( false ), + m_required( false ), + m_queryable( false ), + m_orderable( false ), + m_openChoice( false ), + m_temporary( true ) + { + setTypeFromXml( m_xmlType ); + } + + PropertyType& PropertyType::operator=( const PropertyType& copy ) + { + if ( this != © ) + { + m_id = copy.m_id; + m_localName = copy.m_localName; + m_localNamespace = copy.m_localNamespace; + m_displayName = copy.m_displayName; + m_queryName = copy.m_queryName; + m_type = copy.m_type; + m_xmlType = copy.m_xmlType; + m_multiValued = copy.m_multiValued; + m_updatable = copy.m_updatable; + m_inherited = copy.m_inherited; + m_required = copy.m_required; + m_queryable = copy.m_queryable; + m_orderable = copy.m_orderable; + m_openChoice = copy.m_openChoice; + m_temporary = copy.m_temporary; + } + + return *this; + } + + void PropertyType::setTypeFromJsonType( string jsonType ) + { + if ( jsonType == "json_bool" ) + m_type = Bool; + else if ( jsonType == "json_double" ) + m_type = Decimal; + else if ( jsonType == "json_int" ) + m_type = Integer; + else if ( jsonType == "json_datetime" ) + m_type = DateTime; + else m_type = String; + } + + void PropertyType::setTypeFromXml( string typeStr ) + { + // Default to string + m_xmlType = string( "String" ); + m_type = String; + + if ( typeStr == "datetime" ) + { + m_xmlType = string( "DateTime" ); + m_type = DateTime; + } + else if ( typeStr == "integer" ) + { + m_xmlType = string( "Integer" ); + m_type = Integer; + } + else if ( typeStr == "decimal" ) + { + m_xmlType = string( "Decimal" ); + m_type = Decimal; + } + else if ( typeStr == "boolean" ) + { + m_xmlType = string( "Boolean" ); + m_type = Bool; + } + // Special kinds of String + else if ( typeStr == "html" ) + m_xmlType = string( "Html" ); + else if ( typeStr == "id" ) + m_xmlType = string( "Id" ); + else if ( typeStr == "uri" ) + m_xmlType = string( "Uri" ); + } + + void PropertyType::update( vector< ObjectTypePtr > typesDefs ) + { + for ( vector< ObjectTypePtr >::iterator it = typesDefs.begin(); + it != typesDefs.end( ) && m_temporary; ++it ) + { + map< string, PropertyTypePtr >& propertyTypes = + ( *it )->getPropertiesTypes( ); + map< string, PropertyTypePtr >::iterator propIt = + propertyTypes.find( getId( ) ); + if ( propIt != propertyTypes.end() ) + { + PropertyTypePtr complete = propIt->second; + + m_localName = complete->m_localName; + m_localNamespace = complete->m_localNamespace; + m_displayName = complete->m_displayName; + m_queryName = complete->m_queryName; + m_type = complete->m_type; + m_xmlType = complete->m_xmlType; + m_multiValued = complete->m_multiValued; + m_updatable = complete->m_updatable; + m_inherited = complete->m_inherited; + m_required = complete->m_required; + m_queryable = complete->m_queryable; + m_orderable = complete->m_orderable; + m_openChoice = complete->m_openChoice; + m_temporary = false; + } + } + } +} diff --git a/src/libcmis/property.cxx b/src/libcmis/property.cxx new file mode 100644 index 0000000..41c181b --- /dev/null +++ b/src/libcmis/property.cxx @@ -0,0 +1,232 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/property.hxx> + +#include <boost/algorithm/string.hpp> + +#include <libcmis/object-type.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + Property::Property( ): + m_propertyType( ), + m_strValues( ), + m_boolValues( ), + m_longValues( ), + m_doubleValues( ), + m_dateTimeValues( ) + { + } + + Property::Property( PropertyTypePtr propertyType, std::vector< std::string > strValues ) : + m_propertyType( propertyType ), + m_strValues( ), + m_boolValues( ), + m_longValues( ), + m_doubleValues( ), + m_dateTimeValues( ) + { + setValues( strValues ); + } + + void Property::setValues( vector< string > strValues ) + { + m_strValues = strValues; + m_boolValues.clear( ); + m_longValues.clear( ); + m_doubleValues.clear( ); + m_dateTimeValues.clear( ); + + for ( vector< string >::iterator it = strValues.begin(); it != strValues.end( ); ++it ) + { + try + { + // If no PropertyType was provided at construction time, use String + PropertyType::Type type = PropertyType::String; + if ( getPropertyType( ) != NULL ) + type = getPropertyType( )->getType( ); + + switch ( type ) + { + case PropertyType::Integer: + m_longValues.push_back( parseInteger( *it ) ); + break; + case PropertyType::Decimal: + m_doubleValues.push_back( parseDouble( *it ) ); + break; + case PropertyType::Bool: + m_boolValues.push_back( parseBool( *it ) ); + break; + case PropertyType::DateTime: + { + boost::posix_time::ptime time = parseDateTime( *it ); + if ( !time.is_not_a_date_time( ) ) + m_dateTimeValues.push_back( time ); + } + break; + default: + case PropertyType::String: + // Nothing to convert for strings + break; + } + } + catch( const Exception& ) + { + // Just ignore the unparsable values + } + } + } + + void Property::setPropertyType( PropertyTypePtr propertyType) + { + m_propertyType = propertyType; + } + void Property::toXml( xmlTextWriterPtr writer ) + { + // Don't write the property if we have no type for it. + if ( getPropertyType( ) != NULL ) + { + string xmlType = string( "cmis:property" ) + getPropertyType()->getXmlType( ); + xmlTextWriterStartElement( writer, BAD_CAST( xmlType.c_str( ) ) ); + + // Write the attributes + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "propertyDefinitionId" ), + "%s", BAD_CAST( getPropertyType()->getId( ).c_str( ) ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "localName" ), + "%s", BAD_CAST( getPropertyType()->getLocalName( ).c_str( ) ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "displayName" ), + "%s", BAD_CAST( getPropertyType()->getDisplayName( ).c_str( ) ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "queryName" ), + "%s", BAD_CAST( getPropertyType()->getQueryName( ).c_str( ) ) ); + + // Write the values + for ( vector< string >::iterator it = m_strValues.begin( ); it != m_strValues.end( ); ++it ) + { + xmlTextWriterWriteElement( writer, BAD_CAST( "cmis:value" ), BAD_CAST( it->c_str( ) ) ); + } + + xmlTextWriterEndElement( writer ); + } + } + + string Property::toString( ) + { + string res; + if ( getPropertyType( ) != NULL ) + { + for ( vector< string >::iterator it = m_strValues.begin( ); + it != m_strValues.end( ); ++it ) + { + res.append( *it ); + } + } + return res; + } + + PropertyPtr parseProperty( xmlNodePtr node, ObjectTypePtr objectType ) + { + PropertyPtr property; + + if ( node != NULL ) + { + // Get the property definition Id + string propDefinitionId; + try + { + propDefinitionId = getXmlNodeAttributeValue( node, "propertyDefinitionId" ); + } + catch ( const Exception& ) + { + } + + // Try to get the property type definition + PropertyTypePtr propType; + if ( !propDefinitionId.empty() && objectType ) + { + map< string, PropertyTypePtr >::iterator it = objectType->getPropertiesTypes( ).find( propDefinitionId ); + if ( it != objectType->getPropertiesTypes().end( ) ) + propType = it->second; + } + + // Try to construct a temporary type definition + if ( !propDefinitionId.empty( ) && !propType ) + { + if ( node->name != NULL ) + { + string localName = getXmlNodeAttributeValue( node, + "localName", "" ); + string displayName = getXmlNodeAttributeValue( node, + "displayName", "" ); + string queryName = getXmlNodeAttributeValue( node, + "queryName", "" ); + + string xmlType( ( char * )node->name ); + string propStr( "property" ); + size_t pos = xmlType.find( propStr ); + if ( pos == 0 ) { + xmlType = xmlType.substr( propStr.length( ) ); + boost::to_lower( xmlType ); + } + + propType.reset( new PropertyType( xmlType, propDefinitionId, + localName, displayName, + queryName ) ); + } + } + + if ( propType ) + { + try + { + // Find the value nodes + vector< string > values; + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "value" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + values.push_back( string( ( char * ) content ) ); + xmlFree( content ); + } + } + property.reset( new Property( propType, values ) ); + } + catch ( const Exception& ) + { + // Ignore that non-property node + } + } + } + + return property; + } +} diff --git a/src/libcmis/rendition.cxx b/src/libcmis/rendition.cxx new file mode 100644 index 0000000..c68c053 --- /dev/null +++ b/src/libcmis/rendition.cxx @@ -0,0 +1,195 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/rendition.hxx> + +#include <sstream> + +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis{ + +Rendition::Rendition( ): + m_streamId( ), + m_mimeType( ), + m_kind( ), + m_href( ), + m_title( ), + m_length( -1 ), + m_width ( -1 ), + m_height( -1 ), + m_renditionDocumentId( ) +{ +} + +Rendition::Rendition( string streamId, string mimeType, + string kind, string href, string title, long length, + long width, long height, string renditionDocumentId ): + m_streamId( streamId ), + m_mimeType( mimeType ), + m_kind( kind ), + m_href( href ), + m_title( title ), + m_length( length ), + m_width ( width ), + m_height( height ), + m_renditionDocumentId( renditionDocumentId ) +{ +} + +Rendition::Rendition( xmlNodePtr node ): + m_streamId( ), + m_mimeType( ), + m_kind( ), + m_href( ), + m_title( ), + m_length( -1 ), + m_width ( -1 ), + m_height( -1 ), + m_renditionDocumentId( ) +{ + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char * ) content ); + xmlFree( content ); + + if ( xmlStrEqual( child->name, BAD_CAST( "streamId" ) ) ) + m_streamId = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "mimetype" ) ) ) + m_mimeType = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "length" ) ) ) + m_length = libcmis::parseInteger( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "kind" ) ) ) + m_kind = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "title" ) ) ) + m_title = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "height" ) ) ) + m_height = libcmis::parseInteger( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "width" ) ) ) + m_width = libcmis::parseInteger( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "renditionDocumentId" ) ) ) + m_renditionDocumentId = value; + } +} + +Rendition::~Rendition( ) +{ +} + +bool Rendition::isThumbnail( ) +{ + return m_kind == "cmis:thumbnail"; +} + + +const string& Rendition::getStreamId( ) const +{ + return m_streamId; +} + +const string& Rendition::getMimeType( ) const +{ + return m_mimeType; +} + +const string& Rendition::getKind( ) const +{ + return m_kind; +} + +const string& Rendition::getUrl( ) const +{ + return m_href; +} + +const string& Rendition::getTitle( ) const +{ + return m_title; +} + +long Rendition::getLength( ) const +{ + return m_length; +} + +long Rendition::getWidth( ) const +{ + return m_width; +} + +long Rendition::getHeight( ) const +{ + return m_height; +} + +const string& Rendition::getRenditionDocumentId( ) +{ + return m_renditionDocumentId; +} + + +// LCOV_EXCL_START +string Rendition::toString( ) +{ + stringstream buf; + + if ( !getStreamId( ).empty( ) ) + buf << " ID: " << getStreamId( ) << endl; + + if ( !getKind().empty() ) + buf << " Kind: " << getKind( ) << endl; + + if ( !getMimeType( ).empty() ) + buf << " MimeType: " << getMimeType( ) << endl; + + if ( !getUrl().empty( ) ) + buf << " URL: " << getUrl( ) << endl; + + if ( !getTitle().empty( ) ) + buf << " Title: " << getTitle( ) << endl; + + if ( getLength( ) >= 0 ) + buf << " Length: " << getLength( ) << endl; + + if ( getWidth( ) >= 0 ) + buf << " Width: " << getWidth( ) << endl; + + if ( getHeight( ) >= 0 ) + buf << " Height: " << getHeight( ) << endl; + + if ( !getRenditionDocumentId().empty( ) ) + buf << " Rendition Document ID: " << getRenditionDocumentId( ) << endl; + + return buf.str( ); +} +// LCOV_EXCL_STOP + +} diff --git a/src/libcmis/repository.cxx b/src/libcmis/repository.cxx new file mode 100644 index 0000000..0a7cbe6 --- /dev/null +++ b/src/libcmis/repository.cxx @@ -0,0 +1,294 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Cédric Bosdonnat <cbosdo@users.sourceforge.net> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/repository.hxx> + +#include <sstream> + +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + Repository::Repository( ) : + m_id( ), + m_name( ), + m_description( ), + m_vendorName( ), + m_productName( ), + m_productVersion( ), + m_rootId( ), + m_cmisVersionSupported( ), + m_thinClientUri( ), + m_principalAnonymous( ), + m_principalAnyone( ), + m_capabilities( ) + { + } + + Repository::Repository( xmlNodePtr node ) : + m_id( ), + m_name( ), + m_description( ), + m_vendorName( ), + m_productName( ), + m_productVersion( ), + m_rootId( ), + m_cmisVersionSupported( ), + m_thinClientUri( ), + m_principalAnonymous( ), + m_principalAnyone( ), + m_capabilities( ) + { + initializeFromNode( node ); + } + + string Repository::getId( ) const + { + return m_id; + } + + string Repository::getName( ) const + { + return m_name; + } + + string Repository::getDescription( ) const + { + return m_description; + } + + string Repository::getVendorName( ) const + { + return m_vendorName; + } + + string Repository::getProductName( ) const + { + return m_productName; + } + + string Repository::getProductVersion( ) const + { + return m_productVersion; + } + + string Repository::getRootId( ) const + { + return m_rootId; + } + + string Repository::getCmisVersionSupported( ) const + { + return m_cmisVersionSupported; + } + + boost::shared_ptr< string > Repository::getThinClientUri( ) const + { + return m_thinClientUri; + } + + boost::shared_ptr< string > Repository::getPrincipalAnonymous( ) const + { + return m_principalAnonymous; + } + + boost::shared_ptr< string > Repository::getPrincipalAnyone( ) const + { + return m_principalAnyone; + } + + + void Repository::initializeFromNode( xmlNodePtr node ) + { + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + string localName( ( char* ) child->name ); + + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char* )content ); + xmlFree( content ); + + if ( localName == "repositoryId" ) + m_id = value; + else if ( localName == "repositoryName" ) + m_name = value; + else if ( localName == "repositoryDescription" ) + m_description = value; + else if ( localName == "vendorName" ) + m_vendorName = value; + else if ( localName == "productName" ) + m_productName = value; + else if ( localName == "productVersion" ) + m_productVersion = value; + else if ( localName == "rootFolderId" ) + m_rootId = value; + else if ( localName == "cmisVersionSupported" ) + m_cmisVersionSupported = value; + else if ( localName == "thinClientURI" ) + m_thinClientUri.reset( new string( value ) ); + else if ( localName == "principalAnonymous" ) + m_principalAnonymous.reset( new string( value ) ); + else if ( localName == "principalAnyone" ) + m_principalAnyone.reset( new string( value ) ); + else if ( localName == "capabilities" ) + { + m_capabilities = parseCapabilities( child ); + } + } + } + + string Repository::getCapability( Capability capability ) const + { + string result; + + map< Capability, string >::const_iterator it = m_capabilities.find( capability ); + if ( it != m_capabilities.end() ) + result = it->second; + + return result; + } + + bool Repository::getCapabilityAsBool( Capability capability ) const + { + string value = getCapability( capability ); + bool result = false; + try + { + result = libcmis::parseBool( value ); + } + catch ( const Exception& ) + { + } + return result; + } + + // LCOV_EXCL_START + string Repository::toString( ) const + { + stringstream buf; + + buf << "Id: " << getId( ) << endl; + buf << "Name: " << getName( ) << endl; + buf << "Description: " << getDescription( ) << endl; + buf << "Vendor: " << getVendorName( ) << endl; + buf << "Product: " << getProductName( ) << " - version " << getProductVersion( ) << endl; + buf << "Root Id: " << getRootId( ) << endl; + buf << "Supported CMIS Version: " << getCmisVersionSupported( ) << endl; + if ( getThinClientUri( ) ) + buf << "Thin Client URI: " << *getThinClientUri( ) << endl; + if ( getPrincipalAnonymous( ) ) + buf << "Anonymous user: " << *getPrincipalAnonymous( ) << endl; + if ( getPrincipalAnyone( ) ) + buf << "Anyone user: " << *getPrincipalAnyone( ) << endl; + buf << endl; + buf << "Capabilities:" << endl; + + static string capabilitiesNames[] = + { + "ACL", + "AllVersionsSearchable", + "Changes", + "ContentStreamUpdatability", + "GetDescendants", + "GetFolderTree", + "OrderBy", + "Multifiling", + "PWCSearchable", + "PWCUpdatable", + "Query", + "Renditions", + "Unfiling", + "VersionSpecificFiling", + "Join" + }; + + for ( int i = ACL; i < Join; ++i ) + { + buf << "\t" << capabilitiesNames[i] << ": " << getCapability( ( Capability )i ) << endl; + } + + return buf.str(); + } + // LCOV_EXCL_STOP + + map< Repository::Capability, string > Repository::parseCapabilities( xmlNodePtr node ) + { + map< Capability, string > capabilities; + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + string localName( ( char* ) child->name ); + + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char* )content ); + xmlFree( content ); + + Capability capability = ACL; + bool ignore = false; + if ( localName == "capabilityACL" ) + capability = ACL; + else if ( localName == "capabilityAllVersionsSearchable" ) + capability = AllVersionsSearchable; + else if ( localName == "capabilityChanges" ) + capability = Changes; + else if ( localName == "capabilityContentStreamUpdatability" ) + capability = ContentStreamUpdatability; + else if ( localName == "capabilityGetDescendants" ) + capability = GetDescendants; + else if ( localName == "capabilityGetFolderTree" ) + capability = GetFolderTree; + else if ( localName == "capabilityOrderBy" ) + capability = OrderBy; + else if ( localName == "capabilityMultifiling" ) + capability = Multifiling; + else if ( localName == "capabilityPWCSearchable" ) + capability = PWCSearchable; + else if ( localName == "capabilityPWCUpdatable" ) + capability = PWCUpdatable; + else if ( localName == "capabilityQuery" ) + capability = Query; + else if ( localName == "capabilityRenditions" ) + capability = Renditions; + else if ( localName == "capabilityUnfiling" ) + capability = Unfiling; + else if ( localName == "capabilityVersionSpecificFiling" ) + capability = VersionSpecificFiling; + else if ( localName == "capabilityJoin" ) + capability = Join; + else + ignore = true; + + if ( !ignore ) + capabilities[capability] = value; + } + + return capabilities; + } +} diff --git a/src/libcmis/session-factory.cxx b/src/libcmis/session-factory.cxx new file mode 100644 index 0000000..47dc1c8 --- /dev/null +++ b/src/libcmis/session-factory.cxx @@ -0,0 +1,164 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/session-factory.hxx> + +#include "atom-session.hxx" +#include "gdrive-session.hxx" +#include "onedrive-session.hxx" +#include "sharepoint-session.hxx" +#include "ws-session.hxx" + +using namespace std; + +namespace libcmis +{ + CurlInitProtocolsFunction g_CurlInitProtocolsFunction = 0; + AuthProviderPtr SessionFactory::s_authProvider; + OAuth2AuthCodeProvider SessionFactory::s_oauth2AuthCodeProvider; + + string SessionFactory::s_proxy; + string SessionFactory::s_noProxy; + string SessionFactory::s_proxyUser; + string SessionFactory::s_proxyPass; + + CertValidationHandlerPtr SessionFactory::s_certValidationHandler; + + void SessionFactory::setCurlInitProtocolsFunction(CurlInitProtocolsFunction const initProtocols) + { + g_CurlInitProtocolsFunction = initProtocols; + } + + void SessionFactory::setProxySettings( string proxy, string noProxy, + string proxyUser, string proxyPass ) + { + SessionFactory::s_proxy = proxy; + SessionFactory::s_noProxy = noProxy; + SessionFactory::s_proxyUser = proxyUser; + SessionFactory::s_proxyPass = proxyPass; + } + + Session* SessionFactory::createSession( string bindingUrl, string username, + string password, string repository, bool noSslCheck, + libcmis::OAuth2DataPtr oauth2, bool verbose ) + { + Session* session = NULL; + + if ( !bindingUrl.empty( ) ) + { + // Try the special cases based on the binding URL + if ( bindingUrl == "https://www.googleapis.com/drive/v3" ) + { + session = new GDriveSession( bindingUrl, username, password, + oauth2, verbose ); + } + else if ( bindingUrl == "https://graph.microsoft.com/v1.0" ) + { + session = new OneDriveSession( bindingUrl, username, password, + oauth2, verbose); + } + else + { + libcmis::HttpResponsePtr response; + boost::shared_ptr< HttpSession> httpSession( + new HttpSession( username, password, + noSslCheck, oauth2, verbose, + g_CurlInitProtocolsFunction) ); + + try + { + response = httpSession->httpGetRequest( bindingUrl ); + } + catch ( const CurlException& ) + { + // Could be SharePoint - needs NTLM authentication + session = new SharePointSession( bindingUrl, username, + password, verbose ); + } + + // Try the CMIS cases: we need to autodetect the binding type + if ( session == NULL ) + { + try + { + session = new AtomPubSession( bindingUrl, repository, + *httpSession, response ); + } + catch ( const Exception& ) + { + } + } + + if ( session == NULL ) + { + // We couldn't get an AtomSession, we may have an URL for the WebService binding + try + { + session = new WSSession( bindingUrl, repository, + *httpSession, response ); + } + catch ( const Exception& ) + { + } + } + + if ( session == NULL ) + { + // Maybe the first request didn't throw an exception and the authentication + // succeeded so we need to double check for SharePoint + try + { + session = new SharePointSession( bindingUrl, + *httpSession, response ); + } + catch ( const Exception& ) + { + } + } + } + } + + return session; + } + + vector< RepositoryPtr > SessionFactory::getRepositories( string bindingUrl, + string username, string password, bool verbose ) + { + vector< RepositoryPtr > repos; + + Session* session = createSession( bindingUrl, username, password, + string(), false, OAuth2DataPtr(), verbose ); + if ( session != NULL ) + { + repos = session->getRepositories( ); + delete session; + } + + return repos; + } +} diff --git a/src/libcmis/sharepoint-allowable-actions.hxx b/src/libcmis/sharepoint-allowable-actions.hxx new file mode 100644 index 0000000..abc48fc --- /dev/null +++ b/src/libcmis/sharepoint-allowable-actions.hxx @@ -0,0 +1,102 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Varga Mihai <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_ALLOWABLE_ACTIONS_HXX_ +#define _SHAREPOINT_ALLOWABLE_ACTIONS_HXX_ + +#include <libcmis/allowable-actions.hxx> + +class SharePointAllowableActions: public libcmis::AllowableActions +{ + public: + SharePointAllowableActions( bool isFolder ) : AllowableActions( ) + { + m_states.clear( ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::UpdateProperties, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectRelationships, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectParents, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::MoveObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateRelationship, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyPolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAppliedPolicies, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemovePolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetACL, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyACL, false ) ); + + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderTree, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderParent, isFolder) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetDescendants, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CancelCheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckIn, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::SetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAllVersions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::AddObjectToFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemoveObjectFromFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetRenditions, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetChildren, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateDocument, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateFolder, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteTree, isFolder ) ); + } +}; + +#endif diff --git a/src/libcmis/sharepoint-document.cxx b/src/libcmis/sharepoint-document.cxx new file mode 100644 index 0000000..dec5527 --- /dev/null +++ b/src/libcmis/sharepoint-document.cxx @@ -0,0 +1,213 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-document.hxx" + +#include "sharepoint-session.hxx" +#include "sharepoint-utils.hxx" +#include "json-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointDocument::SharePointDocument( SharePointSession* session ) : + libcmis::Object( session), + libcmis::Document( session ), + SharePointObject( session ) +{ +} + +SharePointDocument::SharePointDocument( SharePointSession* session, Json json, string parentId, string name ) : + libcmis::Object( session), + libcmis::Document( session ), + SharePointObject( session, json, parentId, name ) +{ +} + +SharePointDocument::~SharePointDocument( ) +{ +} + +vector< libcmis::FolderPtr > SharePointDocument::getParents( ) +{ + vector< libcmis::FolderPtr > parents; + + string parentId = getStringProperty( "cmis:parentId" ); + + libcmis::ObjectPtr obj = getSession( )->getObject( parentId ); + libcmis::FolderPtr parent = boost::dynamic_pointer_cast< libcmis::Folder >( obj ); + parents.push_back( parent ); + return parents; +} + +boost::shared_ptr< istream > SharePointDocument::getContentStream( string /*streamId*/ ) +{ + boost::shared_ptr< istream > stream; + // file uri + /$value + string streamUrl = getId( ) + "/%24value"; + try + { + stream = getSession( )->httpGetRequest( streamUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return stream; +} + +void SharePointDocument::setContentStream( boost::shared_ptr< ostream > os, + string contentType, + string /*fileName*/, + bool /*overwrite*/ ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + // file uri + /$value + string putUrl = getId( ) + "/%24value"; + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector <string> headers; + headers.push_back( string( "Content-Type: " ) + contentType ); + try + { + getSession()->httpPutRequest( putUrl, *is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + { + throw libcmis::Exception( "Document content wasn't set for" + "some reason" ); + } + refresh( ); +} + +libcmis::DocumentPtr SharePointDocument::checkOut( ) +{ + istringstream is( "" ); + string url = getId( ) + "/checkout"; + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkout = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkout; +} + +void SharePointDocument::cancelCheckout( ) +{ + istringstream is( "" ); + string url = getId( ) + "/undocheckout"; + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +libcmis::DocumentPtr SharePointDocument::checkIn( bool isMajor, + std::string comment, + const PropertyPtrMap& /*properties*/, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ) +{ + setContentStream( stream, contentType, fileName ); + comment = libcmis::escape( comment ); + string url = getId( ) + "/checkin(comment='" + comment + "'"; + if ( isMajor ) + { + url += ",checkintype=1)"; + } + else + { + url += ",checkintype=0)"; + } + istringstream is( "" ); + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkin = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkin; +} + +vector< libcmis::DocumentPtr > SharePointDocument::getAllVersions( ) +{ + string res; + string url = getStringProperty( "Versions" ); + vector< libcmis::DocumentPtr > allVersions; + try + { + res = getSession( )->httpGetRequest( url )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // adding the latest version + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr doc = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + allVersions.push_back( doc ); + + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["d"]["results"].getList( ); + for ( unsigned int i = 0; i < objs.size( ); i++) + { + string versionNumber = objs[i]["ID"].toString( ); + string versionId = getId( ) + "/Versions(" + versionNumber + ")"; + obj = getSession( )->getObject( versionId ); + doc = boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + allVersions.push_back( doc ); + } + + return allVersions; +} diff --git a/src/libcmis/sharepoint-document.hxx b/src/libcmis/sharepoint-document.hxx new file mode 100644 index 0000000..8423eb0 --- /dev/null +++ b/src/libcmis/sharepoint-document.hxx @@ -0,0 +1,72 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_DOCUMENT_HXX_ +#define _SHAREPOINT_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "sharepoint-object.hxx" +#include "json-utils.hxx" + +class SharePointDocument : public libcmis::Document, public SharePointObject +{ + public: + SharePointDocument( SharePointSession* session ); + + SharePointDocument( SharePointSession* session, Json json, + std::string parentId = std::string( ), + std::string name = std::string( ) ); + ~SharePointDocument( ); + + std::string getType( ) { return std::string( "cmis:document" );} + std::string getBaseType( ) { return std::string( "cmis:document" );} + + virtual std::vector< libcmis::FolderPtr > getParents( ); + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName, + bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + virtual libcmis::DocumentPtr checkIn( bool isMajor, + std::string comment, + const std::map< std::string,libcmis::PropertyPtr >& + properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); +}; + +#endif diff --git a/src/libcmis/sharepoint-folder.cxx b/src/libcmis/sharepoint-folder.cxx new file mode 100644 index 0000000..e4da4a3 --- /dev/null +++ b/src/libcmis/sharepoint-folder.cxx @@ -0,0 +1,205 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-folder.hxx" + +#include "sharepoint-document.hxx" +#include "sharepoint-session.hxx" +#include "sharepoint-property.hxx" +#include "sharepoint-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointFolder::SharePointFolder( SharePointSession* session ): + libcmis::Object( session ), + libcmis::Folder( session ), + SharePointObject( session ) +{ +} + +SharePointFolder::SharePointFolder( SharePointSession* session, Json json, string parentId ): + libcmis::Object( session ), + libcmis::Folder( session ), + SharePointObject( session, json, parentId ) +{ +} + +SharePointFolder::~SharePointFolder( ) +{ +} + +string SharePointFolder::getParentId( ) +{ + string parentId = getStringProperty( "cmis:parentId" ); + if ( parentId.empty( ) ) + { + string parentUrl = getStringProperty( "ParentFolder" ); + string res; + try + { + res = getSession( )->httpGetRequest( parentUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + parentId = jsonRes["d"]["__metadata"]["uri"].toString( ); + PropertyPtr property; + property.reset( new SharePointProperty( "cmis:parentId", + Json( parentId.c_str( ) ) ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + return parentId; +} + +vector< libcmis::ObjectPtr > SharePointFolder::getChildren( ) +{ + vector< libcmis::ObjectPtr > children; + string filesUrl = getStringProperty( "Files" ); + string foldersUrl = getStringProperty( "Folders" ); + + Json::JsonVector objs = getChildrenImpl( filesUrl ); + Json::JsonVector folders = getChildrenImpl( foldersUrl ); + objs.insert( objs.begin( ), folders.begin( ), folders.end( ) ); + + for ( unsigned int i = 0; i < objs.size( ); i++) + { + children.push_back( getSession( )->getObjectFromJson( objs[i], getId( ) ) ); + } + return children; +} + +Json::JsonVector SharePointFolder::getChildrenImpl( string url ) +{ + string res; + try + { + res = getSession( )->httpGetRequest( url )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["d"]["results"].getList( ); + return objs; +} + +libcmis::FolderPtr SharePointFolder::createFolder( const PropertyPtrMap& properties ) +{ + string folderName; + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + if ( it->first == "cmis:name" ) + { + folderName = it->second->toString( ); + } + } + // bindingUrl/folders/add('/path/to/folder') + string relativeUrl; + if ( getStringProperty( "ServerRelativeUrl" ) == "/" ) + { + relativeUrl = "/" + folderName; + } + else + { + relativeUrl = getStringProperty( "ServerRelativeUrl" ) + "/" + folderName; + } + relativeUrl = libcmis::escape( relativeUrl ); + string folderUrl = getSession( )->getBindingUrl( ); + folderUrl += "/folders/add('" + relativeUrl + "')"; + + istringstream is( "" ); + string res; + try + { + res = getSession( )->httpPostRequest( folderUrl, is, "" )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + + libcmis::FolderPtr newFolder( new SharePointFolder( getSession( ), jsonRes, getId( ) ) ); + return newFolder; +} + +libcmis::DocumentPtr SharePointFolder::createDocument( const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, + string contentType, + string fileName ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + if ( fileName.empty( ) ) + { + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + if ( it->first == "cmis:name" || + it->first == "cmis:contentStreamFileName" ) + { + fileName = it->second->toString( ); + } + } + } + fileName = libcmis::escape( fileName ); + string url = getId( ) + "/files/add(overwrite=true,"; + url += "url='" + fileName + "')"; + + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + string res; + try + { + res = getSession( )->httpPostRequest( url, *is, contentType )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + DocumentPtr document( new SharePointDocument( getSession( ), jsonRes, getId( ) ) ); + return document; +} + +vector< string > SharePointFolder::removeTree( bool /*allVersions*/, + libcmis::UnfileObjects::Type /*unfile*/, + bool /*continueOnError*/ ) +{ + remove( ); + // Nothing to return here + return vector< string >( ); +} diff --git a/src/libcmis/sharepoint-folder.hxx b/src/libcmis/sharepoint-folder.hxx new file mode 100644 index 0000000..223ed60 --- /dev/null +++ b/src/libcmis/sharepoint-folder.hxx @@ -0,0 +1,67 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_FOLDER_HXX_ +#define _SHAREPOINT_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "sharepoint-object.hxx" +#include "json-utils.hxx" + +class SharePointFolder : public libcmis::Folder, public SharePointObject +{ + public: + SharePointFolder( SharePointSession* session ); + SharePointFolder( SharePointSession* session, + Json json, + std::string parentId = std::string( ) ); + ~SharePointFolder( ); + + std::string getType( ) { return std::string( "cmis:folder" );} + std::string getBaseType( ) { return std::string( "cmis:folder" );} + virtual std::string getParentId( ); + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + Json::JsonVector getChildrenImpl( std::string url ); + + virtual libcmis::FolderPtr createFolder( const libcmis::PropertyPtrMap& properties ); + + virtual libcmis::DocumentPtr createDocument( const libcmis::PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName ); + + virtual std::vector< std::string > removeTree( bool allVersion = true, + libcmis::UnfileObjects::Type + unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/sharepoint-object-type.cxx b/src/libcmis/sharepoint-object-type.cxx new file mode 100644 index 0000000..d6f405e --- /dev/null +++ b/src/libcmis/sharepoint-object-type.cxx @@ -0,0 +1,105 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-object-type.hxx" + +SharePointObjectType::SharePointObjectType( const std::string& id ): ObjectType( ) +{ + m_id = id; + m_localName = "SharePoint Object Type"; + m_localNamespace = "SharePoint Object Type"; + m_displayName = "SharePoint Object Type"; + m_queryName = "SharePoint Object Type"; + m_description = "SharePoint Object Type"; + m_parentTypeId = id; + m_baseTypeId = id; + m_creatable = true; + m_versionable = true; + m_fulltextIndexed = true; + + libcmis::PropertyTypePtr idType(new libcmis::PropertyType( ) ); + idType->setId( "cmis:objectTypeId" ); + idType->setType( libcmis::PropertyType::String ); + m_propertiesTypes[ idType->getId( ) ] = idType; + + // create PropertyTypes which are updatable. + + // name + libcmis::PropertyTypePtr nameType( new libcmis::PropertyType( ) ); + nameType->setId( "cmis:name" ); + nameType->setType( libcmis::PropertyType::String ); + nameType->setUpdatable( true ); + m_propertiesTypes[ nameType->getId( ) ] = nameType; + + // streamFileName + libcmis::PropertyTypePtr streamFileNameType( new libcmis::PropertyType( ) ); + streamFileNameType->setId( "cmis:contentStreamFileName" ); + streamFileNameType->setType( libcmis::PropertyType::String ); + streamFileNameType->setUpdatable( true ); + m_propertiesTypes[ streamFileNameType->getId( ) ] = streamFileNameType; + + // modifiedDate + libcmis::PropertyTypePtr modifiedDateType( new libcmis::PropertyType( ) ); + modifiedDateType->setId( "cmis:lastModificationDate" ); + modifiedDateType->setType( libcmis::PropertyType::DateTime ); + modifiedDateType->setUpdatable( false ); + m_propertiesTypes[ modifiedDateType->getId( ) ] = modifiedDateType; + + // creationDate + libcmis::PropertyTypePtr creationDateType( new libcmis::PropertyType( ) ); + creationDateType->setId( "cmis:creationDate" ); + creationDateType->setType( libcmis::PropertyType::DateTime ); + creationDateType->setUpdatable( false ); + m_propertiesTypes[ creationDateType->getId( ) ] = creationDateType; + + // size + libcmis::PropertyTypePtr contentStreamLength( new libcmis::PropertyType( ) ); + contentStreamLength->setId( "cmis:contentStreamLength" ); + contentStreamLength->setType( libcmis::PropertyType::Integer ); + contentStreamLength->setUpdatable( false ); + m_propertiesTypes[ contentStreamLength->getId( ) ] = contentStreamLength; + + // checkinComment + libcmis::PropertyTypePtr checkinComment( new libcmis::PropertyType( ) ); + checkinComment->setId( "cmis:checkinComment" ); + checkinComment->setType( libcmis::PropertyType::String ); + checkinComment->setUpdatable( false ); + m_propertiesTypes[ checkinComment->getId( ) ] = checkinComment; +} + +libcmis::ObjectTypePtr SharePointObjectType::getParentType( ) +{ + libcmis::ObjectTypePtr parentTypePtr( new SharePointObjectType( m_parentTypeId ) ); + return parentTypePtr; +} + +libcmis::ObjectTypePtr SharePointObjectType::getBaseType( ) +{ + libcmis::ObjectTypePtr baseTypePtr( new SharePointObjectType( m_baseTypeId ) ); + return baseTypePtr; +} diff --git a/src/libcmis/sharepoint-object-type.hxx b/src/libcmis/sharepoint-object-type.hxx new file mode 100644 index 0000000..8ea8e3d --- /dev/null +++ b/src/libcmis/sharepoint-object-type.hxx @@ -0,0 +1,46 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_OBJECT_TYPE_HXX_ +#define _SHAREPOINT_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "json-utils.hxx" + +class SharePointObjectType: public libcmis::ObjectType +{ + public: + SharePointObjectType( const std::string& id ); + + virtual libcmis::ObjectTypePtr getParentType( ); + + virtual libcmis::ObjectTypePtr getBaseType( ); +}; + +#endif diff --git a/src/libcmis/sharepoint-object.cxx b/src/libcmis/sharepoint-object.cxx new file mode 100644 index 0000000..1523205 --- /dev/null +++ b/src/libcmis/sharepoint-object.cxx @@ -0,0 +1,200 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-object.hxx" + +#include "sharepoint-allowable-actions.hxx" +#include "sharepoint-property.hxx" +#include "sharepoint-repository.hxx" +#include "sharepoint-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointObject::SharePointObject( SharePointSession* session ) : + libcmis::Object( session ) +{ +} + +SharePointObject::SharePointObject( SharePointSession* session, Json json, string parentId, string name ) : + libcmis::Object( session ) +{ + initializeFromJson( json, parentId, name ); +} + +SharePointObject::SharePointObject( const SharePointObject& copy ) : + libcmis::Object( copy ) +{ +} + +SharePointObject& SharePointObject::operator=( const SharePointObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + return *this; +} + +void SharePointObject::initializeFromJson ( Json json, string parentId, string /*name*/ ) +{ + if ( !json["d"].toString( ).empty( ) ) { + // Basic GET requests receive the data inside a "d" object, + // but child listing doesn't, so this unifies the representation + json = json["d"]; + } + Json::JsonObject objs = json.getObjects( ); + Json::JsonObject::iterator it; + PropertyPtr property; + bool isFolder = json["__metadata"]["type"].toString( ) == "SP.Folder"; + for ( it = objs.begin( ); it != objs.end( ); ++it) + { + property.reset( new SharePointProperty( it->first, it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + if ( it->first == "Name" && !isFolder ) + { + property.reset( new SharePointProperty( "cmis:contentStreamFileName", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + } + + if ( !parentId.empty( ) ) + { + // ParentId is not provided in the response + property.reset( new SharePointProperty( "cmis:parentId", Json( parentId.c_str( ) ) ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + + if ( !isFolder ) + { + string authorUrl = getStringProperty( "Author" ); + if ( authorUrl.empty( ) ) + { + // it's a file version + authorUrl = getStringProperty( "CreatedBy" ); + } + Json authorJson = getSession( )->getJsonFromUrl( authorUrl ); + property.reset( new SharePointProperty( "cmis:createdBy", + authorJson["d"]["Title"] ) ); + m_properties[ property->getPropertyType( )->getId( ) ] = property; + } + else + { + // we need to get the creation and lastUpdate time which aren't + // provided in the response + Json propJson = getSession( )->getJsonFromUrl( getStringProperty( "Properties" ) ); + property.reset( new SharePointProperty( "cmis:creationDate", + propJson["d"]["vti_x005f_timecreated"] ) ); + m_properties[ property->getPropertyType( )->getId( ) ] = property; + + property.reset( new SharePointProperty( "cmis:lastModificationDate", + propJson["d"]["vti_x005f_timelastmodified"] ) ); + m_properties[ property->getPropertyType( )->getId( ) ] = property; + } + + m_refreshTimestamp = time( NULL ); + m_allowableActions.reset( new SharePointAllowableActions( isFolder ) ); +} + +SharePointSession* SharePointObject::getSession( ) +{ + return dynamic_cast< SharePointSession* > ( m_session ); +} + +void SharePointObject::refreshImpl( Json json ) +{ + m_typeDescription.reset( ); + m_properties.clear( ); + initializeFromJson( json ); +} + +void SharePointObject::refresh( ) +{ + string res; + try + { + res = getSession( )->httpGetRequest( getId( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json json = Json::parse( res ); + refreshImpl( json ); +} + +void SharePointObject::remove( bool /*allVersions*/ ) +{ + try + { + getSession( )->httpDeleteRequest( getId( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +vector< string> SharePointObject::getMultiStringProperty( const string& propertyName ) +{ + vector< string > values; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + values = it->second->getStrings( ); + return values; +} + +libcmis::ObjectPtr SharePointObject::updateProperties( + const PropertyPtrMap& /*properties*/ ) +{ + // there are no updateable properties so just return the same object + libcmis::ObjectPtr updated = getSession( )->getObject( getId( ) ); + return updated; +} + +void SharePointObject::move( FolderPtr /*source*/, FolderPtr destination ) +{ + if ( !getStringProperty( "cmis:checkinComment" ).empty( ) ) + { + // only documents can be moved and only documents have this property + string url = getId( ) + "/moveto(newurl='"; + url += libcmis::escape( destination->getStringProperty( "ServerRelativeUrl" ) ); + url += "/" + getStringProperty( "cmis:name" ) + "'"; + // overwrite flag + url += ",flags=1)"; + istringstream is( "" ); + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + refresh( ); + } +} diff --git a/src/libcmis/sharepoint-object.hxx b/src/libcmis/sharepoint-object.hxx new file mode 100644 index 0000000..ace581b --- /dev/null +++ b/src/libcmis/sharepoint-object.hxx @@ -0,0 +1,74 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_OBJECT_HXX_ +#define _SHAREPOINT_OBJECT_HXX_ + +#include <libcmis/object.hxx> + +#include "sharepoint-session.hxx" +#include "json-utils.hxx" + +// Class representing an object for SharePoint protocol. +class SharePointObject : public virtual libcmis::Object +{ + public: + SharePointObject( SharePointSession* session ); + + // Create a SharePoint document from Json properties. + SharePointObject( SharePointSession* session, Json json, + std::string parentId = std::string( ), + std::string name = std::string( ) ); + SharePointObject( const SharePointObject& copy ); + virtual ~SharePointObject( ) { } + + SharePointObject& operator=( const SharePointObject& copy ); + + void initializeFromJson( Json json, std::string parentId = std::string( ), + std::string name = std::string( ) ); + + void refreshImpl( Json json ); + virtual void refresh( ); + virtual void remove( bool allVersions = true ); + + std::vector< std::string > getMultiStringProperty( + const std::string& propertyName ); + + virtual boost::shared_ptr< Object > updateProperties( + const libcmis::PropertyPtrMap& properties ); + + virtual std::vector< libcmis::RenditionPtr> getRenditions( std::string /*filter = std::string( )*/ ) + {return std::vector< libcmis::RenditionPtr>( );} + + virtual void move( boost::shared_ptr< libcmis::Folder > source, + boost::shared_ptr< libcmis::Folder > destination ); + + protected: + SharePointSession* getSession( ); + +}; +#endif diff --git a/src/libcmis/sharepoint-property.cxx b/src/libcmis/sharepoint-property.cxx new file mode 100644 index 0000000..dfca7fe --- /dev/null +++ b/src/libcmis/sharepoint-property.cxx @@ -0,0 +1,79 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-property.hxx" + +#include <libcmis/property-type.hxx> + +#include "sharepoint-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointProperty::SharePointProperty( ) +{ +} + +SharePointProperty::~SharePointProperty( ) +{ +} + +SharePointProperty::SharePointProperty( const string& key, Json json ): + Property( ) +{ + PropertyTypePtr propertyType( new PropertyType( ) ); + string convertedKey = SharePointUtils::toCmisKey( key ); + propertyType->setId( convertedKey ); + propertyType->setLocalName( convertedKey ); + propertyType->setLocalNamespace( convertedKey ); + propertyType->setQueryName( convertedKey ); + propertyType->setDisplayName( key ); + propertyType->setTypeFromJsonType( json.getStrType( ) ); + propertyType->setUpdatable( false ); + propertyType->setMultiValued( false ); + propertyType->setType( SharePointUtils::getPropertyType( convertedKey ) ); + + setPropertyType( propertyType ); + + vector< string > values = SharePointUtils::parseSharePointProperty( key, json ); + setValues( values ); +} + +SharePointProperty::SharePointProperty( const SharePointProperty& copy ) : + libcmis::Property( copy ) +{ +} + +SharePointProperty& SharePointProperty::operator=( const SharePointProperty& copy ) +{ + if ( this != © ) + { + libcmis::Property::operator=( copy ); + } + return *this; +} diff --git a/src/libcmis/sharepoint-property.hxx b/src/libcmis/sharepoint-property.hxx new file mode 100644 index 0000000..2649be4 --- /dev/null +++ b/src/libcmis/sharepoint-property.hxx @@ -0,0 +1,50 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_PROPERTY_HXX_ +#define _SHAREPOINT_PROPERTY_HXX_ + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +// reference: http://msdn.microsoft.com/en-us/library/hh243648.aspx +class SharePointProperty : public libcmis::Property +{ + public : + // Create a SharePoint Property from a Json property with its key + SharePointProperty( const std::string& key, Json json); + ~SharePointProperty( ); + SharePointProperty( const SharePointProperty& copy); + SharePointProperty& operator=( const SharePointProperty& copy ); + + private : + // Avoid calling default constructor + SharePointProperty( ); +}; +#endif diff --git a/src/libcmis/sharepoint-repository.cxx b/src/libcmis/sharepoint-repository.cxx new file mode 100644 index 0000000..f992689 --- /dev/null +++ b/src/libcmis/sharepoint-repository.cxx @@ -0,0 +1,65 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#include "sharepoint-repository.hxx" + +SharePointRepository::SharePointRepository( std::string baseUrl ) : + Repository( ) +{ + m_id = "SharePoint"; + m_name = "SharePoint"; + m_description = "SharePoint repository"; + m_productName = "SharePoint"; + m_productVersion = "2010/2013"; + // getFolderByServerRelativeUrl() API expects path to be + // *server-relative*, i.e. they must include site path. + // Given the baseUrl like "https://sp2013/sites/mysite/_api/Web" + // for a site "mysite" on sharepoint server "sp2013", + // the site root is '/sites/mysite/', not '/'. + // Trying to get folder '/' results in "Value does not fall + // within expected range" error. + // Preferrable here is to extract the root path from baseUrl, + // stripping server and api parts. But it can be unreliable + // if api part (_api/Web) is different for some server. + // On the other side, just querying empty path '' gives the root folder. + m_rootId = baseUrl + "/getFolderByServerRelativeUrl('')"; + + m_capabilities[ ACL ] = "discover"; + m_capabilities[ AllVersionsSearchable ] = "true"; + m_capabilities[ Changes ] = "all"; + m_capabilities[ GetDescendants ] = "true"; + m_capabilities[ GetFolderTree ] = "true"; + m_capabilities[ OrderBy ] = "custom"; + m_capabilities[ Multifiling ] = "true"; + m_capabilities[ PWCSearchable ] = "true"; + m_capabilities[ PWCUpdatable ] = "true"; + m_capabilities[ Query ] = "bothcombined"; + m_capabilities[ Renditions ] = "read"; + m_capabilities[ Unfiling ] = "false"; + m_capabilities[ VersionSpecificFiling ] = "false"; + m_capabilities[ Join ] = "none"; +} diff --git a/src/libcmis/sharepoint-repository.hxx b/src/libcmis/sharepoint-repository.hxx new file mode 100644 index 0000000..abf60c8 --- /dev/null +++ b/src/libcmis/sharepoint-repository.hxx @@ -0,0 +1,39 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_REPOSITORY_HXX_ +#define _SHAREPOINT_REPOSITORY_HXX_ + +#include <libcmis/repository.hxx> + +class SharePointRepository: public libcmis::Repository +{ + public: + SharePointRepository( std::string baseUrl ); +}; + +#endif diff --git a/src/libcmis/sharepoint-session.cxx b/src/libcmis/sharepoint-session.cxx new file mode 100644 index 0000000..d33bae8 --- /dev/null +++ b/src/libcmis/sharepoint-session.cxx @@ -0,0 +1,424 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-session.hxx" + +#include <libcmis/session-factory.hxx> + +#include "sharepoint-document.hxx" +#include "sharepoint-folder.hxx" +#include "sharepoint-object.hxx" +#include "sharepoint-object-type.hxx" +#include "sharepoint-repository.hxx" +#include "sharepoint-utils.hxx" + +using namespace std; + +SharePointSession::SharePointSession ( string baseUrl, + string username, + string password, + bool verbose ) : + BaseSession( baseUrl, string(), username, password, false, + libcmis::OAuth2DataPtr(), verbose ), + m_digestCode( string( ) ) + +{ + setAuthMethod( CURLAUTH_NTLM ); + libcmis::HttpResponsePtr response; + try + { + response = httpGetRequest( baseUrl + "/currentuser" ); + } + catch ( const CurlException& e ) + { + // It's not SharePoint or wrong username/passwd provided + throw e.getCmisException( ); + } + + // Add the dummy repository + m_repositories.push_back( getRepository( ) ); + fetchDigestCode( ); +} + +SharePointSession::SharePointSession( string baseUrl, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ) : + BaseSession( baseUrl, string(), httpSession ), + m_digestCode( string( ) ) +{ + if ( !SharePointUtils::isSharePoint( response->getStream( )->str( ) ) ) + { + throw libcmis::Exception( "Not a SharePoint service" ); + } + // Add the dummy repository + m_repositories.push_back( getRepository( ) ); + fetchDigestCode( ); +} + +SharePointSession::SharePointSession() : + BaseSession(), m_digestCode( string( ) ) +{ +} + +SharePointSession::~SharePointSession() +{ +} + +bool SharePointSession::setRepository( string ) +{ + return true; +} + +libcmis::RepositoryPtr SharePointSession::getRepository( ) +{ + // Return a dummy repository since SharePoint doesn't have that notion + libcmis::RepositoryPtr repo( new SharePointRepository( getBindingUrl( ) ) ); + return repo; +} + +libcmis::ObjectPtr SharePointSession::getObject( string objectId ) +{ + // objectId is uri for the file + string res; + try + { + res = httpGetRequest( objectId )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + return getObjectFromJson( jsonRes ); +} + +libcmis::ObjectPtr SharePointSession::getObjectFromJson( Json& jsonRes, string parentId ) +{ + libcmis::ObjectPtr object; + if ( !jsonRes["d"].toString( ).empty( ) ) { + jsonRes = jsonRes["d"]; + } + string kind = jsonRes["__metadata"]["type"].toString( ); + // only SharePointObject available for now + if ( kind == "SP.Folder" ) + { + object.reset( new SharePointFolder( this, jsonRes, parentId ) ); + } + else if ( kind == "SP.File" || kind == "SP.FileVersion" ) + { + object.reset( new SharePointDocument( this, jsonRes, parentId ) ); + } + else + { + object.reset( new SharePointObject( this, jsonRes, parentId ) ); + } + return object; +} + +libcmis::ObjectPtr SharePointSession::getObjectByPath( string path ) +{ + libcmis::ObjectPtr object; + path = libcmis::escape( path ); + // we don't know the object type so we try with Folder first + try + { + string folderUrl = getBindingUrl( ) + "/getFolderByServerRelativeUrl"; + folderUrl += "('" + path + "')"; + object = getObject( folderUrl ); + } + catch ( const libcmis::Exception &e ) + { + // it's not a Folder, maybe it's a File + string fileUrl = getBindingUrl( ) + "/getFileByServerRelativeUrl"; + fileUrl += "('" + path + "')"; + object = getObject( fileUrl ); + } + return object; +} + +libcmis::ObjectTypePtr SharePointSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new SharePointObjectType( id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > SharePointSession::getBaseTypes( ) +{ + vector< libcmis::ObjectTypePtr > types; + return types; +} + +Json SharePointSession::getJsonFromUrl( string url ) +{ + string response; + try + { + response = httpGetRequest( url )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return Json::parse( response ); +} + +/* Overwriting HttpSession::httpRunRequest to add the "accept:application/json" header */ +void SharePointSession::httpRunRequest( string url, vector< string > headers, bool redirect ) +{ + // Redirect + curl_easy_setopt( m_curlHandle, CURLOPT_FOLLOWLOCATION, redirect); + + // Activate the cookie engine + curl_easy_setopt( m_curlHandle, CURLOPT_COOKIEFILE, "" ); + + // Grab something from the web + curl_easy_setopt( m_curlHandle, CURLOPT_URL, url.c_str() ); + + // Set the headers + struct curl_slist *headers_slist = NULL; + for ( vector< string >::iterator it = headers.begin( ); it != headers.end( ); ++it ) + headers_slist = curl_slist_append( headers_slist, it->c_str( ) ); + + headers_slist = curl_slist_append( headers_slist, "accept:application/json; odata=verbose" ); + headers_slist = curl_slist_append( headers_slist, ( "x-requestdigest:" + m_digestCode ).c_str( ) ); + + if ( !getUsername().empty() && !getPassword().empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPAUTH, m_authMethod ); +#if LIBCURL_VERSION_VALUE >= 0x071301 + curl_easy_setopt( m_curlHandle, CURLOPT_USERNAME, getUsername().c_str() ); + curl_easy_setopt( m_curlHandle, CURLOPT_PASSWORD, getPassword().c_str() ); +#else + string userpwd = getUsername() + ":" + getPassword(); + curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, userpwd.c_str( ) ); +#endif + } + + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, headers_slist ); + + // Set the proxy configuration if any + if ( !libcmis::SessionFactory::getProxy( ).empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, libcmis::SessionFactory::getProxy( ).c_str() ); +#if LIBCURL_VERSION_VALUE >= 0x071304 + curl_easy_setopt( m_curlHandle, CURLOPT_NOPROXY, libcmis::SessionFactory::getNoProxy( ).c_str() ); +#endif + const string& proxyUser = libcmis::SessionFactory::getProxyUser( ); + const string& proxyPass = libcmis::SessionFactory::getProxyPass( ); + if ( !proxyUser.empty( ) && !proxyPass.empty( ) ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERNAME, proxyUser.c_str( ) ); + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYPASSWORD, proxyPass.c_str( ) ); +#else + string userpwd = proxyUser + ":" + proxyPass; + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, userpwd.c_str( ) ); +#endif + } + } + + // Get some feedback when something wrong happens + char errBuff[CURL_ERROR_SIZE]; + errBuff[0] = 0; + curl_easy_setopt( m_curlHandle, CURLOPT_ERRORBUFFER, errBuff ); + + // We want to get the response even if there is an Http error + if ( !m_noHttpErrors ) + curl_easy_setopt( m_curlHandle, CURLOPT_FAILONERROR, 1 ); + + if ( m_verbose ) + curl_easy_setopt( m_curlHandle, CURLOPT_VERBOSE, 1 ); + + // We want to get the certificate infos in error cases +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_CERTINFO, 1 ); +#endif + + if ( m_noSSLCheck ) + { +#if LIBCURL_VERSION_VALUE >= 0x070801 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); +#endif +#if LIBCURL_VERSION_VALUE >= 0x070402 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); +#endif + } + + // Perform the query + CURLcode errCode = curl_easy_perform( m_curlHandle ); + + // Free the headers list + curl_slist_free_all( headers_slist ); + + // Process the response + bool isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + if ( CURLE_OK != errCode && !( m_noHttpErrors && isHttpError ) ) + { + long httpError = 0; + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + + bool errorFixed = false; +#if LIBCURL_VERSION_VALUE >= 0X071301 + // If we had a bad certificate, then try to get more details + if ( CURLE_SSL_CACERT == errCode ) + { + vector< string > certificates; + + // We somehow need to rerun the request to get the certificate + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); + errCode = curl_easy_perform( m_curlHandle ); + + union { + struct curl_slist *to_info; + struct curl_certinfo *to_certinfo; + } ptr; + + ptr.to_info = NULL; + + CURLcode res = curl_easy_getinfo(m_curlHandle, CURLINFO_CERTINFO, &ptr.to_info); + + if ( !res && ptr.to_info ) + { + // We need the first certificate in the chain only + if ( ptr.to_certinfo->num_of_certs > 0 ) + { + struct curl_slist *slist; + + string certStart( "Cert:" ); + for ( slist = ptr.to_certinfo->certinfo[0]; slist; slist = slist->next ) + { + string data( slist->data ); + size_t startPos = data.find( certStart ); + if ( startPos == 0 ) + { + startPos = certStart.length(); + data = data.substr( startPos ); + certificates.push_back( data ); + } + } + } + } + + if ( !certificates.empty() ) + { + libcmis::CertValidationHandlerPtr validationHandler = + libcmis::SessionFactory::getCertificateValidationHandler( ); + bool ignoreCert = validationHandler && validationHandler->validateCertificate( certificates ); + if ( ignoreCert ) + { + m_noSSLCheck = true; + + isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + errorFixed = ( CURLE_OK == errCode || ( m_noHttpErrors && isHttpError ) ); + if ( !errorFixed ) + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + } + else + { + throw CurlException( "Invalid SSL certificate" ); + } + } + } +#endif + + if ( !errorFixed ) + throw CurlException( string( errBuff ), errCode, url, httpError ); + } +} + +libcmis::HttpResponsePtr SharePointSession::httpPutRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ) +{ + libcmis::HttpResponsePtr response; + try + { + response = HttpSession::httpPutRequest( url, is, headers ); + } + catch ( const CurlException& e ) + { + fetchDigestCodeCurl( ); + response = HttpSession::httpPutRequest( url, is, headers ); + } + return response; +} + +libcmis::HttpResponsePtr SharePointSession::httpPostRequest( const std::string& url, + std::istream& is, + const std::string& contentType, + bool redirect ) +{ + libcmis::HttpResponsePtr response; + try + { + response = HttpSession::httpPostRequest( url, is, contentType, redirect ); + } + catch ( const CurlException& e ) + { + fetchDigestCodeCurl( ); + response = HttpSession::httpPostRequest( url, is, contentType, redirect ); + } + return response; +} + +void SharePointSession::httpDeleteRequest( std::string url ) +{ + try + { + HttpSession::httpDeleteRequest( url ); + } + catch ( const CurlException& e ) + { + fetchDigestCodeCurl( ); + HttpSession::httpDeleteRequest( url ); + } +} + +void SharePointSession::fetchDigestCode( ) +try +{ + fetchDigestCodeCurl( ); +} +catch ( const CurlException& e ) +{ + throw e.getCmisException( ); +} + +void SharePointSession::fetchDigestCodeCurl( ) +{ + istringstream is( "empty" ); + libcmis::HttpResponsePtr response; + // url = http://host/_api/contextinfo, first we remove the '/web' part + string url = m_bindingUrl.substr( 0, m_bindingUrl.size( ) - 4 ) + "/contextinfo"; + response = HttpSession::httpPostRequest( url, is, "" ); + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + m_digestCode = jsonRes["d"]["GetContextWebInformation"]["FormDigestValue"].toString( ); +} diff --git a/src/libcmis/sharepoint-session.hxx b/src/libcmis/sharepoint-session.hxx new file mode 100644 index 0000000..2d47cb9 --- /dev/null +++ b/src/libcmis/sharepoint-session.hxx @@ -0,0 +1,91 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_SESSION_HXX_ +#define _SHAREPOINT_SESSION_HXX_ + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" +#include "json-utils.hxx" + +class SharePointSession : public BaseSession +{ + public: + SharePointSession( std::string baseUrl, + std::string username, + std::string password, + bool verbose = false ); + + SharePointSession( std::string baseUrl, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ); + + ~SharePointSession ( ); + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + libcmis::ObjectPtr getObjectFromJson( Json& jsonRes, + std::string parentId = std::string( ) ); + + Json getJsonFromUrl( std::string url ); + + void fetchDigestCode( ); + + void httpRunRequest( std::string url, + std::vector< std::string > headers, + bool redirect ); + + libcmis::HttpResponsePtr httpPutRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ); + libcmis::HttpResponsePtr httpPostRequest( const std::string& url, + std::istream& is, + const std::string& contentType, + bool redirect = true ); + void httpDeleteRequest( std::string url ); + + + private: + SharePointSession( ); + SharePointSession( const SharePointSession& copy ) = delete; + SharePointSession& operator=( const SharePointSession& copy ) = delete; + void fetchDigestCodeCurl( ); + std::string m_digestCode; +}; + +#endif /* _SHAREPONT_SESSION_HXX_ */ diff --git a/src/libcmis/sharepoint-utils.cxx b/src/libcmis/sharepoint-utils.cxx new file mode 100644 index 0000000..35a9f87 --- /dev/null +++ b/src/libcmis/sharepoint-utils.cxx @@ -0,0 +1,133 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-utils.hxx" + +#include <boost/shared_ptr.hpp> + +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +string SharePointUtils::toCmisKey( const string& key ) +{ + string convertedKey; + if ( key == "__metadata") + convertedKey = "cmis:objectId"; + else if ( key == "CheckInComment" ) + convertedKey = "cmis:checkinComment"; + else if ( key == "TimeCreated" ) + convertedKey = "cmis:creationDate"; + else if ( key == "TimeLastModified" || + key == "Created" ) + convertedKey = "cmis:lastModificationDate"; + else if ( key == "Name" ) + convertedKey = "cmis:name"; + else if ( key == "CheckOutType" ) + convertedKey = "cmis:isVersionSeriesCheckedOut"; + else if ( key == "UIVersionLabel" || + key == "VersionLabel" ) + convertedKey = "cmis:versionLabel"; + else if ( key == "Length" || + key == "Size" ) + convertedKey = "cmis:contentStreamLength"; + else convertedKey = key; + return convertedKey; +} + +libcmis::PropertyType::Type SharePointUtils::getPropertyType( const string& key ) +{ + libcmis::PropertyType::Type propertyType; + if ( key == "cmis:creationDate" || + key == "cmis:lastModificationDate" ) + { + propertyType = libcmis::PropertyType::DateTime; + } + else if ( key == "cmis:contentStreamLength" ) + { + propertyType = libcmis::PropertyType::Integer; + } + else if ( key == "cmis:isVersionSeriesCheckedOut" ) + { + propertyType = libcmis::PropertyType::Bool; + } + else + { + propertyType = libcmis::PropertyType::String; + } + return propertyType; +} + +vector< string > SharePointUtils::parseSharePointProperty( string key, Json json ) +{ + vector< string > values; + if ( key == "__metadata" ) + { + string id = json["uri"].toString( ); + values.push_back( id ); + } + if ( key == "Author" || + key == "CheckedOutByUser" || + key == "CreatedBy" || + key == "Files" || + key == "Folders" || + key == "ListItemAllFields" || + key == "LockedByUser" || + key == "ModifiedBy" || + key == "ParentFolder" || + key == "Properties" || + key == "Versions" ) + { + string propertyUri = json["__deferred"]["uri"].toString( ); + values.push_back( propertyUri ); + } + if ( key == "CheckOutType" ) + { + // Online = 0, Offline = 1, None = 2 + if ( json.toString( ) == "2" ) + { + values.push_back( "false" ); + } + else + { + values.push_back( "true" ); + } + } + else values.push_back( json.toString( ) ); + return values; +} + +bool SharePointUtils::isSharePoint( string response ) +{ + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( response.c_str( ), response.size( ), "noname.xml", NULL, 0 ), xmlFreeDoc ); + const boost::shared_ptr< xmlXPathContext > xpath( xmlXPathNewContext( doc.get() ), xmlXPathFreeContext ); + return "SP.Web" == libcmis::getXPathValue( xpath.get(), "//@term" ); +} diff --git a/src/libcmis/sharepoint-utils.hxx b/src/libcmis/sharepoint-utils.hxx new file mode 100644 index 0000000..80a5e51 --- /dev/null +++ b/src/libcmis/sharepoint-utils.hxx @@ -0,0 +1,54 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_UTILS_HXX_ +#define _SHAREPOINT_UTILS_HXX_ + +#include <string> + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +class SharePointUtils +{ + public : + + // Convert a SharePoint Property key to a CMIS key + static std::string toCmisKey( const std::string& key); + + // Returns the property type (String/Bool/Integer etc ) + static libcmis::PropertyType::Type getPropertyType( const std::string& key ); + + // Parse a SharePoint property value to CMIS values + static std::vector< std::string > parseSharePointProperty( std::string key, Json jsonValue ); + + // Checks if a response came from a SharePoint service + static bool isSharePoint( std::string response ); +}; + +#endif diff --git a/src/libcmis/ws-document.cxx b/src/libcmis/ws-document.cxx new file mode 100644 index 0000000..ba2f2bc --- /dev/null +++ b/src/libcmis/ws-document.cxx @@ -0,0 +1,135 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-document.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +WSDocument::WSDocument( const WSObject& object ) : + libcmis::Object( object ), + libcmis::Document( const_cast< WSObject& >( object ).getSession( ) ), + WSObject( object ) +{ +} + +WSDocument::~WSDocument( ) +{ +} + +vector< libcmis::FolderPtr > WSDocument::getParents( ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getNavigationService( ).getObjectParents( repoId, getId( ) ); +} + +boost::shared_ptr< istream > WSDocument::getContentStream( std::string /* streamId */ ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).getContentStream( repoId, getId( ) ); +} + +void WSDocument::setContentStream( boost::shared_ptr< ostream > os, string contentType, + string fileName, bool overwrite ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getObjectService( ).setContentStream( repoId, getId( ), + overwrite, getChangeToken( ), os, contentType, fileName ); + + refresh( ); +} + +libcmis::DocumentPtr WSDocument::checkOut( ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getVersioningService( ).checkOut( repoId, getId( ) ); +} + +void WSDocument::cancelCheckout( ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getVersioningService( ).cancelCheckOut( repoId, getId( ) ); +} + +libcmis::DocumentPtr WSDocument::checkIn( bool isMajor, string comment, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > stream, + string contentType, string fileName ) +{ + string repoId = getSession( )->getRepositoryId( ); + libcmis::DocumentPtr newVersion; + + // Try the normal request first, but if we have a server error, we may want to resend it + // without the stream as SharePoint wants no stream in the request, but gets the one from + // the PWC see the following discussion: + // http://social.technet.microsoft.com/Forums/eu/sharepoint2010programming/thread/b30e4d82-5b7e-4ceb-b9ad-c6f0d4c59d11 + bool tryNoStream = false; + try + { + newVersion = getSession( )->getVersioningService( ).checkIn( repoId, getId( ), + isMajor, properties, stream, contentType, fileName, comment ); + } + catch ( const libcmis::Exception& e ) + { + string spError( "Object reference not set to an instance of an object" ); + if ( string( e.what( ) ).find( spError ) != string::npos ) + tryNoStream = true; + else + throw; + } + + if ( tryNoStream ) + { + // Set the content stream first + setContentStream( stream, contentType, fileName ); + + // Then check-in + boost::shared_ptr< ostream > nostream; + newVersion = getSession( )->getVersioningService( ).checkIn( repoId, getId( ), + isMajor, properties, nostream, string( ), string( ), comment ); + } + + if ( newVersion->getId( ) == getId( ) ) + refresh( ); + + return newVersion; +} + +vector< libcmis::DocumentPtr > WSDocument::getAllVersions( ) +{ + vector< libcmis::DocumentPtr > versions; + string repoId = getSession( )->getRepositoryId( ); + string versionSeries; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:versionSeriesId" ) ); + if ( it != getProperties( ).end( ) && !it->second->getStrings( ).empty( ) ) + { + versionSeries = it->second->getStrings( ).front( ); + versions = getSession( )->getVersioningService( ).getAllVersions( repoId, versionSeries ); + } + return versions; +} diff --git a/src/libcmis/ws-document.hxx b/src/libcmis/ws-document.hxx new file mode 100644 index 0000000..c569496 --- /dev/null +++ b/src/libcmis/ws-document.hxx @@ -0,0 +1,60 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_DOCUMENT_HXX_ +#define _WS_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "ws-object.hxx" + +class WSDocument : public libcmis::Document, public WSObject +{ + public: + WSDocument( const WSObject& object ); + virtual ~WSDocument( ); + + virtual std::vector< libcmis::FolderPtr > getParents( ); + + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, std::string contentType, + std::string fileName, bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + + virtual libcmis::DocumentPtr checkIn( bool isMajor, std::string comment, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); +}; + +#endif diff --git a/src/libcmis/ws-folder.cxx b/src/libcmis/ws-folder.cxx new file mode 100644 index 0000000..4e82ac2 --- /dev/null +++ b/src/libcmis/ws-folder.cxx @@ -0,0 +1,68 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-folder.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +WSFolder::WSFolder( const WSObject& object ) : + libcmis::Object( object ), + libcmis::Folder( const_cast< WSObject& >( object ).getSession( ) ), + WSObject( object ) +{ +} + +WSFolder::~WSFolder( ) +{ +} + +vector< libcmis::ObjectPtr > WSFolder::getChildren( ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getNavigationService( ).getChildren( repoId, getId( ) ); +} + +libcmis::FolderPtr WSFolder::createFolder( const PropertyPtrMap& properties ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).createFolder( repoId, properties, getId( ) ); +} + +libcmis::DocumentPtr WSFolder::createDocument( const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType, string fileName ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).createDocument( repoId, properties, getId( ), os, contentType, fileName ); +} + +vector< string > WSFolder::removeTree( bool allVersion, libcmis::UnfileObjects::Type unfile, bool continueOnError ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).deleteTree( repoId, getId( ), allVersion, unfile, continueOnError ); +} diff --git a/src/libcmis/ws-folder.hxx b/src/libcmis/ws-folder.hxx new file mode 100644 index 0000000..2653967 --- /dev/null +++ b/src/libcmis/ws-folder.hxx @@ -0,0 +1,54 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_FOLDER_HXX_ +#define _WS_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "ws-object.hxx" +#include "ws-session.hxx" + +class WSFolder : public libcmis::Folder, public WSObject +{ + public: + WSFolder( const WSObject& object ); + virtual ~WSFolder( ); + + // virtual pure methods from Folder + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( const std::map< std::string, libcmis::PropertyPtr >& properties ); + virtual libcmis::DocumentPtr createDocument( const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType, std::string fileName ); + + virtual std::vector< std::string > removeTree( bool allVersion = true, libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/ws-navigationservice.cxx b/src/libcmis/ws-navigationservice.cxx new file mode 100644 index 0000000..f3d18ec --- /dev/null +++ b/src/libcmis/ws-navigationservice.cxx @@ -0,0 +1,101 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-navigationservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; + +NavigationService::NavigationService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +NavigationService::NavigationService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "NavigationService" ) ) +{ +} + +NavigationService::NavigationService( const NavigationService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +NavigationService::~NavigationService( ) +{ +} + +NavigationService& NavigationService::operator=( const NavigationService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +vector< libcmis::FolderPtr > NavigationService::getObjectParents( std::string repoId, std::string objectId ) +{ + vector< libcmis::FolderPtr > parents; + + GetObjectParentsRequest request( repoId, objectId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetObjectParentsResponse* response = dynamic_cast< GetObjectParentsResponse* >( resp ); + if ( response != NULL ) + parents = response->getParents( ); + } + + return parents; +} + +vector< libcmis::ObjectPtr > NavigationService::getChildren( string repoId, string folderId ) +{ + vector< libcmis::ObjectPtr > children; + + GetChildrenRequest request( repoId, folderId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetChildrenResponse* response = dynamic_cast< GetChildrenResponse* >( resp ); + if ( response != NULL ) + children = response->getChildren( ); + } + + return children; +} diff --git a/src/libcmis/ws-navigationservice.hxx b/src/libcmis/ws-navigationservice.hxx new file mode 100644 index 0000000..c245bbc --- /dev/null +++ b/src/libcmis/ws-navigationservice.hxx @@ -0,0 +1,60 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_NAVIGATIONSERVICE_HXX_ +#define _WS_NAVIGATIONSERVICE_HXX_ + +#include <string> +#include <vector> + +#include <libcmis/folder.hxx> + +class WSSession; + +class NavigationService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + NavigationService( WSSession* session ); + NavigationService( const NavigationService& copy ); + ~NavigationService( ); + + NavigationService& operator=( const NavigationService& copy ); + + std::vector< libcmis::FolderPtr > getObjectParents( std::string repoId, std::string objectId ); + std::vector< libcmis::ObjectPtr > getChildren( std::string repoId, std::string folderId ); + + private: + + NavigationService( ); +}; + +#endif diff --git a/src/libcmis/ws-object-type.cxx b/src/libcmis/ws-object-type.cxx new file mode 100644 index 0000000..9275da9 --- /dev/null +++ b/src/libcmis/ws-object-type.cxx @@ -0,0 +1,90 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-object-type.hxx" + +using namespace std; + +WSObjectType::WSObjectType( WSSession* session, xmlNodePtr node ) : + libcmis::ObjectType( node ), + m_session( session ) +{ +} + +WSObjectType::WSObjectType( ) : + libcmis::ObjectType( ), + m_session( NULL ) +{ +} + +WSObjectType::WSObjectType( const WSObjectType& copy ) : + libcmis::ObjectType( copy ), + m_session( copy.m_session ) +{ +} + +WSObjectType::~WSObjectType( ) +{ +} + +WSObjectType& WSObjectType::operator=( const WSObjectType& copy ) +{ + if ( this != © ) + { + libcmis::ObjectType::operator=( copy ); + m_session = copy.m_session; + } + + return *this; +} + +void WSObjectType::refresh( ) +{ + libcmis::ObjectTypePtr type = m_session->getType( m_id ); + WSObjectType* const other = dynamic_cast< WSObjectType* >( type.get( ) ); + if ( other != NULL ) + *this = *other; +} + +libcmis::ObjectTypePtr WSObjectType::getParentType( ) +{ + return m_session->getType( m_parentTypeId ); +} + +libcmis::ObjectTypePtr WSObjectType::getBaseType( ) +{ + return m_session->getType( m_baseTypeId ); +} + +vector< libcmis::ObjectTypePtr > WSObjectType::getChildren( ) +{ + vector< libcmis::ObjectTypePtr > children; + children = m_session->getRepositoryService( ).getTypeChildren( + m_session->getRepositoryId( ), m_id ); + return children; +} diff --git a/src/libcmis/ws-object-type.hxx b/src/libcmis/ws-object-type.hxx new file mode 100644 index 0000000..1fcf18d --- /dev/null +++ b/src/libcmis/ws-object-type.hxx @@ -0,0 +1,57 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_OBJECT_TYPE_HXX_ +#define _WS_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "ws-session.hxx" + +class WSObjectType : public libcmis::ObjectType +{ + private: + WSSession* m_session; + + public: + WSObjectType( WSSession* session, xmlNodePtr node ); + WSObjectType( const WSObjectType& copy ); + virtual ~WSObjectType( ); + + WSObjectType& operator=( const WSObjectType& copy ); + + virtual void refresh( ); + + virtual libcmis::ObjectTypePtr getParentType( ); + virtual libcmis::ObjectTypePtr getBaseType( ); + virtual std::vector< libcmis::ObjectTypePtr > getChildren( ); + + private: + WSObjectType( ); +}; + +#endif diff --git a/src/libcmis/ws-object.cxx b/src/libcmis/ws-object.cxx new file mode 100644 index 0000000..65a238d --- /dev/null +++ b/src/libcmis/ws-object.cxx @@ -0,0 +1,130 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-object.hxx" + +#include "ws-document.hxx" +#include "ws-folder.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +WSObject::WSObject( WSSession* session ) : + libcmis::Object( session ) +{ +} + + +WSObject::WSObject( WSSession* session, xmlNodePtr node ) : + libcmis::Object( session, node ) +{ +} + +WSObject::WSObject( const WSObject& copy ) : + libcmis::Object( copy ) +{ +} + +WSObject::~WSObject( ) +{ +} + +WSObject& WSObject::operator=( const WSObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + + return *this; +} + +vector< libcmis::RenditionPtr > WSObject::getRenditions( string filter ) +{ + // Check that the server supports that optional feature. There is no need to check it + // when getting the object as we may get them by shear luck + libcmis::RepositoryPtr repo = getSession( )->getRepository( ); + bool isCapable = repo && repo->getCapability( libcmis::Repository::Renditions ) == "read"; + + if ( m_renditions.empty() && isCapable ) + { + string repoId = getSession( )->getRepositoryId( ); + m_renditions = getSession( )->getObjectService( ).getRenditions( repoId, this->getId( ), filter ); + } + return m_renditions; +} + +libcmis::ObjectPtr WSObject::updateProperties( + const PropertyPtrMap& properties ) +{ + // No need to send HTTP request if there is nothing to update + if ( properties.empty( ) ) + { + libcmis::ObjectPtr object; + if ( getBaseType( ) == "cmis:document" ) + { + const WSDocument& thisDoc = dynamic_cast< const WSDocument& >( *this ); + object.reset( new WSDocument( thisDoc ) ); + } + else if ( getBaseType( ) == "cmis:folder" ) + { + const WSFolder& thisFolder = dynamic_cast< const WSFolder& >( *this ); + object.reset( new WSFolder( thisFolder ) ); + } + return object; + } + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).updateProperties( repoId, this->getId( ), properties, this->getChangeToken( ) ); +} + +void WSObject::refresh( ) +{ + libcmis::ObjectPtr object = m_session->getObject( getId( ) ); + WSObject* const other = dynamic_cast< WSObject* >( object.get( ) ); + if ( other != NULL ) + *this = *other; +} + +void WSObject::remove( bool allVersions ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getObjectService( ).deleteObject( repoId, this->getId( ), allVersions ); +} + +void WSObject::move( libcmis::FolderPtr source, libcmis::FolderPtr destination ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getObjectService( ).move( repoId, getId( ), destination->getId( ), source->getId( ) ); + + refresh( ); +} + +WSSession* WSObject::getSession( ) +{ + return dynamic_cast< WSSession* >( m_session ); +} diff --git a/src/libcmis/ws-object.hxx b/src/libcmis/ws-object.hxx new file mode 100644 index 0000000..f657371 --- /dev/null +++ b/src/libcmis/ws-object.hxx @@ -0,0 +1,61 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_OBJECT_HXX_ +#define _WS_OBJECT_HXX_ + +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> + +#include "ws-session.hxx" + +class WSObject : public virtual libcmis::Object +{ + protected: + WSObject( WSSession* session ); + + public: + WSObject( WSSession* session, xmlNodePtr node ); + WSObject( const WSObject& copy ); + virtual ~WSObject( ); + + WSObject& operator=( const WSObject& copy ); + + virtual std::vector< libcmis::RenditionPtr > getRenditions( std::string filter ); + virtual libcmis::ObjectPtr updateProperties( + const std::map< std::string, libcmis::PropertyPtr >& properties ); + + virtual void refresh( ); + + virtual void remove( bool allVersions = true ); + + virtual void move( libcmis::FolderPtr source, libcmis::FolderPtr destination ); + + WSSession* getSession( ); +}; + +#endif diff --git a/src/libcmis/ws-objectservice.cxx b/src/libcmis/ws-objectservice.cxx new file mode 100644 index 0000000..871349c --- /dev/null +++ b/src/libcmis/ws-objectservice.cxx @@ -0,0 +1,242 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-objectservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +ObjectService::ObjectService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +ObjectService::ObjectService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "ObjectService" ) ) +{ +} + +ObjectService::ObjectService( const ObjectService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +ObjectService::~ObjectService( ) +{ +} + +ObjectService& ObjectService::operator=( const ObjectService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +libcmis::ObjectPtr ObjectService::getObject( string repoId, string id ) +{ + libcmis::ObjectPtr object; + + GetObjectRequest request( repoId, id ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetObjectResponse* response = dynamic_cast< GetObjectResponse* >( resp ); + if ( response != NULL ) + object = response->getObject( ); + } + + return object; +} + +libcmis::ObjectPtr ObjectService::getObjectByPath( string repoId, string path ) +{ + libcmis::ObjectPtr object; + + GetObjectByPathRequest request( repoId, path ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetObjectResponse* response = dynamic_cast< GetObjectResponse* >( resp ); + if ( response != NULL ) + object = response->getObject( ); + } + + return object; +} + +vector< libcmis::RenditionPtr > ObjectService::getRenditions( + string repoId, string objectId, string filter ) +{ + vector< libcmis::RenditionPtr > renditions; + + GetRenditionsRequest request( repoId, objectId, filter ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetRenditionsResponse* response = dynamic_cast< GetRenditionsResponse* >( resp ); + if ( response != NULL ) + { + renditions = response->getRenditions( ); + } + } + + return renditions; +} + +libcmis::ObjectPtr ObjectService::updateProperties( + string repoId, string objectId, + const PropertyPtrMap& properties, + string changeToken ) +{ + libcmis::ObjectPtr object; + + UpdatePropertiesRequest request( repoId, objectId, properties, changeToken ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + UpdatePropertiesResponse* response = dynamic_cast< UpdatePropertiesResponse* >( resp ); + if ( response != NULL ) + { + string id = response->getObjectId( ); + object = getObject( repoId, id ); + } + } + + return object; +} + +void ObjectService::deleteObject( string repoId, string id, bool allVersions ) +{ + DeleteObjectRequest request( repoId, id, allVersions ); + m_session->soapRequest( m_url, request ); +} + +vector< string > ObjectService::deleteTree( std::string repoId, std::string folderId, bool allVersions, + libcmis::UnfileObjects::Type unfile, bool continueOnFailure ) +{ + vector< string > failedIds; + + DeleteTreeRequest request( repoId, folderId, allVersions, unfile, continueOnFailure ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + DeleteTreeResponse* response = dynamic_cast< DeleteTreeResponse* >( resp ); + if ( response != NULL ) + failedIds = response->getFailedIds( ); + } + + return failedIds; +} + +void ObjectService::move( string repoId, string objectId, string destId, string srcId ) +{ + MoveObjectRequest request( repoId, objectId, destId, srcId ); + m_session->soapRequest( m_url, request ); +} + +boost::shared_ptr< istream > ObjectService::getContentStream( string repoId, string objectId ) +{ + boost::shared_ptr< istream > stream; + + GetContentStreamRequest request( repoId, objectId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetContentStreamResponse* response = dynamic_cast< GetContentStreamResponse* >( resp ); + if ( response != NULL ) + stream = response->getStream( ); + } + + return stream; +} + +void ObjectService::setContentStream( std::string repoId, std::string objectId, bool overwrite, std::string changeToken, + boost::shared_ptr< std::ostream > stream, std::string contentType, std::string fileName ) +{ + SetContentStreamRequest request( repoId, objectId, overwrite, changeToken, stream, contentType, fileName ); + m_session->soapRequest( m_url, request ); +} + +libcmis::FolderPtr ObjectService::createFolder( string repoId, const PropertyPtrMap& properties, + string folderId ) +{ + libcmis::FolderPtr folder; + + CreateFolderRequest request( repoId, properties, folderId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CreateFolderResponse* response = dynamic_cast< CreateFolderResponse* >( resp ); + if ( response != NULL ) + { + string id = response->getObjectId( ); + folder = m_session->getFolder( id ); + } + } + + return folder; +} + +libcmis::DocumentPtr ObjectService::createDocument( string repoId, const PropertyPtrMap& properties, + string folderId, boost::shared_ptr< ostream > stream, string contentType, string fileName ) +{ + libcmis::DocumentPtr document; + + CreateDocumentRequest request( repoId, properties, folderId, stream, contentType, fileName ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CreateFolderResponse* response = dynamic_cast< CreateFolderResponse* >( resp ); + if ( response != NULL ) + { + string id = response->getObjectId( ); + libcmis::ObjectPtr object = m_session->getObject( id ); + document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + } + } + + return document; +} diff --git a/src/libcmis/ws-objectservice.hxx b/src/libcmis/ws-objectservice.hxx new file mode 100644 index 0000000..877deb7 --- /dev/null +++ b/src/libcmis/ws-objectservice.hxx @@ -0,0 +1,95 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_OBJECTSERVICE_HXX_ +#define _WS_OBJECTSERVICE_HXX_ + +#include <istream> +#include <string> +#include <vector> + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> + +#include "base-session.hxx" +#include "ws-soap.hxx" + +class WSSession; + +class ObjectService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + ObjectService( WSSession* session ); + ObjectService( const ObjectService& copy ); + ~ObjectService( ); + + ObjectService& operator=( const ObjectService& copy ); + + libcmis::ObjectPtr getObject( std::string repoId, std::string id ); + + libcmis::ObjectPtr getObjectByPath( std::string repoId, std::string path ); + + std::vector< libcmis::RenditionPtr > getRenditions( + std::string repoId, std::string objectId, std::string filter ); + + libcmis::ObjectPtr updateProperties( + std::string repoId, + std::string objectId, + const std::map< std::string, libcmis::PropertyPtr > & properties, + std::string changeToken ); + + void deleteObject( std::string repoId, std::string id, bool allVersions ); + + std::vector< std::string > deleteTree( std::string repoId, std::string folderId, bool allVersions, + libcmis::UnfileObjects::Type unfile, bool continueOnFailure ); + + void move( std::string repoId, std::string objectId, std::string destId, std::string srcId ); + + boost::shared_ptr< std::istream > getContentStream( std::string repoId, std::string objectId ); + + void setContentStream( std::string repoId, std::string objectId, bool overwrite, std::string changeToken, + boost::shared_ptr< std::ostream > stream, std::string contentType, std::string fileName ); + + libcmis::FolderPtr createFolder( std::string repoId, const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId ); + + libcmis::DocumentPtr createDocument( std::string repoId, const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId, boost::shared_ptr< std::ostream > stream, std::string contentType, + std::string fileName ); + + private: + + ObjectService( ); +}; + +#endif diff --git a/src/libcmis/ws-relatedmultipart.cxx b/src/libcmis/ws-relatedmultipart.cxx new file mode 100644 index 0000000..4def5dd --- /dev/null +++ b/src/libcmis/ws-relatedmultipart.cxx @@ -0,0 +1,353 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-relatedmultipart.hxx" + +#include <algorithm> +#include <sstream> +#include <boost/algorithm/string.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> +#include <curl/curl.h> + +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace boost::uuids; + +RelatedPart::RelatedPart( string& name, string& type, string& content ) : + m_name( name ), + m_contentType( type ), + m_content( content ) +{ +} + +// LCOV_EXCL_START +string RelatedPart::toString( string cid ) +{ + string buf; + + buf += "Content-Id: <" + cid + ">\r\n"; + buf += "Content-Type: " + getContentType( ) + "\r\n"; + buf += "Content-Transfer-Encoding: binary\r\n\r\n"; + buf += getContent( ); + + return buf; +} +// LCOV_EXCL_STOP + +RelatedMultipart::RelatedMultipart( ) : + m_startId( ), + m_startInfo( ), + m_parts( ), + m_boundary( ) +{ + stringstream tmpStream("--------uuid:"); + tmpStream << random_generator()(); + m_boundary = tmpStream.str(); +} + +RelatedMultipart::RelatedMultipart( const string& body, const string& contentType ) : + m_startId( ), + m_startInfo( ), + m_parts( ), + m_boundary( ) +{ + // Parse the content-type + size_t lastPos = 0; + size_t pos = contentType.find_first_of( ";\"" ); + while ( pos != string::npos ) + { + bool escaped = contentType[pos] == '"'; + if ( escaped ) + { + // Look for the closing quote and then look for the ; after it + pos = contentType.find( "\"", pos + 1 ); + pos = contentType.find( ";", pos + 1 ); + } + + string param = contentType.substr( lastPos, pos - lastPos ); + size_t eqPos = param.find( "=" ); + if ( eqPos != string::npos ) + { + string name = param.substr( 0, eqPos ); + string value = param.substr( eqPos + 1 ); + if ( value[0] == '"' && value[value.length() - 1] == '"' ) + value = value.substr( 1, value.length( ) - 2 ); + + name = libcmis::trim( name ); + + if ( name == "start" ) + { + m_startId = value; + // Remove the '<' '>' around the id if any + if ( m_startId[0] == '<' && m_startId[m_startId.size()-1] == '>' ) + m_startId = m_startId.substr( 1, m_startId.size() - 2 ); + } + else if ( name == "boundary" ) + m_boundary = value; + else if ( name == "start-info" ) + m_startInfo = value; + } + + if ( pos != string::npos ) + { + lastPos = pos + 1; + pos = contentType.find_first_of( ";\"", lastPos ); + } + } + + // Parse the multipart + string bodyFixed( body ); + if ( boost::starts_with( bodyFixed, "--" + m_boundary + "\r\n" ) ) + bodyFixed = "\r\n" + bodyFixed; + + if ( bodyFixed[bodyFixed.length() - 1 ] != '\n' ) + bodyFixed += '\n'; + + string lineEnd( "\n" ); + string boundaryString( "--" + m_boundary ); + string endBoundaryString( "--" + m_boundary + "--" ); + pos = bodyFixed.find( lineEnd ); + lastPos = 0; + bool inPart = false; + bool inHeaders = false; + string cid; + string name; + string type; + string partBody; + + while ( pos != string::npos ) + { + string line = bodyFixed.substr( lastPos, pos - lastPos ); + if ( boost::starts_with( line, boundaryString ) ) + { + // Found a part start + inPart = true; + + if ( !cid.empty() && !type.empty( ) ) + { + // Remove potential \r at the end of the body part + if ( partBody[partBody.length() - 1] == '\r' ) + partBody = partBody.substr( 0, partBody.length() - 1 ); + + RelatedPartPtr relatedPart( new RelatedPart( name, type, partBody ) ); + m_parts[cid] = relatedPart; + + } + cid.clear( ); + type.clear( ); + name.clear( ); + partBody.clear( ); + inHeaders = true; + } + else if ( inPart ) + { + if ( inHeaders ) + { + // Remove potential \r at the end + if ( !line.empty() && line[line.length() - 1] == '\r' ) + line = line.substr( 0, line.length() - 1 ); + + if ( line.empty( ) ) + inHeaders = false; + else + { + size_t colonPos = line.find( ":" ); + string headerName = line.substr( 0, colonPos ); + string headerValue = line.substr( colonPos + 1 ); + if ( boost::to_lower_copy( headerName ) == "content-id" ) + { + cid = libcmis::trim( headerValue ); + // Remove the '<' '>' around the id if any + if ( cid[0] == '<' && cid[cid.size()-1] == '>' ) + cid = cid.substr( 1, cid.size() - 2 ); + } + else if ( headerName == "Content-Type" ) + type = libcmis::trim( headerValue ); + // TODO Handle the Content-Transfer-Encoding + } + } + else + { + if ( !partBody.empty() ) + partBody += lineEnd; + partBody += line; + } + } + + // If we found the end of the multipart, no need to continue looping + if ( boost::starts_with( line, endBoundaryString ) ) + break; + + lastPos = pos + lineEnd.length(); + pos = bodyFixed.find( lineEnd, lastPos ); + } +} + +vector< string > RelatedMultipart::getIds( ) +{ + vector< string > ids; + + for ( map< string, RelatedPartPtr >::iterator it = m_parts.begin( ); + it != m_parts.end( ); ++it ) + { + ids.push_back( it->first ); + } + + return ids; +} + +RelatedPartPtr RelatedMultipart::getPart( string& cid ) +{ + RelatedPartPtr part; + map< string, RelatedPartPtr >::iterator it = m_parts.find( cid ); + if ( it != m_parts.end( ) ) + part = it->second; + + return part; +} + +string RelatedMultipart::addPart( RelatedPartPtr part ) +{ + string cid = createPartId( part->getName( ) ); + m_parts[cid] = part; + return cid; +} + +void RelatedMultipart::setStart( string& cid, string& startInfo ) +{ + RelatedPartPtr start = getPart( cid ); + + if ( start.get( ) != NULL ) + { + m_startId = cid; + m_startInfo = startInfo; + } +} + +string RelatedMultipart::getContentType( ) +{ + string type = "multipart/related;"; + + RelatedPartPtr start = getPart( getStartId( ) ); + if ( start.get( ) != NULL ) + { + type += "start=\"" + getStartId( ) + "\";"; + + string startType = start->getContentType( ); + size_t pos = startType.find( ";" ); + if ( pos != string::npos ) + startType = startType.substr( 0, pos ); + + type += "type=\"" + startType + "\";"; + } + type += "boundary=\"" + m_boundary + "\";"; + type += "start-info=\"" + m_startInfo + "\""; + + return type; +} + +boost::shared_ptr< istringstream > RelatedMultipart::toStream( ) +{ + string buf; + + // Output the start part first + buf += "\r\n--" + m_boundary + "\r\n"; + RelatedPartPtr part = getPart( getStartId( ) ); + if ( part.get( ) != NULL ) + { + buf += part->toString( getStartId( ) ); + } + + for ( map< string, RelatedPartPtr >::iterator it = m_parts.begin( ); + it != m_parts.end( ); ++it ) + { + if ( it->first != getStartId( ) ) + { + buf += "\r\n--" + m_boundary + "\r\n"; + buf += it->second->toString( it->first ); + } + } + + buf += "\r\n--" + m_boundary + "--\r\n"; + + boost::shared_ptr< istringstream > is( new istringstream( buf ) ); + return is; +} + +string RelatedMultipart::createPartId( const string& name ) +{ + stringstream tmpStream(name); + tmpStream << "*"; + tmpStream << random_generator()(); + tmpStream << "@libcmis.sourceforge.net"; + + return tmpStream.str(); +} + +boost::shared_ptr< istream > getStreamFromNode( xmlNodePtr node, RelatedMultipart& multipart ) +{ + boost::shared_ptr< stringstream > stream; + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "Include" ) ) ) + { + // Get the content from the multipart + xmlChar* value = xmlGetProp( child, BAD_CAST( "href" ) ); + string href( ( char* )value ); + xmlFree( value ); + // Get the Content ID from the href (cid:content-id) + string id = href; + if ( href.substr( 0, 4 ) == "cid:" ) + { + id = href.substr( 4 ); + // URL-decode the id + id = libcmis::unescape( id ); + } + RelatedPartPtr part = multipart.getPart( id ); + if ( part != NULL ) + stream.reset( new stringstream( part->getContent( ) ) ); + } + } + + // If there was no xop:Include, then use the content as base64 data + if ( stream.get( ) == NULL ) + { + xmlChar* content = xmlNodeGetContent( node ); + + stream.reset( new stringstream( ) ); + libcmis::EncodedData decoder( stream.get( ) ); + decoder.setEncoding( "base64" ); + decoder.decode( ( void* )content, 1, xmlStrlen( content ) ); + decoder.finish( ); + + xmlFree( content ); + } + return stream; +} diff --git a/src/libcmis/ws-relatedmultipart.hxx b/src/libcmis/ws-relatedmultipart.hxx new file mode 100644 index 0000000..cd17e73 --- /dev/null +++ b/src/libcmis/ws-relatedmultipart.hxx @@ -0,0 +1,138 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_RELATEDMULTIPART_HXX_ +#define _WS_RELATEDMULTIPART_HXX_ + +#include <exception> +#include <map> +#include <string> +#include <sstream> +#include <vector> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +class RelatedPart +{ + private: + std::string m_name; + std::string m_contentType; + std::string m_content; + + public: + RelatedPart( std::string& name, std::string& type, std::string& content ); + ~RelatedPart( ) { }; + + std::string getName( ) { return m_name; } + std::string getContentType( ) { return m_contentType; } + std::string getContent( ) { return m_content; } + + /** Create the string to place between the boundaries in the multipart. + + \param cid the content Id to output + */ + std::string toString( std::string cid ); +}; +typedef boost::shared_ptr< RelatedPart > RelatedPartPtr; + +/** Represents a multipart/related content as specified by RFC-2387. + */ +class RelatedMultipart +{ + private: + + std::string m_startId; + std::string m_startInfo; + std::map< std::string, RelatedPartPtr > m_parts; + std::string m_boundary; + + public: + + /** Create a multipart/related from scratch (most probably + to output it later). + */ + RelatedMultipart( ); + + /** Parse a multipart body to extract the entries from it. + */ + RelatedMultipart( const std::string& body, const std::string& contentType ); + + /** Get the Content ids of all the parts; + */ + std::vector< std::string > getIds( ); + + /** Get the entry corresponding to the given ID. + */ + RelatedPartPtr getPart( std::string& cid ); + + /** Get the id of the multipart start entry. + */ + std::string& getStartId( ) { return m_startId; } + + std::string& getStartInfo( ) { return m_startInfo; } + + /** Add an entry to the multipart and returns the content ID that + was created for it. + */ + std::string addPart( RelatedPartPtr part ); + + /** Define the start of the multipart. That method needs to be + called before running toString(): the output order of the entries + is not guaranteed. + + \param cid the Content-Id of the start entry + \param startInfo the type to use as start-info in the Content-Type + */ + void setStart( std::string& cid, std::string& startInfo ); + + /** Compute the content type for the multipart object to set as the + Content-Type HTTP header. + */ + std::string getContentType( ); + + /** Dump the multipart to an input stream: this can be provided as is as + an HTTP post request body. + */ + boost::shared_ptr< std::istringstream > toStream( ); + + /** Provide an access to the boundary token for the unit tests. + */ + std::string getBoundary( ) { return m_boundary; } + + private: + + /** Generate a content id, using an entry name and some random uuid. + */ + std::string createPartId( const std::string& name ); +}; + +/** Extract stream from xs:base64Binary node using either xop:Include or base64 encoded data. + */ +boost::shared_ptr< std::istream > getStreamFromNode( xmlNodePtr node, RelatedMultipart& multipart ); + +#endif diff --git a/src/libcmis/ws-repositoryservice.cxx b/src/libcmis/ws-repositoryservice.cxx new file mode 100644 index 0000000..700078a --- /dev/null +++ b/src/libcmis/ws-repositoryservice.cxx @@ -0,0 +1,136 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-repositoryservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; + +RepositoryService::RepositoryService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +RepositoryService::RepositoryService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "RepositoryService" ) ) +{ +} + +RepositoryService::RepositoryService( const RepositoryService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +RepositoryService::~RepositoryService( ) +{ +} + +RepositoryService& RepositoryService::operator=( const RepositoryService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +map< string, string > RepositoryService::getRepositories( ) +{ + map< string, string > repositories; + + GetRepositoriesRequest request; + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + + if ( responses.size() == 1 ) + { + GetRepositoriesResponse* response = dynamic_cast< GetRepositoriesResponse* >( responses.front( ).get( ) ); + if ( response != NULL ) + { + repositories = response->getRepositories(); + } + } + return repositories; +} + +libcmis::RepositoryPtr RepositoryService::getRepositoryInfo( string id ) +{ + libcmis::RepositoryPtr repository; + + GetRepositoryInfoRequest request( id ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetRepositoryInfoResponse* response = dynamic_cast< GetRepositoryInfoResponse* >( resp ); + if ( response != NULL ) + repository = response->getRepository( ); + } + + return repository; +} + +libcmis::ObjectTypePtr RepositoryService::getTypeDefinition( string repoId, string typeId ) +{ + libcmis::ObjectTypePtr type; + + GetTypeDefinitionRequest request( repoId, typeId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetTypeDefinitionResponse* response = dynamic_cast< GetTypeDefinitionResponse* >( resp ); + if ( response != NULL ) + type = response->getType( ); + } + + return type; +} + +vector< libcmis::ObjectTypePtr > RepositoryService::getTypeChildren( string repoId, string typeId ) +{ + vector< libcmis::ObjectTypePtr > children; + + GetTypeChildrenRequest request( repoId, typeId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetTypeChildrenResponse* response = dynamic_cast< GetTypeChildrenResponse* >( resp ); + if ( response != NULL ) + children = response->getChildren( ); + } + + return children; +} diff --git a/src/libcmis/ws-repositoryservice.hxx b/src/libcmis/ws-repositoryservice.hxx new file mode 100644 index 0000000..4ef794f --- /dev/null +++ b/src/libcmis/ws-repositoryservice.hxx @@ -0,0 +1,71 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_REPOSITORYSERVICE_HXX_ +#define _WS_REPOSITORYSERVICE_HXX_ + +#include <map> +#include <string> +#include <vector> + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" +#include "ws-soap.hxx" + +class WSSession; + +class RepositoryService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + RepositoryService( WSSession* session ); + RepositoryService( const RepositoryService& copy ); + ~RepositoryService( ); + + RepositoryService& operator=( const RepositoryService& copy ); + + std::map< std::string, std::string > getRepositories( ); + + /** Get the repository information based on its identifier. + */ + libcmis::RepositoryPtr getRepositoryInfo( std::string id ); + + libcmis::ObjectTypePtr getTypeDefinition( std::string repoId, std::string typeId ); + + std::vector< libcmis::ObjectTypePtr > getTypeChildren( std::string repoId, std::string typeId ); + + private: + + RepositoryService(); +}; + +#endif diff --git a/src/libcmis/ws-requests.cxx b/src/libcmis/ws-requests.cxx new file mode 100644 index 0000000..e34d59e --- /dev/null +++ b/src/libcmis/ws-requests.cxx @@ -0,0 +1,877 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-requests.hxx" + +#include <libcmis/xml-utils.hxx> + +#include "ws-document.hxx" +#include "ws-folder.hxx" +#include "ws-object.hxx" +#include "ws-object-type.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +CmisSoapFaultDetail::CmisSoapFaultDetail( xmlNodePtr node ) : + SoapFaultDetail( ), + m_type( ), + m_code( 0 ), + m_message( ) +{ + // Extract the type, code and message + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char * )content ); + xmlFree( content ); + + if ( xmlStrEqual( child->name, BAD_CAST( "type" ) ) ) + { + m_type = value; + } + else if ( xmlStrEqual( child->name, BAD_CAST( "code" ) ) ) + { + try + { + m_code = libcmis::parseInteger( value ); + } + catch ( const libcmis::Exception& ) + { + // Simply leave the default error code if unparsable + } + } + else if ( xmlStrEqual( child->name, BAD_CAST( "message" ) ) ) + { + m_message = value; + } + } +} + +libcmis::Exception CmisSoapFaultDetail::toException( ) +{ + libcmis::Exception e( m_message, m_type ); + return e; +} + +boost::shared_ptr< libcmis::Exception > getCmisException( const SoapFault& fault ) +{ + boost::shared_ptr< libcmis::Exception > exception; + + vector< SoapFaultDetailPtr > details = fault.getDetail( ); + for ( vector< SoapFaultDetailPtr >::iterator it = details.begin( ); + it != details.end( ) && exception.get( ) == NULL; ++ it ) + { + boost::shared_ptr< CmisSoapFaultDetail > cmisDetail = boost::dynamic_pointer_cast< CmisSoapFaultDetail >( *it ); + if ( cmisDetail.get( ) != NULL ) + exception.reset( new libcmis::Exception( cmisDetail->toException( ) ) ); + } + + return exception; +} + +void writeCmismStream( xmlTextWriterPtr writer, RelatedMultipart& multipart, boost::shared_ptr< ostream > os, string& contentType, string filename ) +{ + // Get the stream as a string + istream is( os->rdbuf( ) ); + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + + char* buf = new char[ size ]; + is.read( buf, size ); + string content( buf, size ); + delete[ ] buf; + + xmlTextWriterWriteFormatElement( writer, BAD_CAST( "cmism:length" ), "%ld", static_cast<long int>(content.size( )) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:mimeType" ), BAD_CAST( contentType.c_str( ) ) ); + if ( !filename.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:filename" ), BAD_CAST( filename.c_str( ) ) ); + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:stream" ) ); + + string name( "stream" ); + RelatedPartPtr streamPart( new RelatedPart( name, contentType, content ) ); + string partHref = "cid:"; + partHref += multipart.addPart( streamPart ); + + xmlTextWriterStartElement( writer, BAD_CAST( "xop:Include" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:xop" ), BAD_CAST( "http://www.w3.org/2004/08/xop/include" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "href" ), BAD_CAST( partHref.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // xop:Include + xmlTextWriterEndElement( writer ); // cmism:stream +} + +SoapFaultDetailPtr CmisSoapFaultDetail::create( xmlNodePtr node ) +{ + return SoapFaultDetailPtr( new CmisSoapFaultDetail( node ) ); +} + +void GetRepositoriesRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getRepositories" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetRepositoriesResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + GetRepositoriesResponse* response = new GetRepositoriesResponse( ); + + // Look for the cmiss:repositories children + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "repositories" ) ) ) + { + string id; + string name; + + // Look for repositoryId and repositoryName + for ( xmlNodePtr repoChild = child->children; repoChild; repoChild = repoChild->next ) + { + xmlChar* content = xmlNodeGetContent( repoChild ); + string value( ( char* ) content ); + xmlFree( content ); + + if ( xmlStrEqual( repoChild->name, BAD_CAST( "repositoryId" ) ) ) + { + id = value; + } + else if ( xmlStrEqual( repoChild->name, BAD_CAST( "repositoryName" ) ) ) + { + name = value; + } + + } + + if ( !id.empty( ) ) + response->m_repositories[ id ] = name; + } + } + + return SoapResponsePtr( response ); +} + +void GetRepositoryInfoRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getRepositoryInfo" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_id.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetRepositoryInfoResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + GetRepositoryInfoResponse* response = new GetRepositoryInfoResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "repositoryInfo" ) ) ) + { + libcmis::RepositoryPtr repository( new libcmis::Repository( child ) ); + response->m_repository = repository; + } + } + + return SoapResponsePtr( response ); +} + +void GetTypeDefinitionRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getTypeDefinition" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:typeId" ), BAD_CAST( m_typeId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetTypeDefinitionResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetTypeDefinitionResponse* response = new GetTypeDefinitionResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "type" ) ) ) + { + libcmis::ObjectTypePtr type( new WSObjectType( wsSession, child ) ); + response->m_type = type; + } + } + + return SoapResponsePtr( response ); +} + +void GetTypeChildrenRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getTypeChildren" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:typeId" ), BAD_CAST( m_typeId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includePropertyDefinitions" ), BAD_CAST( "true" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetTypeChildrenResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetTypeChildrenResponse* response = new GetTypeChildrenResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "types" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "types" ) ) ) + { + libcmis::ObjectTypePtr type( new WSObjectType( wsSession, gdchild ) ); + response->m_children.push_back( type ); + } + } + } + } + + return SoapResponsePtr( response ); +} + +void GetObjectRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getObject" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_id.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + + // Ask for renditions... some servers like Alfresco are providing them only this way + // and it saves time (another HTTP request) anyway. + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetObjectResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetObjectResponse* response = new GetObjectResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "object" ) ) ) + { + libcmis::ObjectPtr object; + WSObject tmp( wsSession, child ); + if ( tmp.getBaseType( ) == "cmis:folder" ) + { + object.reset( new WSFolder( tmp ) ); + } + else if ( tmp.getBaseType( ) == "cmis:document" ) + { + object.reset( new WSDocument( tmp ) ); + } + else + { + // This should never happen... but who knows if the standard is 100% repected? + object.reset( new WSObject( wsSession, child ) ); + } + response->m_object = object; + } + } + + return SoapResponsePtr( response ); +} + +void GetObjectByPathRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getObjectByPath" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:path" ), BAD_CAST( m_path.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +void UpdatePropertiesRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:updateProperties" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + if ( !m_changeToken.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:changeToken" ), BAD_CAST( m_changeToken.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + if( property->getPropertyType( )->isUpdatable( ) ) + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr UpdatePropertiesResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + UpdatePropertiesResponse* response = new UpdatePropertiesResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_id = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void DeleteObjectRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:deleteObject" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + string allVersionsStr( "false" ); + if ( m_allVersions ) + allVersionsStr = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:allVersions" ), BAD_CAST( allVersionsStr.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +void DeleteTreeRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:deleteTree" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + + string allVersionsStr( "false" ); + if ( m_allVersions ) + allVersionsStr = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:allVersions" ), BAD_CAST( allVersionsStr.c_str( ) ) ); + + string unfileStr( "" ); + switch ( m_unfile ) + { + case libcmis::UnfileObjects::Unfile: + unfileStr = "unfile"; + break; + case libcmis::UnfileObjects::DeleteSingleFiled: + unfileStr = "deletesinglefiled"; + break; + case libcmis::UnfileObjects::Delete: + unfileStr = "delete"; + break; + default: + break; + } + if ( !unfileStr.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:unfileObjects" ), BAD_CAST( unfileStr.c_str( ) ) ); + + string continueStr( "false" ); + if ( m_continueOnFailure ) + continueStr = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:continueOnFailure" ), BAD_CAST( continueStr.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr DeleteTreeResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + DeleteTreeResponse* response = new DeleteTreeResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "failedToDelete" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "objectIds" ) ) ) + { + xmlChar* content = xmlNodeGetContent( gdchild ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_failedIds.push_back( value ); + } + } + } + } + } + + return SoapResponsePtr( response ); +} + +void MoveObjectRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:moveObject" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:targetFolderId" ), BAD_CAST( m_destId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:sourceFolderId" ), BAD_CAST( m_srcId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +void GetContentStreamRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getContentStream" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetContentStreamResponse::create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* ) +{ + GetContentStreamResponse* response = new GetContentStreamResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "contentStream" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "stream" ) ) ) + { + xmlChar* content = xmlNodeGetContent( gdchild ); + if ( content != NULL ) + { + // We can either have directly the base64 encoded data or + // an <xop:Include> pointing to another part of the multipart + response->m_stream = getStreamFromNode( gdchild, multipart ); + } + xmlFree( content ); + } + } + } + } + + return SoapResponsePtr( response ); +} + +void GetObjectParentsRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getObjectParents" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetObjectParentsResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetObjectParentsResponse* response = new GetObjectParentsResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "parents" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "object" ) ) ) + { + libcmis::FolderPtr parent; + WSObject tmp( wsSession, gdchild ); + if ( tmp.getBaseType( ) == "cmis:folder" ) + { + parent.reset( new WSFolder( tmp ) ); + response->m_parents.push_back( parent ); + } + } + } + } + } + + return SoapResponsePtr( response ); +} + +void GetChildrenRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getChildren" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetChildrenResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetChildrenResponse* response = new GetChildrenResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objects" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "objects" ) ) ) + { + for ( xmlNodePtr gdgdchild = gdchild->children; gdgdchild; gdgdchild = gdgdchild->next ) + { + if ( xmlStrEqual( gdgdchild->name, BAD_CAST( "object" ) ) ) + { + libcmis::ObjectPtr object; + WSObject tmp( wsSession, gdgdchild ); + if ( tmp.getBaseType( ) == "cmis:folder" ) + { + object.reset( new WSFolder( tmp ) ); + } + else if ( tmp.getBaseType( ) == "cmis:document" ) + { + object.reset( new WSDocument( tmp ) ); + } + else + { + // This should never happen... but who knows if the standard is 100% repected? + object.reset( new WSObject( wsSession, gdgdchild ) ); + } + response->m_children.push_back( object ); + } + } + } + } + } + } + + return SoapResponsePtr( response ); +} + +void CreateFolderRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:createFolder" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr CreateFolderResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + CreateFolderResponse* response = new CreateFolderResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_id = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void CreateDocumentRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:createDocument" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:contentStream" ) ); + writeCmismStream( writer, m_multipart, m_stream, m_contentType, m_filename ); + xmlTextWriterEndElement( writer ); // cmism:contentStream + + xmlTextWriterEndElement( writer ); +} + +void SetContentStreamRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:setContentStream" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + string overwrite( "false" ); + if ( m_overwrite ) + overwrite = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:overwriteFlag" ), BAD_CAST( overwrite.c_str( ) ) ); + + if ( !m_changeToken.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:changeToken" ), BAD_CAST( m_changeToken.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:contentStream" ) ); + writeCmismStream( writer, m_multipart, m_stream, m_contentType, m_filename ); + + xmlTextWriterEndElement( writer ); // cmism:contentStream + + xmlTextWriterEndElement( writer ); +} + +void GetRenditionsRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getRenditions" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( m_filter.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetRenditionsResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + GetRenditionsResponse* response = new GetRenditionsResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "renditions" ) ) ) + { + libcmis::RenditionPtr rendition( new libcmis::Rendition( child ) ); + response->m_renditions.push_back( rendition ); + } + } + + return SoapResponsePtr( response ); +} + +void CheckOutRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:checkOut" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr CheckOutResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + CheckOutResponse* response = new CheckOutResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_objectId = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void CancelCheckOutRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:cancelCheckOut" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +void CheckInRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:checkIn" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + string major = "false"; + if ( m_isMajor ) + major = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:major" ), BAD_CAST( major.c_str( ) ) ); + + if ( m_properties.empty( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + } + + if ( m_stream.get( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:contentStream" ) ); + writeCmismStream( writer, m_multipart, m_stream, m_contentType, m_fileName ); + xmlTextWriterEndElement( writer ); // cmism:contentStream + } + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:checkinComment" ), BAD_CAST( m_comment.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr CheckInResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + CheckInResponse* response = new CheckInResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_objectId = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void GetAllVersionsRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getAllVersions" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetAllVersionsResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetAllVersionsResponse* response = new GetAllVersionsResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objects" ) ) ) + { + WSObject tmp( wsSession, child ); + if ( tmp.getBaseType( ) == "cmis:document" ) + { + libcmis::DocumentPtr object( new WSDocument( tmp ) ); + response->m_objects.push_back( object ); + } + } + } + + return SoapResponsePtr( response ); +} diff --git a/src/libcmis/ws-requests.hxx b/src/libcmis/ws-requests.hxx new file mode 100644 index 0000000..1782768 --- /dev/null +++ b/src/libcmis/ws-requests.hxx @@ -0,0 +1,786 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_REQUESTS_HXX_ +#define _WS_REQUESTS_HXX_ + +#include <istream> +#include <map> +#include <ostream> +#include <string> +#include <vector> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include <libcmis/document.hxx> +#include <libcmis/exception.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> +#include <libcmis/object-type.hxx> +#include <libcmis/repository.hxx> + +#include "ws-soap.hxx" + +class CmisSoapFaultDetail : public SoapFaultDetail +{ + private: + std::string m_type; + long m_code; + std::string m_message; + + CmisSoapFaultDetail( xmlNodePtr node ); + + public: + ~CmisSoapFaultDetail( ) noexcept { }; + + std::string getType( ) { return m_type; } + int getCode( ) { return m_code; } + std::string getMessage( ) { return m_message; } + + libcmis::Exception toException( ); + + static SoapFaultDetailPtr create( xmlNodePtr node ); +}; + +boost::shared_ptr< libcmis::Exception > getCmisException( const SoapFault& fault ); + +void writeCmismStream( xmlTextWriterPtr writer, RelatedMultipart& multipart, + boost::shared_ptr< std::ostream >, std::string& contentType, std::string filename ); + +/** getRepositories request. + */ +class GetRepositoriesRequest : public SoapRequest +{ + public: + GetRepositoriesRequest( ) { }; + ~GetRepositoriesRequest( ) { }; + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRepositoriesResponse : public SoapResponse +{ + private: + std::map< std::string, std::string > m_repositories; + + GetRepositoriesResponse( ) : SoapResponse( ), m_repositories( ) { }; + + public: + + /** Parse cmism:getRepositoriesResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::map< std::string, std::string > getRepositories( ) { return m_repositories; } +}; + +class GetRepositoryInfoRequest : public SoapRequest +{ + private: + std::string m_id; + + public: + GetRepositoryInfoRequest( std::string id ) : m_id( id ) { }; + ~GetRepositoryInfoRequest( ) { }; + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRepositoryInfoResponse : public SoapResponse +{ + private: + libcmis::RepositoryPtr m_repository; + + GetRepositoryInfoResponse( ) : SoapResponse( ), m_repository( ) { }; + + public: + + /** Parse cmism:getRepositoriesResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + libcmis::RepositoryPtr getRepository( ) { return m_repository; } +}; + +class GetTypeDefinitionRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_typeId; + + public: + GetTypeDefinitionRequest( std::string repoId, std::string typeId ) : + m_repositoryId( repoId ), + m_typeId( typeId ) + { + } + + ~GetTypeDefinitionRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetTypeDefinitionResponse : public SoapResponse +{ + private: + libcmis::ObjectTypePtr m_type; + + GetTypeDefinitionResponse( ) : SoapResponse( ), m_type( ) { } + + public: + + /** Parse cmism:getTypeDefinitionResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + libcmis::ObjectTypePtr getType( ) { return m_type; } +}; + +class GetTypeChildrenRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_typeId; + + public: + GetTypeChildrenRequest( std::string repoId, std::string typeId ) : + m_repositoryId( repoId ), + m_typeId( typeId ) + { + } + + ~GetTypeChildrenRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetTypeChildrenResponse : public SoapResponse +{ + private: + std::vector< libcmis::ObjectTypePtr > m_children; + + GetTypeChildrenResponse( ) : SoapResponse( ), m_children( ) { } + + public: + + /** Parse cmism:getTypeChildrenResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::ObjectTypePtr > getChildren( ) { return m_children; } +}; + +class GetObjectRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_id; + + public: + GetObjectRequest( std::string repoId, std::string id ) : + m_repositoryId( repoId ), + m_id( id ) + { + } + + ~GetObjectRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetObjectResponse : public SoapResponse +{ + private: + libcmis::ObjectPtr m_object; + + GetObjectResponse( ) : SoapResponse( ), m_object( ) { } + + public: + + /** Parse cmism:getObjectResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + libcmis::ObjectPtr getObject( ) { return m_object; } +}; + +class GetObjectByPathRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_path; + + public: + GetObjectByPathRequest( std::string repoId, std::string path ) : + m_repositoryId( repoId ), + m_path( path ) + { + } + + ~GetObjectByPathRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class UpdatePropertiesRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + std::string m_changeToken; + + public: + UpdatePropertiesRequest( std::string repoId, std::string objectId, + const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string changeToken ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_properties( properties ), + m_changeToken( changeToken ) + { + } + + ~UpdatePropertiesRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class UpdatePropertiesResponse : public SoapResponse +{ + private: + std::string m_id; + + UpdatePropertiesResponse( ) : SoapResponse( ), m_id( ) { } + + public: + + /** Parse cmism:updatePropertiesResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_id; } +}; + +class DeleteObjectRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + bool m_allVersions; + + public: + DeleteObjectRequest( std::string repoId, std::string objectId, bool allVersions ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_allVersions( allVersions ) + { + } + + ~DeleteObjectRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class DeleteTreeRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_folderId; + bool m_allVersions; + libcmis::UnfileObjects::Type m_unfile; + bool m_continueOnFailure; + + public: + DeleteTreeRequest( std::string repoId, + std::string folderId, + bool allVersions, + libcmis::UnfileObjects::Type unfile, + bool continueOnFailure ) : + m_repositoryId( repoId ), + m_folderId( folderId ), + m_allVersions( allVersions ), + m_unfile( unfile ), + m_continueOnFailure( continueOnFailure ) + { + } + + ~DeleteTreeRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class DeleteTreeResponse : public SoapResponse +{ + private: + std::vector< std::string > m_failedIds; + + DeleteTreeResponse( ) : SoapResponse( ), m_failedIds( ) { } + + public: + + /** Parse cmism:deleteTreeResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< std::string > getFailedIds( ) { return m_failedIds; } +}; + +class MoveObjectRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + std::string m_destId; + std::string m_srcId; + + public: + MoveObjectRequest( std::string repoId, std::string objectId, std::string destId, std::string srcId ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_destId( destId ), + m_srcId( srcId ) + { + } + + ~MoveObjectRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetContentStreamRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + GetContentStreamRequest( std::string repoId, std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~GetContentStreamRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetContentStreamResponse : public SoapResponse +{ + private: + boost::shared_ptr< std::istream > m_stream; + + GetContentStreamResponse( ) : SoapResponse( ), m_stream( ) { } + + public: + + /** Parse cmism:getContentStreamResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + boost::shared_ptr< std::istream> getStream( ) { return m_stream; } +}; + +class GetObjectParentsRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + GetObjectParentsRequest( std::string repoId, + std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~GetObjectParentsRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetObjectParentsResponse : public SoapResponse +{ + private: + std::vector< libcmis::FolderPtr > m_parents; + + GetObjectParentsResponse( ) : SoapResponse( ), m_parents( ) { } + + public: + + /** Parse cmism:getObjectParentsResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::FolderPtr > getParents( ) { return m_parents; } +}; + +class GetChildrenRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_folderId; + + public: + GetChildrenRequest( std::string repoId, + std::string folderId ) : + m_repositoryId( repoId ), + m_folderId( folderId ) + { + } + + ~GetChildrenRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetChildrenResponse : public SoapResponse +{ + private: + std::vector< libcmis::ObjectPtr > m_children; + + GetChildrenResponse( ) : SoapResponse( ), m_children( ) { } + + public: + + /** Parse cmism:getChildrenResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::ObjectPtr > getChildren( ) { return m_children; } +}; + +class CreateFolderRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + std::string m_folderId; + + public: + CreateFolderRequest( std::string repoId, + const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId ) : + m_repositoryId( repoId ), + m_properties( properties ), + m_folderId( folderId ) + { + } + + ~CreateFolderRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CreateFolderResponse : public SoapResponse +{ + private: + std::string m_id; + + CreateFolderResponse( ) : SoapResponse( ), m_id( ) { } + + public: + + /** Parse cmism:createFolderResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_id; } +}; + +class CreateDocumentRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + std::string m_folderId; + boost::shared_ptr< std::ostream > m_stream; + std::string m_contentType; + std::string m_filename; + + public: + CreateDocumentRequest( std::string repoId, + const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId, boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string filename ) : + m_repositoryId( repoId ), + m_properties( properties ), + m_folderId( folderId ), + m_stream( stream ), + m_contentType( contentType ), + m_filename( filename ) + { + } + + ~CreateDocumentRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class SetContentStreamRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + bool m_overwrite; + std::string m_changeToken; + boost::shared_ptr< std::ostream > m_stream; + std::string m_contentType; + std::string m_filename; + + public: + SetContentStreamRequest( std::string repoId, + std::string objectId, + bool overwrite, + std::string changeToken, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string filename ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_overwrite( overwrite ), + m_changeToken( changeToken ), + m_stream( stream ), + m_contentType( contentType ), + m_filename( filename ) + { + } + + ~SetContentStreamRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRenditionsRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + std::string m_filter; + + public: + GetRenditionsRequest( std::string repoId, std::string objectId, std::string filter ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_filter( filter ) + { + } + + ~GetRenditionsRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRenditionsResponse : public SoapResponse +{ + private: + std::vector< libcmis::RenditionPtr > m_renditions; + + GetRenditionsResponse( ) : SoapResponse( ), m_renditions( ) { } + + public: + + /** Parse cmism:getRenditionsResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::RenditionPtr > getRenditions( ) { return m_renditions; } +}; + +class CheckOutRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + CheckOutRequest( std::string repoId, + std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~CheckOutRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CheckOutResponse : public SoapResponse +{ + private: + std::string m_objectId; + + CheckOutResponse( ) : SoapResponse( ), m_objectId( ) { } + + public: + + /** Parse cmism:checkOutResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_objectId; } +}; + +class CancelCheckOutRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + CancelCheckOutRequest( std::string repoId, + std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~CancelCheckOutRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CheckInRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + bool m_isMajor; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + boost::shared_ptr< std::ostream > m_stream; + std::string m_contentType; + std::string m_fileName; + std::string m_comment; + + public: + CheckInRequest( std::string repoId, + std::string objectId, bool isMajor, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string fileName, std::string comment ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_isMajor( isMajor ), + m_properties( properties ), + m_stream( stream ), + m_contentType( contentType ), + m_fileName( fileName ), + m_comment( comment ) + { + } + + ~CheckInRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CheckInResponse : public SoapResponse +{ + private: + std::string m_objectId; + + CheckInResponse( ) : SoapResponse( ), m_objectId( ) { } + + public: + + /** Parse cmism:checkInResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_objectId; } +}; + +class GetAllVersionsRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + GetAllVersionsRequest( std::string repoId, std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~GetAllVersionsRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetAllVersionsResponse : public SoapResponse +{ + private: + std::vector< libcmis::DocumentPtr > m_objects; + + GetAllVersionsResponse( ) : SoapResponse( ), m_objects( ) { } + + public: + + /** Parse cmism:getAllVersionsResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::DocumentPtr > getObjects( ) { return m_objects; } +}; + +#endif diff --git a/src/libcmis/ws-session.cxx b/src/libcmis/ws-session.cxx new file mode 100644 index 0000000..3b1b291 --- /dev/null +++ b/src/libcmis/ws-session.cxx @@ -0,0 +1,417 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-session.hxx" + +#include <sstream> + +#include <boost/date_time.hpp> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/xml-utils.hxx> + +#include "ws-requests.hxx" + +using namespace std; + +WSSession::WSSession( string bindingUrl, string repositoryId, string username, + string password, bool noSslCheck, libcmis::OAuth2DataPtr oauth2, + bool verbose ) : + BaseSession( bindingUrl, repositoryId, username, password, noSslCheck, oauth2, verbose ), + m_servicesUrls( ), + m_navigationService( NULL ), + m_objectService( NULL ), + m_repositoryService( NULL ), + m_versioningService( NULL ), + m_responseFactory( ) +{ + // We don't want to have the HTTP exceptions as the errors are coming + // back as SoapFault elements. + setNoHttpErrors( true ); + initialize( ); +} + +WSSession::WSSession( string bindingUrl, string repositoryId, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ) : + BaseSession( bindingUrl, repositoryId, httpSession ), + m_servicesUrls( ), + m_navigationService( NULL ), + m_objectService( NULL ), + m_repositoryService( NULL ), + m_versioningService( NULL ), + m_responseFactory( ) +{ + // We don't want to have the HTTP exceptions as the errors are coming + // back as SoapFault elements. + setNoHttpErrors( true ); + initialize( response ); +} + +WSSession::WSSession( ) : + BaseSession( ), + m_servicesUrls( ), + m_navigationService( NULL ), + m_objectService( NULL ), + m_repositoryService( NULL ), + m_versioningService( NULL ), + m_responseFactory( ) +{ + setNoHttpErrors( true ); +} + +WSSession::~WSSession( ) +{ + delete m_navigationService; + delete m_objectService; + delete m_repositoryService; + delete m_versioningService; +} + +string WSSession::getWsdl( string url, libcmis::HttpResponsePtr response ) +{ + string buf; + if ( response ) + buf = response->getStream( )->str( ); + else + buf = httpGetRequest( url )->getStream( )->str( ); + + // Do we have a wsdl file? + bool isWsdl = false; + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), m_bindingUrl.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerCmisWSNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + string definitionsXPath( "/wsdl:definitions" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( definitionsXPath.c_str() ), xpathCtx ); + + isWsdl = ( xpathObj != NULL ) && ( xpathObj->nodesetval != NULL ) && ( xpathObj->nodesetval->nodeNr > 0 ); + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + xmlFreeDoc( doc ); + + // If we don't have a wsdl file we may have received an HTML explanation for it, + // try to add ?wsdl to the URL (last chance to get something) + if ( !isWsdl ) + { + if ( url.find( "?" ) == string::npos ) + url += "?"; + else + url += "&"; + url += "wsdl"; + + buf = httpGetRequest( url )->getStream( )->str( ); + } + + return buf; +} + +vector< SoapResponsePtr > WSSession::soapRequest( string& url, SoapRequest& request ) +{ + vector< SoapResponsePtr > responses; + + try + { + // Place the request in an envelope + RelatedMultipart& multipart = request.getMultipart( getUsername( ), getPassword( ) ); + libcmis::HttpResponsePtr response = httpPostRequest( url, *multipart.toStream( ).get( ), multipart.getContentType( ) ); + + string responseType; + map< string, string >::iterator it = response->getHeaders( ).find( "Content-Type" ); + if ( it != response->getHeaders( ).end( ) ) + { + responseType = it->second; + if ( string::npos != responseType.find( "multipart/related" ) ) + { + RelatedMultipart answer( response->getStream( )->str( ), responseType ); + + responses = getResponseFactory( ).parseResponse( answer ); + } + else if ( string::npos != responseType.find( "text/xml" ) ) + { + // Parse the envelope + string xml = response->getStream( )->str( ); + responses = getResponseFactory( ).parseResponse( xml ); + } + } + } + catch ( const SoapFault& fault ) + { + boost::shared_ptr< libcmis::Exception > cmisException = getCmisException( fault ); + if ( cmisException ) + { + throw *cmisException; + } + throw libcmis::Exception( fault.what( ), "runtime" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + return responses; +} + +void WSSession::parseWsdl( string buf ) +{ + // parse the content + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( buf.c_str(), buf.size(), m_bindingUrl.c_str(), NULL, 0 ), xmlFreeDoc ); + + if ( bool( doc ) ) + { + // Check that we have a WSDL document + xmlNodePtr root = xmlDocGetRootElement( doc.get() ); + if ( !xmlStrEqual( root->name, BAD_CAST( "definitions" ) ) ) + throw libcmis::Exception( "Not a WSDL document" ); + + // Get all the services soap URLs + m_servicesUrls.clear( ); + + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc.get() ); + libcmis::registerCmisWSNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + string serviceXPath( "//wsdl:service" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( serviceXPath.c_str() ), xpathCtx ); + + if ( xpathObj != NULL ) + { + int nbServices = 0; + if ( xpathObj->nodesetval ) + nbServices = xpathObj->nodesetval->nodeNr; + + for ( int i = 0; i < nbServices; i++ ) + { + // What service do we have here? + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + string name = libcmis::getXmlNodeAttributeValue( node, "name" ); + + // Gimme you soap:address location attribute + string locationXPath = serviceXPath + "[@name='" + name + "']/wsdl:port/soap:address/attribute::location"; + string location = libcmis::getXPathValue( xpathCtx, locationXPath ); + + m_servicesUrls[name] = location; + } + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + else + throw libcmis::Exception( "Failed to parse service document" ); +} + +void WSSession::initializeResponseFactory( ) +{ + map< string, string > ns; + ns[ "wsssecurity" ] = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; + ns[ NS_SOAP_ENV_PREFIX ] = NS_SOAP_ENV_URL; + ns[ "cmism" ] = NS_CMISM_URL; + ns[ "cmisw" ] = NS_CMISW_URL; + ns[ "cmis" ] = NS_CMIS_URL; + m_responseFactory.setNamespaces( ns ); + m_responseFactory.setMapping( getResponseMapping() ); + m_responseFactory.setDetailMapping( getDetailMapping( ) ); + m_responseFactory.setSession( this ); +} + +void WSSession::initializeRepositories( map< string, string > repositories ) +{ + for ( map< string, string >::iterator it = repositories.begin( ); + it != repositories.end( ); ++it ) + { + string repoId = it->first; + m_repositories.push_back( getRepositoryService( ).getRepositoryInfo( repoId ) ); + } +} + +void WSSession::initialize( libcmis::HttpResponsePtr response ) +{ + if ( m_repositories.empty() ) + { + // Get the wsdl file + string buf; + try + { + buf = getWsdl( m_bindingUrl, response ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + parseWsdl( buf ); + initializeResponseFactory( ); + map< string, string > repositories = getRepositoryService( ).getRepositories( ); + initializeRepositories( repositories ); + } +} + +map< string, SoapResponseCreator > WSSession::getResponseMapping( ) +{ + map< string, SoapResponseCreator > mapping; + + mapping[ "{" + string( NS_CMISM_URL ) + "}getRepositoriesResponse" ] = &GetRepositoriesResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getRepositoryInfoResponse" ] = &GetRepositoryInfoResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getTypeDefinitionResponse" ] = &GetTypeDefinitionResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getTypeChildrenResponse" ] = &GetTypeChildrenResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getObjectResponse" ] = &GetObjectResponse::create; + // No need to create a GetObjectByPathResponse as it would do the same than GetObjectResponse + mapping[ "{" + string( NS_CMISM_URL ) + "}getObjectByPathResponse" ] = &GetObjectResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}updatePropertiesResponse" ] = &UpdatePropertiesResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}deleteTreeResponse" ] = &DeleteTreeResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getContentStreamResponse" ] = &GetContentStreamResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getObjectParentsResponse" ] = &GetObjectParentsResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getChildrenResponse" ] = &GetChildrenResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}createFolderResponse" ] = &CreateFolderResponse::create; + // Use the same response object than folders as it contains the same elements + mapping[ "{" + string( NS_CMISM_URL ) + "}createDocumentResponse" ] = &CreateFolderResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}checkOutResponse" ] = &CheckOutResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}checkInResponse" ] = &CheckInResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getAllVersionsResponse" ] = &GetAllVersionsResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getRenditionsResponse" ] = &GetRenditionsResponse::create; + + return mapping; +} + +map< string, SoapFaultDetailCreator > WSSession::getDetailMapping( ) +{ + map< string, SoapFaultDetailCreator > mapping; + + mapping[ "{" + string( NS_CMISM_URL ) + "}cmisFault" ] = &CmisSoapFaultDetail::create; + + return mapping; +} + +string WSSession::getServiceUrl( string name ) +{ + string url; + + map< string, string >::iterator it = m_servicesUrls.find( name ); + if ( it != m_servicesUrls.end( ) ) + url = it->second; + + return url; +} + +RepositoryService& WSSession::getRepositoryService( ) +{ + if ( m_repositoryService == NULL ) + m_repositoryService = new RepositoryService( this ); + return *m_repositoryService; +} + +ObjectService& WSSession::getObjectService( ) +{ + if ( m_objectService == NULL ) + m_objectService = new ObjectService( this ); + return *m_objectService; +} + +NavigationService& WSSession::getNavigationService( ) +{ + if ( m_navigationService == NULL ) + m_navigationService = new NavigationService( this ); + return *m_navigationService; +} + +VersioningService& WSSession::getVersioningService( ) +{ + if ( m_versioningService == NULL ) + m_versioningService = new VersioningService( this ); + return *m_versioningService; +} + +libcmis::RepositoryPtr WSSession::getRepository( ) +{ + // Check if we already have the repository + libcmis::RepositoryPtr repo; + vector< libcmis::RepositoryPtr >::iterator it = m_repositories.begin(); + while ( !repo && it != m_repositories.end() ) + { + if ( ( *it )->getId() == m_repositoryId ) + repo = *it; + ++it; + } + + // We found nothing cached, so try to get it from the server + if ( !repo ) + { + repo = getRepositoryService( ).getRepositoryInfo( m_repositoryId ); + if ( repo ) + m_repositories.push_back( repo ); + } + + return repo; +} + +bool WSSession::setRepository( string repositoryId ) +{ + bool success = false; + try + { + libcmis::RepositoryPtr repo = getRepositoryService( ).getRepositoryInfo( repositoryId ); + if (repo && repo->getId( ) == repositoryId ) + m_repositoryId = repositoryId; + success = true; + } + catch ( const libcmis::Exception& ) + { + } + return success; +} + +libcmis::ObjectPtr WSSession::getObject( string id ) +{ + return getObjectService( ).getObject( getRepositoryId( ), id ); +} + +libcmis::ObjectPtr WSSession::getObjectByPath( string path ) +{ + return getObjectService( ).getObjectByPath( getRepositoryId( ), path ); +} + +libcmis::ObjectTypePtr WSSession::getType( string id ) +{ + return getRepositoryService( ).getTypeDefinition( m_repositoryId, id ); +} + +vector< libcmis::ObjectTypePtr > WSSession::getBaseTypes( ) +{ + return getRepositoryService().getTypeChildren( m_repositoryId, "" ); +} diff --git a/src/libcmis/ws-session.hxx b/src/libcmis/ws-session.hxx new file mode 100644 index 0000000..a7e6710 --- /dev/null +++ b/src/libcmis/ws-session.hxx @@ -0,0 +1,124 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_SESSION_HXX_ +#define _WS_SESSION_HXX_ + +#include <map> +#include <string> + +#include "base-session.hxx" +#include "ws-navigationservice.hxx" +#include "ws-objectservice.hxx" +#include "ws-repositoryservice.hxx" +#include "ws-soap.hxx" +#include "ws-versioningservice.hxx" + +class WSSession : public BaseSession, public SoapSession +{ + private: + std::map< std::string, std::string > m_servicesUrls; + NavigationService* m_navigationService; + ObjectService* m_objectService; + RepositoryService* m_repositoryService; + VersioningService* m_versioningService; + + SoapResponseFactory m_responseFactory; + + public: + WSSession( std::string bindingUrl, std::string repositoryId, + std::string username, std::string password, + bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), + bool verbose = false ); + + /** This constructor uses the response of an HTTP request made + before to spare some HTTP request. This constructor has mostly + been designed for the SessionFactory use. + */ + WSSession( std::string bindingUrl, std::string repositoryId, + const HttpSession& HttpSession, + libcmis::HttpResponsePtr response ); + ~WSSession( ); + + // Utility methods + + /** Get an instance of the SoapResponseFactory, setup with all the + CMIS namespaces and function pointers. + */ + SoapResponseFactory& getResponseFactory( ) { return m_responseFactory; } + + /** Try hard to get a WSDL file at the given URL (tries to add ?wsdl if needed) + */ + std::string getWsdl( std::string url, libcmis::HttpResponsePtr response ); + + std::vector< SoapResponsePtr > soapRequest( std::string& url, SoapRequest& request ); + + /** Get the service location URL given its name. + */ + std::string getServiceUrl( std::string name ); + + RepositoryService& getRepositoryService( ); + + ObjectService& getObjectService( ); + + NavigationService& getNavigationService( ); + + VersioningService& getVersioningService( ); + + + // Override session methods + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string repositoryId ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + private: + + // Default constructor shouldn't be called + WSSession( ); + WSSession( const WSSession& copy ) = delete; + WSSession& operator=( const WSSession& copy ) = delete; + + void parseWsdl( std::string buf ); + void initializeResponseFactory( ); + void initializeRepositories( std::map< std::string, std::string > repositories ); + void initialize( libcmis::HttpResponsePtr response = libcmis::HttpResponsePtr() ); + + std::map< std::string, SoapResponseCreator > getResponseMapping( ); + std::map< std::string, SoapFaultDetailCreator > getDetailMapping( ); +}; + +#endif diff --git a/src/libcmis/ws-soap.cxx b/src/libcmis/ws-soap.cxx new file mode 100644 index 0000000..20e9c68 --- /dev/null +++ b/src/libcmis/ws-soap.cxx @@ -0,0 +1,335 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-soap.hxx" + +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xmlstring.h> + +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace boost::uuids; + +SoapFault::SoapFault( xmlNodePtr node, SoapResponseFactory* factory ) : + exception( ), + m_faultcode( ), + m_faultstring( ), + m_detail( ), + m_message( ) +{ + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "faultcode" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + xmlChar* prefix = NULL; + xmlChar* localName = xmlSplitQName2( content, &prefix ); + if (localName == NULL) + localName = xmlStrdup( content ); + m_faultcode = string( ( char* )localName ); + xmlFree( content ); + xmlFree( prefix ); + xmlFree( localName ); + } + else if ( xmlStrEqual( child->name, BAD_CAST( "faultstring" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + m_faultstring = string( ( char* )content ); + xmlFree( content ); + } + else if ( xmlStrEqual( child->name, BAD_CAST( "detail" ) ) ) + { + m_detail = factory->parseFaultDetail( child ); + } + } + + m_message = getFaultcode() + ": " + getFaultstring(); + for ( vector< SoapFaultDetailPtr >::const_iterator it = m_detail.begin( ); it != m_detail.end( ); ++it ) + { + m_message += "\n" + ( *it )->toString( ); + } + +} + +// LCOV_EXCL_START +const char* SoapFault::what( ) const noexcept +{ + return m_message.c_str( ); +} +// LCOV_EXCL_STOP + + +SoapResponseFactory::SoapResponseFactory( ) : + m_mapping( ), + m_namespaces( ), + m_detailMapping( ), + m_session( NULL ) +{ +} + +SoapResponseFactory::SoapResponseFactory( const SoapResponseFactory& copy ) : + m_mapping( copy.m_mapping ), + m_namespaces( copy.m_namespaces ), + m_detailMapping( copy.m_detailMapping ), + m_session( copy.m_session ) +{ +} + +SoapResponseFactory& SoapResponseFactory::operator=( const SoapResponseFactory& copy ) +{ + if ( this != © ) + { + m_mapping = copy.m_mapping; + m_namespaces = copy.m_namespaces; + m_detailMapping = copy.m_detailMapping; + m_session = copy.m_session; + } + + return *this; +} + +vector< SoapResponsePtr > SoapResponseFactory::parseResponse( string& xml ) +{ + // Create a fake multipart + RelatedMultipart multipart; + string name = "root"; + string type = "text/xml"; + string info; + RelatedPartPtr part( new RelatedPart( name, type, xml ) ); + string cid = multipart.addPart( part ); + multipart.setStart( cid, info ); + + // Then parse it normally + return parseResponse( multipart ); +} + +vector< SoapResponsePtr > SoapResponseFactory::parseResponse( RelatedMultipart& multipart ) +{ + string xml; + RelatedPartPtr part = multipart.getPart( multipart.getStartId( ) ); + if ( part.get() != NULL ) + xml = part->getContent( ); + + vector< SoapResponsePtr > responses; + + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( xml.c_str(), xml.size(), "", NULL, 0 ), xmlFreeDoc ); + + if ( bool( doc ) ) + { + const boost::shared_ptr< xmlXPathContext > xpathCtx( xmlXPathNewContext( doc.get() ), xmlXPathFreeContext ); + libcmis::registerSoapNamespaces( xpathCtx.get() ); + + for ( map< string, string >::iterator it = m_namespaces.begin( ); + it != m_namespaces.end( ); ++it ) + { + xmlXPathRegisterNs( xpathCtx.get(), BAD_CAST( it->first.c_str() ), BAD_CAST( it->second.c_str( ) ) ); + } + + if ( bool( xpathCtx ) ) + { + string bodyXPath( "//soap-env:Body/*" ); + const boost::shared_ptr< xmlXPathObject > xpathObj( xmlXPathEvalExpression( BAD_CAST( bodyXPath.c_str() ), xpathCtx.get() ), xmlXPathFreeObject ); + + if ( bool( xpathObj ) ) + { + int nbChildren = 0; + if ( xpathObj->nodesetval ) + nbChildren = xpathObj->nodesetval->nodeNr; + + for ( int i = 0; i < nbChildren; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + + // Is it a fault? + if ( xmlStrEqual( BAD_CAST( NS_SOAP_ENV_URL ), node->ns->href ) && + xmlStrEqual( BAD_CAST( "Fault" ), node->name ) ) + { + throw SoapFault( node, this ); + } + SoapResponsePtr response = createResponse( node, multipart ); + if ( NULL != response.get( ) ) + responses.push_back( response ); + } + } + } + } + + return responses; +} + +SoapResponsePtr SoapResponseFactory::createResponse( xmlNodePtr node, RelatedMultipart& multipart ) +{ + SoapResponsePtr response; + + string ns( ( const char* ) node->ns->href ); + string name( ( const char* ) node->name ); + string id = "{" + ns + "}" + name; + map< string, SoapResponseCreator >::iterator it = m_mapping.find( id ); + + if ( it != m_mapping.end( ) ) + { + SoapResponseCreator creator = it->second; + response = creator( node, multipart, m_session ); + } + + return response; +} + +vector< SoapFaultDetailPtr > SoapResponseFactory::parseFaultDetail( xmlNodePtr node ) +{ + vector< SoapFaultDetailPtr > detail; + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + string ns; + if ( child->ns != NULL ) + ns = string( ( const char* ) child->ns->href ); + string name( ( const char* ) child->name ); + string id = "{" + ns + "}" + name; + map< string, SoapFaultDetailCreator >::iterator it = m_detailMapping.find( id ); + + if ( it != m_detailMapping.end( ) ) + { + SoapFaultDetailCreator creator = it->second; + detail.push_back( creator( child ) ); + } + } + + return detail; +} + +RelatedMultipart& SoapRequest::getMultipart( string& username, string& password ) +{ + // Generate the envelope and add it to the multipart + string envelope = createEnvelope( username, password ); + string name( "root" ); + string type( "application/xop+xml;charset=UTF-8;type=\"text/xml\"" ); + RelatedPartPtr envelopePart( new RelatedPart( name, type, envelope ) ); + string rootId = m_multipart.addPart( envelopePart ); + + // Set the envelope as the start part of the multipart + string startInfo( "text/xml" ); + m_multipart.setStart( rootId, startInfo ); + + return m_multipart; +} + +string SoapRequest::createEnvelope( string& username, string& password ) +{ + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + /* Sample envelope: + <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2012-06-14T09:20:29Z</Created> + <Expires>2012-06-15T09:20:29Z</Expires> + </Timestamp> + <UsernameToken> + <Username>admin</Username> + <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">admin</Password> + <Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2012-06-14T09:20:29Z</Created> + </UsernameToken> + </Security> + </S:Header> + <S:Body> + <ns2:getRepositories xmlns="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/messaging/200908/"/> + </S:Body> + </S:Envelope> + */ + xmlChar* wsseUrl = BAD_CAST( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ); + xmlChar* wsuUrl = BAD_CAST( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ); + + // Use an unsecure password transmission (PasswordText) because some clients can't support the PasswordDigest. + xmlChar* passTypeStr = BAD_CAST( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText" ); + + // Created must be a UTC time with no more than 3 digits fractional seconds. + boost::posix_time::ptime created( boost::posix_time::second_clock::universal_time( ) ); + boost::posix_time::ptime expires( created ); + expires = expires + boost::gregorian::days( 1 ); + string createdStr = libcmis::writeDateTime( created ); + string expiresStr = libcmis::writeDateTime( expires ); + + xmlTextWriterStartElement( writer, BAD_CAST( "S:Envelope" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:S" ), BAD_CAST( NS_SOAP_ENV_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:wsu" ), wsuUrl ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:wsse" ), wsseUrl ); + + xmlTextWriterStartElement( writer, BAD_CAST( "S:Header" ) ); + + // Write out the Basic Security Profile 1.0 compliant headers + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Security" ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Timestamp" ) ); + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Created" ) ); + xmlTextWriterWriteRaw( writer, BAD_CAST( createdStr.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Created + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Expires" ) ); + xmlTextWriterWriteRaw( writer, BAD_CAST( expiresStr.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Expires + xmlTextWriterEndElement( writer ); // End of Timestamp + + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:UsernameToken" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "wsse:Username" ), BAD_CAST( username.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Password" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "Type" ), passTypeStr ); + xmlTextWriterWriteRaw( writer, BAD_CAST( password.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Password + xmlTextWriterStartElement( writer, BAD_CAST( "wsu:Created" ) ); + xmlTextWriterWriteRaw( writer, BAD_CAST( createdStr.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Created + xmlTextWriterEndElement( writer ); // End of UsernameToken + + xmlTextWriterEndElement( writer ); // End of Security + + xmlTextWriterEndElement( writer ); // End of S:Header + + xmlTextWriterStartElement( writer, BAD_CAST( "S:Body" ) ); + toXml( writer ); + xmlTextWriterEndElement( writer ); // End of S:Body + + xmlTextWriterEndElement( writer ); // End of S:Envelope + xmlTextWriterEndDocument( writer ); + + string str( ( const char * )xmlBufferContent( buf ) ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + return str; +} diff --git a/src/libcmis/ws-soap.hxx b/src/libcmis/ws-soap.hxx new file mode 100644 index 0000000..62b8b36 --- /dev/null +++ b/src/libcmis/ws-soap.hxx @@ -0,0 +1,178 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_SOAP_HXX_ +#define _WS_SOAP_HXX_ + +#include <exception> +#include <map> +#include <vector> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include <libcmis/xmlserializable.hxx> + +#include "ws-relatedmultipart.hxx" + +/** Interface for soap sessions to communicate to response objects. + + \attention + It currently doesn't provide anything, but it may later + provide all useful methods for SOAP requests low level handling. + */ +class SoapSession +{ + public: + SoapSession( ) { } + virtual ~SoapSession( ) { } +}; + +/** Base class for all SOAP response objects. + + The factory will need to create the response objects using a static + creator method in each class. + */ +class SoapResponse +{ + public: + virtual ~SoapResponse( ) { }; +}; +typedef boost::shared_ptr< SoapResponse > SoapResponsePtr; +typedef SoapResponsePtr ( *SoapResponseCreator ) ( xmlNodePtr, RelatedMultipart&, SoapSession* session ); + +/** Base clas for SoapFault details parsed data. + */ +class SoapFaultDetail +{ + public: + virtual ~SoapFaultDetail() {}; + + virtual const std::string toString( ) const { return std::string( ); } +}; +typedef boost::shared_ptr< SoapFaultDetail > SoapFaultDetailPtr; +typedef SoapFaultDetailPtr ( *SoapFaultDetailCreator ) ( xmlNodePtr ); + +class SoapResponseFactory; +/** Class representing a SOAP Fault element, to be used as an exception. + */ +class SoapFault : public std::exception +{ + private: + std::string m_faultcode; + std::string m_faultstring; + std::vector< SoapFaultDetailPtr > m_detail; + std::string m_message; + + public: + SoapFault( xmlNodePtr faultNode, SoapResponseFactory* factory ); + virtual ~SoapFault( ) noexcept { }; + + const std::string& getFaultcode ( ) const { return m_faultcode; } + const std::string& getFaultstring ( ) const { return m_faultstring; } + std::vector< SoapFaultDetailPtr > getDetail( ) const { return m_detail; } + + virtual const char* what() const noexcept; +}; + + +/** Class parsing the SOAP response message and extracting the SoapResponse objects. + */ +class SoapResponseFactory +{ + private: + std::map< std::string, SoapResponseCreator > m_mapping; + std::map< std::string, std::string > m_namespaces; + std::map< std::string, SoapFaultDetailCreator > m_detailMapping; + SoapSession* m_session; + + public: + + SoapResponseFactory( ); + SoapResponseFactory( const SoapResponseFactory& copy ); + + SoapResponseFactory& operator=( const SoapResponseFactory& copy ); + + void setMapping( std::map< std::string, SoapResponseCreator > mapping ) { m_mapping = mapping; } + + /** Set the additional namespaces to parse the responses. There is no need to + add the soap / wsdl namespaces... they are automatically added. + */ + void setNamespaces( std::map< std::string, std::string > namespaces ) { m_namespaces = namespaces; } + + void setDetailMapping( std::map< std::string, SoapFaultDetailCreator > mapping ) { m_detailMapping = mapping; } + + void setSession( SoapSession* session ) { m_session = session; } + + /** Get the Soap envelope from the multipart and extract the response objects from it. This + method will also read the possible related parts to construct the response. + */ + std::vector< SoapResponsePtr > parseResponse( RelatedMultipart& multipart ); + + /** Get the Soap envelope from an XML-only file and extract the response objects from it. + */ + std::vector< SoapResponsePtr > parseResponse( std::string& xml ); + + /** Create a SoapResponse object depending on the node we have. This shouldn't be used + directly: only from parseResponse or unit tests. + */ + SoapResponsePtr createResponse( xmlNodePtr node, RelatedMultipart& multipart ); + + std::vector< SoapFaultDetailPtr > parseFaultDetail( xmlNodePtr detailNode ); +}; + + +/** Base class for all SOAP request objects. + + The implementer's toXml() method needs to take care of two things: + \li generate the XML to put in the Soap envelope body + \li add the potential attachement to the multipart. + + There is no need to add the envelope to the multipart: it will + automatically be added as the start part of it by getMultipart(). + This also means that adding parts to the multipart will have to + be done directly on the m_multipart protected member. + + The RelatedMultipart object is the final result to be used. + */ +class SoapRequest : public libcmis::XmlSerializable +{ + protected: + RelatedMultipart m_multipart; + + public: + SoapRequest( ) : m_multipart( ) { }; + virtual ~SoapRequest( ) { }; + + RelatedMultipart& getMultipart( std::string& username, std::string& password ); + + protected: + + std::string createEnvelope( std::string& username, std::string& password ); +}; + +#endif diff --git a/src/libcmis/ws-versioningservice.cxx b/src/libcmis/ws-versioningservice.cxx new file mode 100644 index 0000000..579ec11 --- /dev/null +++ b/src/libcmis/ws-versioningservice.cxx @@ -0,0 +1,138 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-versioningservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +VersioningService::VersioningService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +VersioningService::VersioningService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "VersioningService" ) ) +{ +} + +VersioningService::VersioningService( const VersioningService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +VersioningService::~VersioningService( ) +{ +} + +VersioningService& VersioningService::operator=( const VersioningService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +libcmis::DocumentPtr VersioningService::checkOut( string repoId, string documentId ) +{ + libcmis::DocumentPtr pwc; + + CheckOutRequest request( repoId, documentId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CheckOutResponse* response = dynamic_cast< CheckOutResponse* >( resp ); + if ( response != NULL ) + { + string pwcId = response->getObjectId( ); + libcmis::ObjectPtr object = m_session->getObject( pwcId ); + pwc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + } + } + + return pwc; +} + +void VersioningService::cancelCheckOut( string repoId, string documentId ) +{ + CancelCheckOutRequest request( repoId, documentId ); + m_session->soapRequest( m_url, request ); +} + +libcmis::DocumentPtr VersioningService::checkIn( string repoId, string objectId, bool isMajor, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > stream, string contentType, string fileName, + string comment ) +{ + libcmis::DocumentPtr newVersion; + + CheckInRequest request( repoId, objectId, isMajor, properties, stream, contentType, fileName, comment ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CheckInResponse* response = dynamic_cast< CheckInResponse* >( resp ); + if ( response != NULL ) + { + string newId = response->getObjectId( ); + libcmis::ObjectPtr object = m_session->getObject( newId ); + newVersion = boost::dynamic_pointer_cast< libcmis::Document >( object ); + } + } + + return newVersion; +} + +vector< libcmis::DocumentPtr > VersioningService::getAllVersions( string repoId, string objectId ) +{ + vector< libcmis::DocumentPtr > versions; + + GetAllVersionsRequest request( repoId, objectId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetAllVersionsResponse* response = dynamic_cast< GetAllVersionsResponse* >( resp ); + if ( response != NULL ) + { + versions = response->getObjects( ); + } + } + + return versions; +} diff --git a/src/libcmis/ws-versioningservice.hxx b/src/libcmis/ws-versioningservice.hxx new file mode 100644 index 0000000..5156ebb --- /dev/null +++ b/src/libcmis/ws-versioningservice.hxx @@ -0,0 +1,67 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_VERSIONINGSERVICE_HXX_ +#define _WS_VERSIONINGSERVICE_HXX_ + +#include <string> + +#include <libcmis/document.hxx> + +class WSSession; + +class VersioningService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + VersioningService( WSSession* session ); + VersioningService( const VersioningService& copy ); + ~VersioningService( ); + + VersioningService& operator=( const VersioningService& copy ); + + libcmis::DocumentPtr checkOut( std::string repoId, std::string documentId ); + + void cancelCheckOut( std::string repoId, std::string documentId ); + + libcmis::DocumentPtr checkIn( std::string repoId, std::string objectId, bool isMajor, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, std::string contentType, + std::string fileName, std::string comment ); + + std::vector< libcmis::DocumentPtr > getAllVersions( std::string repoId, std::string objectId ); + + private: + + VersioningService( ); +}; + +#endif diff --git a/src/libcmis/xml-utils.cxx b/src/libcmis/xml-utils.cxx new file mode 100644 index 0000000..3fa2df7 --- /dev/null +++ b/src/libcmis/xml-utils.cxx @@ -0,0 +1,573 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/xml-utils.hxx> + +#include <errno.h> +#include <memory> +#include <sstream> +#include <stdlib.h> + +#include <boost/algorithm/string.hpp> +#include <boost/version.hpp> + +#if BOOST_VERSION >= 106800 +#include <boost/uuid/detail/sha1.hpp> +#else +#include <boost/uuid/sha1.hpp> +#endif +#include <curl/curl.h> + +#if LIBCURL_VERSION_VALUE < 0x070F04 +#define curl_easy_escape( dummy, str, len ) curl_escape( str, len ) +#define curl_easy_unescape( dummy, str, len, dummy2 ) curl_unescape( str, len ) +#endif + +using namespace std; + +namespace +{ + static const char chars64[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + bool lcl_getBufValue( char encoded, int* value ) + { + bool found = false; + const char *i = chars64; + while ( !found && *i ) + { + if ( *i == encoded ) + { + found = true; + *value = ( i - chars64 ); + } + ++i; + } + return found; + } +} + +namespace libcmis +{ + EncodedData::EncodedData( FILE* stream ) : + m_writer( NULL ), + m_stream( stream ), + m_outStream( NULL ), + m_encoding( ), + m_decode( false ), + m_pendingValue( 0 ), + m_pendingRank( 0 ), + m_missingBytes( 0 ) + { + } + + EncodedData::EncodedData( ostream* stream ) : + m_writer( NULL ), + m_stream( NULL ), + m_outStream( stream ), + m_encoding( ), + m_decode( false ), + m_pendingValue( 0 ), + m_pendingRank( 0 ), + m_missingBytes( 0 ) + { + } + + EncodedData::EncodedData( xmlTextWriterPtr writer ) : + m_writer( writer ), + m_stream( NULL ), + m_outStream( NULL ), + m_encoding( ), + m_decode( false ), + m_pendingValue( 0 ), + m_pendingRank( 0 ), + m_missingBytes( 0 ) + { + } + + EncodedData::EncodedData( const EncodedData& copy ) : + m_writer( copy.m_writer ), + m_stream( copy.m_stream ), + m_outStream( copy.m_outStream ), + m_encoding( copy.m_encoding ), + m_decode( copy.m_decode ), + m_pendingValue( copy.m_pendingValue ), + m_pendingRank( copy.m_pendingRank ), + m_missingBytes( copy.m_missingBytes ) + { + } + + EncodedData& EncodedData::operator=( const EncodedData& copy ) + { + if ( this != © ) + { + m_writer = copy.m_writer; + m_stream = copy.m_stream; + m_outStream = copy.m_outStream; + m_encoding = copy.m_encoding; + m_decode = copy.m_decode; + m_pendingValue = copy.m_pendingValue; + m_pendingRank = copy.m_pendingRank; + m_missingBytes = copy.m_missingBytes; + } + return *this; + } + + void EncodedData::write( void* buf, size_t size, size_t nmemb ) + { + if ( m_writer ) + xmlTextWriterWriteRawLen( m_writer, ( xmlChar* )buf, size * nmemb ); + else if ( m_stream ) + fwrite( buf, size, nmemb, m_stream ); + else if ( m_outStream ) + m_outStream->write( ( const char* )buf, size * nmemb ); + } + + void EncodedData::decode( void* buf, size_t size, size_t nmemb ) + { + m_decode = true; + if ( 0 == m_encoding.compare( "base64" ) ) + { + decodeBase64( ( const char* )buf, size * nmemb ); + } + else + write( buf, size, nmemb ); + } + + void EncodedData::encode( void* buf, size_t size, size_t nmemb ) + { + m_decode = false; + if ( 0 == m_encoding.compare( "base64" ) ) + { + encodeBase64( ( const char* )buf, size * nmemb ); + } + else + write( buf, size, nmemb ); + } + + void EncodedData::finish( ) + { + // Flushes the last bytes in base64 encoding / decoding if any + if ( 0 == m_encoding.compare( "base64" ) ) + { + if ( m_decode && ( m_pendingValue != 0 || m_pendingRank != 0 || m_missingBytes != 0 ) ) + { + int missingBytes = m_missingBytes; + if ( 0 == m_missingBytes ) + missingBytes = 4 - m_pendingRank; + + char decoded[3]; + decoded[0] = ( m_pendingValue & 0xFF0000 ) >> 16; + decoded[1] = ( m_pendingValue & 0xFF00 ) >> 8; + decoded[2] = ( m_pendingValue & 0xFF ); + + write( decoded, 1, 3 - missingBytes ); + + m_pendingRank = 0; + m_pendingValue = 0; + m_missingBytes = 0; + } + else if ( !m_decode && ( m_pendingValue != 0 || m_pendingRank != 0 ) ) + { + // Missing bytes should be zeroed: no need to do it + char encoded[4]; + encoded[0] = chars64[ ( m_pendingValue & 0xFC0000 ) >> 18 ]; + encoded[1] = chars64[ ( m_pendingValue & 0x03F000 ) >> 12 ]; + encoded[2] = chars64[ ( m_pendingValue & 0x000FC0 ) >> 6 ]; + encoded[3] = chars64[ ( m_pendingValue & 0x00003F ) ]; + + // Output the padding + int nEquals = 3 - m_pendingRank; + for ( int i = 0; i < nEquals; ++i ) + encoded[ 3 - i ] = '='; + + write( encoded, 1, 4 ); + + m_pendingRank = 0; + m_pendingValue = 0; + } + } + } + + void EncodedData::decodeBase64( const char* buf, size_t len ) + { + unsigned long blockValue = m_pendingValue; + int byteRank = m_pendingRank; + int missingBytes = m_missingBytes; + + size_t i = 0; + while ( i < len ) + { + int value = 0; + if ( lcl_getBufValue( buf[i], &value ) ) + { + blockValue += value << ( ( 3 - byteRank ) * 6 ); + ++byteRank; + } + else if ( buf[i] == '=' ) + { + ++missingBytes; + ++byteRank; + } + + // Reached the end of a block, decode it + if ( byteRank >= 4 ) + { + char decoded[3]; + decoded[0] = ( blockValue & 0xFF0000 ) >> 16; + decoded[1] = ( blockValue & 0xFF00 ) >> 8; + decoded[2] = ( blockValue & 0xFF ); + + write( decoded, 1, 3 - missingBytes ); + + byteRank = 0; + blockValue = 0; + missingBytes = 0; + } + ++i; + } + + // Store the values if the last block is incomplete: they may come later + m_pendingValue = blockValue; + m_pendingRank = byteRank; + m_missingBytes = missingBytes; + } + + void EncodedData::encodeBase64( const char* buf, size_t len ) + { + unsigned long blockValue = m_pendingValue; + int byteRank = m_pendingRank; + + size_t i = 0; + while ( i < len ) + { + // Cast the char to an unsigned char or we'll shift negative values + blockValue += static_cast< unsigned char >( buf[i] ) << ( 2 - byteRank ) * 8; + ++byteRank; + + // Reached the end of a block, encode it + if ( byteRank >= 3 ) + { + char encoded[4]; + encoded[0] = chars64[ ( blockValue & 0xFC0000 ) >> 18 ]; + encoded[1] = chars64[ ( blockValue & 0x03F000 ) >> 12 ]; + encoded[2] = chars64[ ( blockValue & 0x000FC0 ) >> 6 ]; + encoded[3] = chars64[ ( blockValue & 0x00003F ) ]; + + write( encoded, 1, 4 ); + + byteRank = 0; + blockValue = 0; + } + ++i; + } + + // Store the values if the last block is incomplete: they may come later + m_pendingValue = blockValue; + m_pendingRank = byteRank; + } + + HttpResponse::HttpResponse( ) : + m_headers( ), + m_stream( ), + m_data( ) + { + m_stream.reset( new stringstream( ) ); + m_data.reset( new EncodedData( m_stream.get( ) ) ); + } + + void registerNamespaces( xmlXPathContextPtr xpathCtx ) + { + if ( xpathCtx != NULL ) + { + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "app" ), BAD_CAST( NS_APP_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "atom" ), BAD_CAST( NS_ATOM_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmisra" ), BAD_CAST( NS_CMISRA_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmism" ), BAD_CAST( NS_CMISM_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "xsi" ), BAD_CAST( "http://www.w3.org/2001/XMLSchema-instance" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "type" ), BAD_CAST( "cmis:cmisTypeDocumentDefinitionType" ) ); + } + } + + void registerCmisWSNamespaces( xmlXPathContextPtr xpathCtx ) + { + if ( xpathCtx != NULL ) + { + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmisw" ), BAD_CAST( NS_CMISW_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmisra" ), BAD_CAST( NS_CMISRA_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + registerSoapNamespaces( xpathCtx ); + } + } + + void registerSoapNamespaces( xmlXPathContextPtr xpathCtx ) + { + if ( xpathCtx != NULL ) + { + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "soap" ), BAD_CAST( NS_SOAP_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "soap-env" ), BAD_CAST( NS_SOAP_ENV_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "wsdl" ), BAD_CAST ( "http://schemas.xmlsoap.org/wsdl/" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "ns" ), BAD_CAST ( "http://schemas.xmlsoap.org/soap/encoding/" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "jaxws" ), BAD_CAST( "http://java.sun.com/xml/ns/jaxws" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "xsd" ), BAD_CAST ( "http://www.w3.org/2001/XMLSchema" ) ); + } + } + + string getXPathValue( xmlXPathContextPtr xpathCtx, string req ) + { + string value; + if ( xpathCtx != NULL ) + { + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( req.c_str() ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlChar* pContent = xmlNodeGetContent( xpathObj->nodesetval->nodeTab[0] ); + value = string( ( char* )pContent ); + xmlFree( pContent ); + } + xmlXPathFreeObject( xpathObj ); + } + + return value; + } + + xmlDocPtr wrapInDoc( xmlNodePtr entryNd ) + { + xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); + if ( entryNd != NULL ) + { + xmlNodePtr entryCopy = xmlCopyNode( entryNd, 1 ); + xmlDocSetRootElement( doc, entryCopy ); + } + return doc; + } + + string getXmlNodeAttributeValue( xmlNodePtr node, + const char* attributeName, + const char* defaultValue ) + { + xmlChar* xmlStr = xmlGetProp( node, BAD_CAST( attributeName ) ); + if ( xmlStr == NULL ) + { + if ( !defaultValue ) + throw Exception( "Missing attribute" ); + else + return string( defaultValue ); + } + string value( ( char * ) xmlStr ); + xmlFree( xmlStr ); + return value; + } + + boost::posix_time::ptime parseDateTime( string dateTimeStr ) + { + boost::posix_time::ptime t( boost::date_time::not_a_date_time ); + // Get the time zone offset + boost::posix_time::time_duration tzOffset( boost::posix_time::duration_from_string( "+00:00" ) ); + + if ( dateTimeStr.empty( ) ) + return t; // obviously not a time + + size_t teePos = dateTimeStr.find( 'T' ); + if ( teePos == string::npos || teePos == dateTimeStr.size() - 1 ) + return t; // obviously not a time + + string noTzStr = dateTimeStr.substr( 0, teePos + 1 ); + string timeStr = dateTimeStr.substr( teePos + 1 ); + + // Get the TZ if any + if ( timeStr[ timeStr.size() - 1] == 'Z' ) + { + noTzStr += timeStr.substr( 0, timeStr.size() - 1 ); + } + else + { + size_t tzPos = timeStr.find( '+' ); + if ( tzPos == string::npos ) + tzPos = timeStr.find( '-' ); + + if ( tzPos != string::npos ) + { + noTzStr += timeStr.substr( 0, tzPos ); + + // Check the validity of the TZ value + string tzStr = timeStr.substr( tzPos ); + try + { + tzOffset = boost::posix_time::time_duration( boost::posix_time::duration_from_string( tzStr.c_str() ) ); + } + catch ( const std::exception& ) + { + // Error converting, not a datetime + return t; + } + + } + else + noTzStr += timeStr; + } + + // Remove all the '-' and ':' + size_t pos = noTzStr.find_first_of( ":-" ); + while ( pos != string::npos ) + { + noTzStr.erase( pos, 1 ); + pos = noTzStr.find_first_of( ":-" ); + } + try + { + t = boost::posix_time::from_iso_string( noTzStr.c_str( ) ); + t = t + tzOffset; + } + catch ( const std::exception& ) + { + // Ignore boost parsing errors: will result in not_a_date_time + } + + return t; + } + + string writeDateTime( boost::posix_time::ptime time ) + { + string str; + if ( !time.is_special( ) ) + { + str = boost::posix_time::to_iso_extended_string( time ); + str += "Z"; + } + return str; + } + + bool parseBool( string boolStr ) + { + bool value = false; + if ( boolStr == "true" || boolStr == "1" ) + value = true; + else if ( boolStr == "false" || boolStr == "0" ) + value = false; + else + throw Exception( string( "Invalid xsd:boolean input: " ) + boolStr ); + return value; + } + + long parseInteger( string intStr ) + { + char* end; + errno = 0; + long value = strtol( intStr.c_str(), &end, 0 ); + + if ( ( ERANGE == errno && ( LONG_MAX == value || LONG_MIN == value ) ) || + ( errno != 0 && value == 0 ) ) + { + throw Exception( string( "xsd:integer input can't fit to long: " ) + intStr ); + } + else if ( !string( end ).empty( ) ) + { + throw Exception( string( "Invalid xsd:integer input: " ) + intStr ); + } + + return value; + } + + double parseDouble( string doubleStr ) + { + char* end; + errno = 0; + double value = strtod( doubleStr.c_str(), &end ); + + if ( ( ERANGE == errno ) || ( errno != 0 && value == 0 ) ) + { + throw Exception( string( "xsd:decimal input can't fit to double: " ) + doubleStr ); + } + else if ( !string( end ).empty( ) ) + { + throw Exception( string( "Invalid xsd:decimal input: " ) + doubleStr ); + } + + return value; + } + + string trim( const string& str ) + { + return boost::trim_copy_if( str, boost::is_any_of( " \t\r\n" ) ); + } + + std::string base64encode( const std::string& str ) + { + stringstream stream; + EncodedData data( &stream ); + data.setEncoding( "base64" ); + data.encode( ( void * )str.c_str( ), size_t( 1 ), str.size() ); + data.finish( ); + return stream.str(); + } + + std::string sha1( const std::string& str ) + { + boost::uuids::detail::sha1 sha1; + sha1.process_bytes( str.c_str(), str.size() ); + + unsigned int digest[5]; + sha1.get_digest( digest ); + + stringstream out; + // Setup writing mode. Every number must produce eight + // hexadecimal digits, including possible leading 0s, or we get + // less than 40 digits as result. + out << hex << setfill('0') << right; + for ( int i = 0; i < 5; ++i ) + out << setw(8) << digest[i]; + return out.str(); + } + + int stringstream_write_callback( void * context, const char * s, int len ) + { + stringstream * ss=static_cast< stringstream * >( context ); + if ( ss ) + { + ss->write( s, len ); + return len; + } + return 0; + } + + string escape( string str ) + { + std::unique_ptr< char, void(*)( void* ) > escaped{ curl_easy_escape( NULL, str.c_str(), str.length() ), curl_free }; + return escaped.get(); + } + + string unescape( string str ) + { + std::unique_ptr< char, void(*)( void* ) > unescaped{ curl_easy_unescape( NULL, str.c_str(), str.length(), NULL ), curl_free }; + return unescaped.get(); + } +} |