summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:41:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:41:20 +0000
commit2cd20b3e73d0162e3fa23ebcee8e89a3b967ca6f (patch)
tree754a142de5cd8f987abe255e8a15b5ef94109da4 /src
parentInitial commit. (diff)
downloadlibcmis-2cd20b3e73d0162e3fa23ebcee8e89a3b967ca6f.tar.xz
libcmis-2cd20b3e73d0162e3fa23ebcee8e89a3b967ca6f.zip
Adding upstream version 0.6.2.upstream/0.6.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am17
-rw-r--r--src/cmis-client.cxx1158
-rw-r--r--src/libcmis-c/Makefile.am39
-rw-r--r--src/libcmis-c/allowable-actions.cxx56
-rw-r--r--src/libcmis-c/document.cxx448
-rw-r--r--src/libcmis-c/error.cxx74
-rw-r--r--src/libcmis-c/folder.cxx369
-rw-r--r--src/libcmis-c/internals.hxx242
-rw-r--r--src/libcmis-c/oauth2-data.cxx110
-rw-r--r--src/libcmis-c/object-type.cxx388
-rw-r--r--src/libcmis-c/object.cxx512
-rw-r--r--src/libcmis-c/property-type.cxx201
-rw-r--r--src/libcmis-c/property.cxx200
-rw-r--r--src/libcmis-c/rendition.cxx110
-rw-r--r--src/libcmis-c/repository.cxx208
-rw-r--r--src/libcmis-c/session-factory.cxx239
-rw-r--r--src/libcmis-c/session.cxx305
-rw-r--r--src/libcmis-c/vectors.cxx139
-rw-r--r--src/libcmis/Makefile.am147
-rw-r--r--src/libcmis/allowable-actions.cxx294
-rw-r--r--src/libcmis/atom-document.cxx480
-rw-r--r--src/libcmis/atom-document.hxx66
-rw-r--r--src/libcmis/atom-folder.cxx322
-rw-r--r--src/libcmis/atom-folder.hxx56
-rw-r--r--src/libcmis/atom-object-type.cxx167
-rw-r--r--src/libcmis/atom-object-type.hxx63
-rw-r--r--src/libcmis/atom-object.cxx486
-rw-r--r--src/libcmis/atom-object.hxx106
-rw-r--r--src/libcmis/atom-session.cxx349
-rw-r--r--src/libcmis/atom-session.hxx90
-rw-r--r--src/libcmis/atom-workspace.cxx228
-rw-r--r--src/libcmis/atom-workspace.hxx91
-rw-r--r--src/libcmis/base-session.cxx146
-rw-r--r--src/libcmis/base-session.hxx104
-rw-r--r--src/libcmis/document.cxx107
-rw-r--r--src/libcmis/dummy.cxx0
-rw-r--r--src/libcmis/folder.cxx92
-rw-r--r--src/libcmis/gdrive-allowable-actions.hxx102
-rw-r--r--src/libcmis/gdrive-document.cxx250
-rw-r--r--src/libcmis/gdrive-document.hxx90
-rw-r--r--src/libcmis/gdrive-folder.cxx192
-rw-r--r--src/libcmis/gdrive-folder.hxx66
-rw-r--r--src/libcmis/gdrive-object-type.cxx141
-rw-r--r--src/libcmis/gdrive-object-type.hxx46
-rw-r--r--src/libcmis/gdrive-object.cxx276
-rw-r--r--src/libcmis/gdrive-object.hxx87
-rw-r--r--src/libcmis/gdrive-property.cxx79
-rw-r--r--src/libcmis/gdrive-property.hxx51
-rw-r--r--src/libcmis/gdrive-repository.cxx55
-rw-r--r--src/libcmis/gdrive-repository.hxx41
-rw-r--r--src/libcmis/gdrive-session.cxx254
-rw-r--r--src/libcmis/gdrive-session.hxx72
-rw-r--r--src/libcmis/gdrive-utils.cxx221
-rw-r--r--src/libcmis/gdrive-utils.hxx67
-rw-r--r--src/libcmis/http-session.cxx994
-rw-r--r--src/libcmis/http-session.hxx179
-rw-r--r--src/libcmis/json-utils.cxx306
-rw-r--r--src/libcmis/json-utils.hxx87
-rw-r--r--src/libcmis/oauth2-data.cxx95
-rw-r--r--src/libcmis/oauth2-handler.cxx196
-rw-r--r--src/libcmis/oauth2-handler.hxx97
-rw-r--r--src/libcmis/oauth2-providers.cxx246
-rw-r--r--src/libcmis/oauth2-providers.hxx63
-rw-r--r--src/libcmis/object-type.cxx368
-rw-r--r--src/libcmis/object.cxx398
-rw-r--r--src/libcmis/onedrive-allowable-actions.hxx102
-rw-r--r--src/libcmis/onedrive-document.cxx177
-rw-r--r--src/libcmis/onedrive-document.hxx75
-rw-r--r--src/libcmis/onedrive-folder.cxx167
-rw-r--r--src/libcmis/onedrive-folder.hxx64
-rw-r--r--src/libcmis/onedrive-object-type.cxx118
-rw-r--r--src/libcmis/onedrive-object-type.hxx46
-rw-r--r--src/libcmis/onedrive-object.cxx198
-rw-r--r--src/libcmis/onedrive-object.hxx76
-rw-r--r--src/libcmis/onedrive-property.cxx78
-rw-r--r--src/libcmis/onedrive-property.hxx52
-rw-r--r--src/libcmis/onedrive-repository.cxx54
-rw-r--r--src/libcmis/onedrive-repository.hxx40
-rw-r--r--src/libcmis/onedrive-session.cxx207
-rw-r--r--src/libcmis/onedrive-session.hxx75
-rw-r--r--src/libcmis/onedrive-utils.cxx131
-rw-r--r--src/libcmis/onedrive-utils.hxx60
-rw-r--r--src/libcmis/property-type.cxx254
-rw-r--r--src/libcmis/property.cxx232
-rw-r--r--src/libcmis/rendition.cxx195
-rw-r--r--src/libcmis/repository.cxx294
-rw-r--r--src/libcmis/session-factory.cxx164
-rw-r--r--src/libcmis/sharepoint-allowable-actions.hxx102
-rw-r--r--src/libcmis/sharepoint-document.cxx213
-rw-r--r--src/libcmis/sharepoint-document.hxx72
-rw-r--r--src/libcmis/sharepoint-folder.cxx205
-rw-r--r--src/libcmis/sharepoint-folder.hxx67
-rw-r--r--src/libcmis/sharepoint-object-type.cxx105
-rw-r--r--src/libcmis/sharepoint-object-type.hxx46
-rw-r--r--src/libcmis/sharepoint-object.cxx200
-rw-r--r--src/libcmis/sharepoint-object.hxx74
-rw-r--r--src/libcmis/sharepoint-property.cxx79
-rw-r--r--src/libcmis/sharepoint-property.hxx50
-rw-r--r--src/libcmis/sharepoint-repository.cxx65
-rw-r--r--src/libcmis/sharepoint-repository.hxx39
-rw-r--r--src/libcmis/sharepoint-session.cxx424
-rw-r--r--src/libcmis/sharepoint-session.hxx91
-rw-r--r--src/libcmis/sharepoint-utils.cxx133
-rw-r--r--src/libcmis/sharepoint-utils.hxx54
-rw-r--r--src/libcmis/ws-document.cxx135
-rw-r--r--src/libcmis/ws-document.hxx60
-rw-r--r--src/libcmis/ws-folder.cxx68
-rw-r--r--src/libcmis/ws-folder.hxx54
-rw-r--r--src/libcmis/ws-navigationservice.cxx101
-rw-r--r--src/libcmis/ws-navigationservice.hxx60
-rw-r--r--src/libcmis/ws-object-type.cxx90
-rw-r--r--src/libcmis/ws-object-type.hxx57
-rw-r--r--src/libcmis/ws-object.cxx130
-rw-r--r--src/libcmis/ws-object.hxx61
-rw-r--r--src/libcmis/ws-objectservice.cxx242
-rw-r--r--src/libcmis/ws-objectservice.hxx95
-rw-r--r--src/libcmis/ws-relatedmultipart.cxx353
-rw-r--r--src/libcmis/ws-relatedmultipart.hxx138
-rw-r--r--src/libcmis/ws-repositoryservice.cxx136
-rw-r--r--src/libcmis/ws-repositoryservice.hxx71
-rw-r--r--src/libcmis/ws-requests.cxx877
-rw-r--r--src/libcmis/ws-requests.hxx786
-rw-r--r--src/libcmis/ws-session.cxx417
-rw-r--r--src/libcmis/ws-session.hxx124
-rw-r--r--src/libcmis/ws-soap.cxx335
-rw-r--r--src/libcmis/ws-soap.hxx178
-rw-r--r--src/libcmis/ws-versioningservice.cxx138
-rw-r--r--src/libcmis/ws-versioningservice.hxx67
-rw-r--r--src/libcmis/xml-utils.cxx573
129 files changed, 23552 insertions, 0 deletions
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 != &copy )
+ 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 != &copy )
+ {
+ 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 != &copy )
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &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;
+ }
+
+ 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 != &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;
+ }
+
+ 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 != &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;
+ }
+ 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 != &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;
+ }
+
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &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;
+ }
+
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &copy )
+ {
+ 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 != &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;
+ }
+ 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();
+ }
+}