diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:41:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:41:20 +0000 |
commit | 2cd20b3e73d0162e3fa23ebcee8e89a3b967ca6f (patch) | |
tree | 754a142de5cd8f987abe255e8a15b5ef94109da4 /src | |
parent | Initial commit. (diff) | |
download | libcmis-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')
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 != © ) + m_msg = copy.m_msg; + return *this; + } + + virtual const char* what() const noexcept { return m_msg.c_str(); } +}; + +class CmisClient +{ + private: + variables_map& m_vm; + public: + CmisClient( variables_map& vm ) : m_vm( vm ) { } + + libcmis::Session* getSession( bool inGetRepositories = false ); + + void execute( ); + + void printHelp( ); + + static options_description getOptionsDescription( ); + + private: + map< int, string > getSessionParams(); + map< string, string > getObjectProperties( ); +}; + +map< string, string > CmisClient::getObjectProperties( ) +{ + map< string, string > result; + if ( m_vm.count( "object-property" ) > 0 ) + { + vector< string > params = m_vm["object-property"].as< vector< string > >( ); + for ( vector< string >::iterator it = params.begin( ); it != params.end( ); ++it ) + { + size_t pos = it->find( "=" ); + if ( pos != string::npos ) + { + string name = it->substr( 0, pos ); + string value = it->substr( pos + 1 ); + result.insert( pair< string, string >( name, value ) ); + } + } + } + + return result; +} + +libcmis::Session* CmisClient::getSession( bool inGetRepositories ) +{ + if ( m_vm.count( "url" ) == 0 ) + throw CommandException( "Missing binding URL" ); + + // Setup the authentication provider in case we have missing credentials + libcmis::AuthProviderPtr provider( new CinAuthProvider( ) ); + libcmis::SessionFactory::setAuthenticationProvider( provider ); + + libcmis::CertValidationHandlerPtr certValidator( new CinCertValidationHandler( ) ); + libcmis::SessionFactory::setCertificateValidationHandler( certValidator ); + + string url = m_vm["url"].as<string>(); + + // Look for the credentials + string username; + string password; + if ( m_vm.count( "username" ) > 0 ) + { + username = m_vm["username"].as< string >(); + + if ( m_vm.count( "password" ) > 0 ) + password = m_vm["password"].as< string >(); + } + + // Look for proxy settings + string proxyUrl; + string proxyUser; + string proxyPass; + string noproxy; + if ( m_vm.count( "proxy" ) > 0 ) + { + proxyUrl = m_vm["proxy"].as< string >(); + + if ( m_vm.count( "proxy-user" ) > 0 ) + proxyUser = m_vm["proxy-user"].as< string >(); + + if ( m_vm.count( "proxy-password" ) > 0 ) + proxyPass = m_vm["proxy-password"].as< string >(); + + if ( m_vm.count( "noproxy" ) > 0 ) + noproxy = m_vm["noproxy"].as< string >(); + + libcmis::SessionFactory::setProxySettings( proxyUrl, noproxy, proxyUser, proxyPass ); + } + + bool verbose = m_vm.count( "verbose" ) > 0; + + libcmis::Session* session = NULL; + string repoId; + // The repository ID is needed to initiate a session + if ( m_vm.count( "repository" ) == 0 && !inGetRepositories ) + { + // Do we have a single repository on the server? + session = getSession( true ); + if ( session != NULL ) + { + vector< libcmis::RepositoryPtr > repos = session->getRepositories(); + if ( repos.size() == 1 ) + { + repoId = repos.front( )->getId( ); + session->setRepository( repoId ); + } + } + + // We couldn't auto-guess the repository, then throw an error + if ( repoId.empty( ) ) + { + delete session; + throw CommandException( "Missing repository ID" ); + } + } + else if ( m_vm.count( "repository" ) > 0 ) + { + repoId = m_vm["repository"].as< string >(); + } + + if ( session == NULL ) + { + bool noSslCheck = m_vm.count( "no-ssl-check" ) > 0; + + // Should we use OAuth2? + string oauth2ClientId; + string oauth2ClientSecret; + string oauth2AuthUrl; + string oauth2TokenUrl; + string oauth2RedirectUri; + string oauth2Scope; + if ( m_vm.count( "oauth2-client-id" ) > 0 ) + oauth2ClientId = m_vm["oauth2-client-id"].as< string >(); + if ( m_vm.count( "oauth2-client-secret" ) > 0 ) + oauth2ClientSecret = m_vm["oauth2-client-secret"].as< string >(); + if ( m_vm.count( "oauth2-auth-url" ) > 0 ) + oauth2AuthUrl = m_vm["oauth2-auth-url"].as< string >(); + if ( m_vm.count( "oauth2-token-url" ) > 0 ) + oauth2TokenUrl = m_vm["oauth2-token-url"].as< string >(); + if ( m_vm.count( "oauth2-redirect-uri" ) > 0 ) + oauth2RedirectUri = m_vm["oauth2-redirect-uri"].as< string >(); + if ( m_vm.count( "oauth2-scope" ) > 0 ) + oauth2Scope = m_vm["oauth2-scope"].as< string >(); + if ( m_vm.count( "oauth2-auth-code" ) > 0 ) + AuthCodeProvider::setAuthCode( m_vm["oauth2-auth-code"].as< string >() ); + + libcmis::OAuth2DataPtr oauth2Data( new libcmis::OAuth2Data( oauth2AuthUrl, oauth2TokenUrl, + oauth2Scope, oauth2RedirectUri, oauth2ClientId, oauth2ClientSecret) ); + + if ( oauth2Data->isComplete( ) ) + { + // Set the fallback AuthCode provider + if ( m_vm.count( "oauth2-auth-code" ) > 0 ) + { + libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthCodeProvider::getAuthCode ); + } + else + { + libcmis::SessionFactory::setOAuth2AuthCodeProvider( lcl_queryAuthCode ); + } + } + else + { + oauth2Data.reset( ); + } + + session = libcmis::SessionFactory::createSession( url, username, password, repoId, noSslCheck, oauth2Data, verbose ); + } + + return session; +} + +void CmisClient::execute( ) +{ + if ( ( m_vm.count( "help" ) > 0 ) || m_vm.count( "command" ) != 1 ) + { + printHelp(); + return; + } + + if ( m_vm.count( "command" ) == 1 ) + { + string command = m_vm["command"].as<string>(); + if ( "list-repos" == command ) + { + libcmis::Session* session = getSession( true ); + if ( session != NULL ) + { + vector< libcmis::RepositoryPtr > repos = session->getRepositories(); + + cout << "Repositories: name (id)" << endl; + for ( vector< libcmis::RepositoryPtr >::iterator it = repos.begin(); it != repos.end(); ++it ) + cout << "\t" << ( *it )->getName( ) << " (" << ( *it )->getId( ) << ")" << endl; + } + else + { + cerr << "Couldn't create a session for some reason" << endl; + } + } + else if ( "show-root" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + libcmis::FolderPtr root = session->getRootFolder(); + if ( root.get() ) + { + cout << "------------------------------------------------" << endl; + cout << root->toString() << endl; + } + } + else if ( "repo-infos" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + libcmis::RepositoryPtr repo = session->getRepository( ); + + if ( repo ) + { + cout << "------------------------------------------------" << endl; + cout << repo->toString() << endl; + } + else + throw CommandException( "Please select a repository" ); + } + else if ( "type-by-id" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the types to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node ids to show as command args" ); + + vector< string > ids = m_vm["args"].as< vector< string > >( ); + + + for ( vector< string >::iterator it = ids.begin(); it != ids.end(); ++it ) + { + cout << "------------------------------------------------" << endl; + try + { + libcmis::ObjectTypePtr type = session->getType( *it ); + cout << type->toString() << endl; + } + catch ( const libcmis::Exception& e ) + { + cout << e.what() << endl; + } + } + } + else if ( "show-by-id" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node ids to show as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + + for ( vector< string >::iterator it = objIds.begin(); it != objIds.end(); ++it ) + { + libcmis::ObjectPtr cmisObj = session->getObject( *it ); + cout << "------------------------------------------------" << endl; + if ( cmisObj.get() ) + cout << cmisObj->toString() << endl; + else + cout << "No such node: " << *it << endl; + } + } + else if ( "show-by-path" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the paths of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node paths to show as command args" ); + + vector< string > objPaths = m_vm["args"].as< vector< string > >( ); + + + for ( vector< string >::iterator it = objPaths.begin(); it != objPaths.end(); ++it ) + { + libcmis::ObjectPtr cmisObj = session->getObjectByPath( *it ); + cout << "------------------------------------------------" << endl; + if ( cmisObj.get() ) + cout << cmisObj->toString() << endl; + else + cout << "No such node: " << *it << endl; + } + } + else if ( "get-content" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + if ( objIds.empty( ) ) + throw CommandException( "Please provide a content object Id" ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != document ) + { + // TODO Handle name clashes + string streamId; + if ( m_vm.count( "stream-id" ) > 0 ) + streamId = m_vm["stream-id"].as<string>(); + + boost::shared_ptr< istream > in = document->getContentStream( streamId ); + ofstream out( document->getContentFilename().c_str() ); + out << in->rdbuf(); + out.close(); + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else if ( "set-content" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + if ( objIds.empty( ) ) + throw CommandException( "Please provide a content object Id" ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + libcmis::Document* document = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != document ) + { + if ( m_vm.count( "input-file" ) == 0 ) + throw CommandException( "Missing --input-file" ); + if ( m_vm.count( "input-type" ) == 0 ) + throw CommandException( "Missing --input-type" ); + + string type = m_vm["input-type"].as<string>(); + string filename; + if ( m_vm.count( "input-name" ) > 0 ) + filename = m_vm["input-name"].as<string>(); + string file = m_vm["input-file"].as<string>(); + ifstream is( file.c_str(), ifstream::in ); + boost::shared_ptr< ostream > os ( new ostream ( is.rdbuf( ) ) ); + if ( is.fail( ) ) + throw CommandException( string( "Unable to open file " ) + file ); + + document->setContentStream( os, type, filename ); + + is.close( ); + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else if ( "create-folder" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > >( ); + if ( args.size() < 2 ) + throw CommandException( "Please provide a parent Id and folder name" ); + + libcmis::FolderPtr parent = session->getFolder( args[0] ); + + // Get the folder type to create + string folderType( "cmis:folder" ); + if ( m_vm.count( "object-type" ) != 0 ) + folderType = m_vm["object-type"].as<string>( ); + + libcmis::ObjectTypePtr type = session->getType( folderType ); + if ( "cmis:folder" != type->getBaseType( )->getId( ) ) + throw CommandException( string( "Not a folder type: " ) + folderType ); + + PropertyPtrMap properties; + map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( ); + + // Set the name + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( string( "cmis:name" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:name on the object type... weird" ) ); + vector< string > nameValues; + string newFolderName = args[1]; + for ( unsigned int i = 2; i < args.size( ); i++ ) + { + newFolderName += " " + args[i]; + } + nameValues.push_back( newFolderName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( typeIt->second, nameValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // Set the objectTypeId + typeIt = propertiesTypes.find( string( "cmis:objectTypeId" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:objectTypeId on the object type... weird" ) ); + vector< string > typeIdValues; + typeIdValues.push_back( folderType ); + libcmis::PropertyPtr typeIdProperty( new libcmis::Property( typeIt->second, typeIdValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeIdProperty ) ); + + // Checks for the properties to set if any + map< string, string > propsToSet = getObjectProperties( ); + for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it ) + { + // Create the CMIS property if it exists + typeIt = propertiesTypes.find( it->first ); + if ( typeIt != propertiesTypes.end( ) ) + { + vector< string > values; + values.push_back( it->second ); + libcmis::PropertyPtr cmisProperty( new libcmis::Property( typeIt->second, values ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( it->first, cmisProperty ) ); + } + } + + libcmis::FolderPtr created = parent->createFolder( properties ); + + cout << "------------------------------------------------" << endl; + cout << created->toString() << endl; + } + else if ( "create-document" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > >( ); + if ( args.size() < 2 ) + throw CommandException( "Please provide a parent Id and document name" ); + + libcmis::FolderPtr parent = session->getFolder( args[0] ); + + // Get the document type to create + string documentType( "cmis:document" ); + if ( m_vm.count( "object-type" ) != 0 ) + documentType = m_vm["object-type"].as<string>( ); + + libcmis::ObjectTypePtr type = session->getType( documentType ); + if ( "cmis:document" != type->getBaseType( )->getId( ) ) + throw CommandException( string( "Not a document type: " ) + documentType ); + + PropertyPtrMap properties; + map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( ); + + // Set the name + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( string( "cmis:name" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:name on the object type... weird" ) ); + vector< string > nameValues; + string newDocumentName = args[1]; + for ( unsigned int i = 2; i < args.size( ); i++ ) + { + newDocumentName += " " + args[i]; + } + nameValues.push_back( newDocumentName ); + libcmis::PropertyPtr nameProperty( new libcmis::Property( typeIt->second, nameValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) ); + + // Set the objectTypeId + typeIt = propertiesTypes.find( string( "cmis:objectTypeId" ) ); + if ( typeIt == propertiesTypes.end( ) ) + throw CommandException( string( "No cmis:objectTypeId on the object type... weird" ) ); + vector< string > typeIdValues; + typeIdValues.push_back( documentType ); + libcmis::PropertyPtr typeIdProperty( new libcmis::Property( typeIt->second, typeIdValues ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeIdProperty ) ); + + // Checks for the properties to set if any + map< string, string > propsToSet = getObjectProperties( ); + for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it ) + { + // Create the CMIS property if it exists + typeIt = propertiesTypes.find( it->first ); + if ( typeIt != propertiesTypes.end( ) ) + { + vector< string > values; + values.push_back( it->second ); + libcmis::PropertyPtr cmisProperty( new libcmis::Property( typeIt->second, values ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( it->first, cmisProperty ) ); + } + } + + // Get the content type and stream + boost::shared_ptr< ostream > contentStream; + string contentType; + string filename; + + bool hasInputFile = m_vm.count( "input-file" ) != 0; + bool hasInputType = m_vm.count( "input-type" ) != 0; + bool hasInputName = m_vm.count( "input-name" ) != 0; + + if ( hasInputType && !hasInputFile ) + throw CommandException( "Missing --input-file" ); + if ( hasInputFile && !hasInputType ) + throw CommandException( "Missing --input-type" ); + + if ( hasInputFile && hasInputType ) + { + contentType = m_vm["input-type"].as<string>(); + string file = m_vm["input-file"].as<string>(); + fstream is( file.c_str() ); + if ( is.fail( ) ) + throw CommandException( string( "Unable to open file " ) + file ); + contentStream.reset( new ostringstream( ios_base::out | ios_base::in ) ); + + *contentStream << is.rdbuf(); + } + + if ( hasInputName ) + filename = m_vm[ "input-name" ].as< string >( ); + + // Actually create the document + libcmis::DocumentPtr created = parent->createDocument( properties, contentStream, contentType, filename ); + + cout << "------------------------------------------------" << endl; + cout << created->toString() << endl; + } + else if ( "update-object" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > >( ); + if ( args.size() != 1 ) + throw CommandException( "Please provide an object id" ); + + libcmis::ObjectPtr object = session->getObject( args[0] ); + libcmis::ObjectTypePtr type = session->getType( object->getType( ) ); + map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( ); + + PropertyPtrMap properties; + + // Checks for the properties to set if any + map< string, string > propsToSet = getObjectProperties( ); + for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it ) + { + // Create the CMIS property if it exists + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( it->first ); + if ( typeIt != propertiesTypes.end( ) && typeIt->second->isUpdatable( ) ) + { + vector< string > values; + values.push_back( it->second ); + libcmis::PropertyPtr property( new libcmis::Property( typeIt->second, values ) ); + properties[ it->first ] = property; + } + } + + libcmis::ObjectPtr updated = object->updateProperties( properties ); + + cout << "------------------------------------------------" << endl; + // Output updated instead of object as it may be different depending on the server + cout << updated->toString() << endl; + } + else if ( "move-object" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + vector< string > args = m_vm["args"].as< vector< string > > ( ); + if ( args.size() != 3 ) + throw CommandException( "Please provide an object id and source and destination folder ids" ); + string& objId = args[0]; + string& srcId = args[1]; + string& dstId = args[2]; + + libcmis::ObjectPtr obj = session->getObject( objId ); + + libcmis::ObjectPtr src = session->getObject( srcId ); + libcmis::FolderPtr srcFolder = boost::dynamic_pointer_cast< libcmis::Folder > ( src ); + if ( !srcFolder ) + throw CommandException( "Source object is not a folder" ); + + libcmis::ObjectPtr dst = session->getObject( dstId ); + libcmis::FolderPtr dstFolder = boost::dynamic_pointer_cast< libcmis::Folder > ( dst ); + if ( !dstFolder ) + throw CommandException( "Destinaton object is not a folder" ); + + obj->move( srcFolder, dstFolder ); + + cout << "------------------------------------------------" << endl; + cout << obj->toString( ) << endl; + } + else if ( "delete" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node ids to delete as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + vector< string > errors; + for ( vector< string >::iterator it = objIds.begin(); it != objIds.end(); ++it ) + { + libcmis::ObjectPtr cmisObj = session->getObject( *it ); + libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( cmisObj.get() ); + if ( NULL != folder ) + { + try + { + vector< string > failed = folder->removeTree( ); + string error; + for ( vector< string >::iterator dumpIt = failed.begin( ); + dumpIt != failed.end( ); ++dumpIt ) + { + if ( dumpIt == failed.begin( ) ) + error += "Failed to remove children nodes: "; + else + error += ", "; + error = *dumpIt; + } + if ( !error.empty( ) ) + errors.push_back( error ); + } + catch ( const libcmis::Exception& e ) + { + string msg = *it + ": " + e.what( ); + errors.push_back( msg ); + } + } + else if ( cmisObj.get() ) + { + try + { + cmisObj->remove( ); + } + catch ( const libcmis::Exception& e ) + { + string msg = *it + ": " + e.what( ); + errors.push_back( msg ); + } + } + else + { + string msg = "No such node: " + *it; + errors.push_back( msg ); + } + } + + // Show the errors + if ( !errors.empty( ) ) + { + cout << "Errors:" << endl; + for ( vector< string >::iterator it = errors.begin( ); it != errors.end( ); ++it ) + { + cout << "\t" << *it << endl; + } + } + else + { + cout << "All nodes have been removed" << endl; + } + } + else if ( "checkout" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node id to checkout as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + if ( cmisObj.get() ) + { + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != doc ) + { + libcmis::DocumentPtr pwc = doc->checkOut( ); + if ( pwc.get( ) ) + { + cout << "------------------------------------------------" << endl; + cout << pwc->toString() << endl; + } + else + cout << "No Private Working Copy returned?" << endl; + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "cancel-checkout" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the private working copy object id to cancel as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() ); + cout << "------------------------------------------------" << endl; + if ( cmisObj.get() ) + { + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( cmisObj.get() ); + if ( NULL != doc ) + { + doc->cancelCheckout( ); + cout << "Checkout cancelled" << endl; + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "checkin" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node id to checkin as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr object = session->getObject( objIds.front() ); + if ( object.get() ) + { + // Create the properties map + PropertyPtrMap properties; + map< string, string > propsToSet = getObjectProperties( ); + libcmis::ObjectTypePtr type = session->getType( object->getType( ) ); + map< string, libcmis::PropertyTypePtr > propertyTypes = type->getPropertiesTypes( ); + for ( map< string, string >::iterator propIt = propsToSet.begin(); + propIt != propsToSet.end(); ++propIt ) + { + string name = propIt->first; + map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertyTypes.find( name ); + if ( typeIt != propertyTypes.end( ) ) + { + vector< string > values; + values.push_back( propIt->second ); + libcmis::PropertyPtr prop( new libcmis::Property( typeIt->second, values ) ); + properties.insert( pair< string, libcmis::PropertyPtr >( name, prop ) ); + } + } + + // Get the content stream if any + string contentType; + string filename; + boost::shared_ptr< ostream > stream; + if ( m_vm.count( "input-file" ) > 0 ) + { + string file = m_vm["input-file"].as<string>(); + ifstream is( file.c_str(), ios_base::in ); + stringstream os; + os << is.rdbuf( ); + if ( is.fail( ) ) + throw CommandException( string( "Unable to open file " ) + file ); + + string content = os.str( ); + stream.reset( new stringstream( content ) ); + is.close( ); + + if ( m_vm.count( "input-type" ) > 0 ) + { + contentType = m_vm["input-type"].as<string>(); + } + if ( m_vm.count( "input-name" ) > 0 ) + { + filename = m_vm["input-name"].as<string>(); + } + } + + bool major = false; + if ( m_vm.count( "major" ) > 0 ) + major = "yes"; + + string comment; + if ( m_vm.count( "message" ) > 0 ) + comment = m_vm["message"].as< string >( ); + + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( object.get() ); + if ( NULL != doc ) + { + libcmis::DocumentPtr newDoc = doc->checkIn( major, comment, properties, stream, contentType, filename ); + + cout << "------------------------------------------------" << endl; + cout << newDoc->toString() << endl; + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "get-versions" == command ) + { + unique_ptr<libcmis::Session> session( getSession( ) ); + + // Get the ids of the objects to fetch + if ( m_vm.count( "args" ) == 0 ) + throw CommandException( "Please provide the node id to get versions from as command args" ); + + vector< string > objIds = m_vm["args"].as< vector< string > >( ); + + libcmis::ObjectPtr object = session->getObject( objIds.front() ); + if ( object.get() ) + { + libcmis::Document* doc = dynamic_cast< libcmis::Document* >( object.get() ); + if ( NULL != doc ) + { + vector< libcmis::DocumentPtr > versions = doc->getAllVersions( ); + + for ( vector< libcmis::DocumentPtr >::iterator it = versions.begin( ); + it != versions.end( ); ++it ) + { + cout << "------------------------------------------------" << endl; + cout << ( *it )->toString() << endl; + } + } + else + throw CommandException( string( "Not a document object id: " ) + objIds.front() ); + } + else + cout << "No such node: " << objIds.front() << endl; + } + else if ( "help" == command ) + { + printHelp(); + } + else + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: Unknown command: " << command << endl; + cerr << "------------------------------------------------" << endl; + printHelp( ); + } + + // TODO Add some more useful commands here + } +} + +options_description CmisClient::getOptionsDescription( ) +{ + options_description desc( "Allowed options" ); + desc.add_options( ) + ( "help", "Produce help message and exists" ) + ( "verbose,v", "Show loads of useful messages for debugging" ) + ( "url", value< string >(), "URL of the binding of the server" ) + ( "repository,r", value< string >(), "Name of the repository to use" ) + ( "username,u", value< string >(), "Username used to authenticate to the repository" ) + ( "password,p", value< string >(), "Password used to authenticate to the repository" ) + ( "no-ssl-check", "Disable the verification of SSL certificates. This may come handy" + "for self-signed certificates for example, though it lowers the security" ) + ( "proxy", value< string >(), "HTTP proxy url to override the system settings" ) + ( "noproxy", value< string >(), "Coma separated list if host and domain names not going" + "through the proxy" ) + ( "proxy-username", value< string >(), "Username to authenticate on the proxy" ) + ( "proxy-password", value< string >(), "Password to authenticate on the proxy" ) + ( "oauth2-client-id", value< string >(), "OAuth2 application client_id" ) + ( "oauth2-client-secret", value< string >(), "OAuth2 application client_secret" ) + ( "oauth2-auth-url", value< string >(), "URL to authenticate in the OAuth2 flow" ) + ( "oauth2-token-url", value< string >(), "URL to convert code to tokens in the OAuth2 flow" ) + ( "oauth2-redirect-uri", value< string >(), "redirect URI indicating that the authentication is finished in OAuth2 flow" ) + ( "oauth2-scope", value< string >(), "The authentication scope in OAuth2" ) + ( "oauth2-auth-code", value< string >(), "The authentication code required to get the access token" ) + ; + + options_description setcontentOpts( "modification operations options" ); + setcontentOpts.add_options( ) + ( "input-file", value< string >(), "File to push to the repository" ) + ( "input-type", value< string >(), "Mime type of the file to push to the repository" ) + ( "input-name", value< string >(), "Name of the file to push to the repository" + "(may be different from the local name)" ) + ( "object-type", value< string >(), "CMIS type of the object to create" ) + ( "object-property", value< vector< string > >(), "under the form prop-id=prop-value, defines a property" + "to be set on the object" ) + ( "message,m", value< string >(), "Check in message" ) + ( "major", "The version to create during the check in will be a major version." ) + ( "stream-id", value< string >(), "streamId of the rendition to get content." ) + ; + + desc.add( setcontentOpts ); + return desc; +} + +void CmisClient::printHelp( ) +{ + cerr << "cmis-client [options] [command] arguments" << endl; + + cerr << endl << "Commands" << endl; + cerr << " list-repos\n" + " Lists the repositories available on the server" << endl; + cerr << " repo-infos\n" + " Show the informations and capabilities of the selected repository" << endl; + cerr << " show-root\n" + " Dump the root node of the repository." << endl; + cerr << " type-by-id <Type Id 1> [... <Type Id N>]\n" + " Dumps the type informations for all the ids." << endl; + cerr << " show-by-id <Object Id 1> [... <Object Id N>]\n" + " Dumps the objects informations for all the ids." << endl; + cerr << " show-by-path <Object Path 1> [... <Object Path N>]\n" + " Dumps the objects informations for all the paths." << endl; + cerr << " get-content <Object Id>\n" + " Saves the stream of the content object in the\n" + " current folder. Any existing file is overwritten.\n" + " streamId can be used to get the desired rendition with --stream-id"<< endl; + cerr << " set-content <Object Id>\n" + " Replaces the stream of the content object by the\n" + " file selected with --input-file." << endl; + cerr << " create-folder <Parent Id> <Folder Name>\n" + " Creates a new folder inside the folder <Parent Id> named <Folder Name>." << endl; + cerr << " create-document <Parent Id> <Document Name>\n" + " Creates a new document inside the folder <Parent Id>\n" + " named <Document Name>.\n" + " Note that --input-file and --input-type may be requested if\n" + " the server requires a content stream." << endl; + cerr << " update-object <Object Id>\n" + " Update the object matching id <Object Id> with the properties\n" + " defined with --object-property." << endl; + cerr << " move-object <Object Id> <Source Folder Id> <Destination Folder Id>\n" + " Move the object matching id <Object Id> from the\n" + " folder <Source Folder Id> to the folder <Destination Folder Id>." << endl; + cerr << " delete <Object Id 1> [... <Object Id N>]\n" + " Delete the objects corresponding to the ids. If the node\n" + " is a folder, its content will be removed as well." << endl; + cerr << " checkout <Object Id>\n" + " Check out the document corresponding to the id and shows the\n" + " Private Working Copy document infos." << endl; + cerr << " cancel-checkout <Object Id>\n" + " Cancel the Private Working Copy corresponding to the id" << endl; + cerr << " checkin <Object Id>\n" + " Check in the Private Working copy corresponding to the id.\n" + " Use the --message and --major parameters to give more\n" + " details about the new version.\n" + " The modification options may be needed to set the new\n" + " version properties and content stream if the repository\n" + " doesn't allow to change the private working copies." << endl; + cerr << " get-versions <Object-Id>\n" + " Show all the versions of a document." << endl; + cerr << " help\n" + " Prints this help message and exits (like --help option)." << endl; + + cerr << endl << getOptionsDescription() << endl; +} + +int main ( int argc, char* argv[] ) +{ + options_description hidden( "Hidden options" ); + hidden.add_options( ) + ( "command", value< string >(), "Command" ) + ( "args", value< vector< string > >(), "Arguments for the command" ) + ; + + options_description allOptions = CmisClient::getOptionsDescription( ); + allOptions.add( hidden ); + + positional_options_description pd; + pd.add( "command", 1 ); + pd.add( "args", -1 ); + + variables_map vm; + store( command_line_parser( argc, argv ).options( allOptions ).positional( pd ).run( ), vm ); + notify( vm ); + + CmisClient client( vm ); + try + { + client.execute( ); + } + catch ( const CommandException& e ) + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: " << e.what() << endl; + cerr << "------------------------------------------------" << endl; + client.printHelp(); + return 1; + } + catch ( const libcmis::Exception& e ) + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: " << e.what() << endl; + cerr << "------------------------------------------------" << endl; + return 1; + } + catch ( const exception& e ) + { + cerr << "------------------------------------------------" << endl; + cerr << "ERROR: " << e.what() << endl; + cerr << "------------------------------------------------" << endl; + return 1; + } + + return 0; +} diff --git a/src/libcmis-c/Makefile.am b/src/libcmis-c/Makefile.am new file mode 100644 index 0000000..1510272 --- /dev/null +++ b/src/libcmis-c/Makefile.am @@ -0,0 +1,39 @@ + +libcmis_c_@LIBCMIS_API_VERSION@_la_CXXFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/inc/libcmis-c \ + $(XML2_CFLAGS) \ + $(BOOST_CPPFLAGS) + +libcmis_c_@LIBCMIS_API_VERSION@_la_CPPFLAGS = -DLIBCMIS_C_BUILD +if ENABLE_VISIBILITY +libcmis_c_@LIBCMIS_API_VERSION@_la_CXXFLAGS += -fvisibility=hidden +libcmis_c_@LIBCMIS_API_VERSION@_la_CPPFLAGS += -DLIBCMIS_C_VISIBILITY +endif + +lib_LTLIBRARIES = libcmis-c-@LIBCMIS_API_VERSION@.la +libcmis_c_@LIBCMIS_API_VERSION@_la_SOURCES = \ + allowable-actions.cxx \ + document.cxx \ + error.cxx \ + folder.cxx \ + internals.hxx \ + oauth2-data.cxx \ + object-type.cxx \ + object.cxx \ + property-type.cxx \ + property.cxx \ + rendition.cxx \ + repository.cxx \ + session-factory.cxx \ + session.cxx \ + vectors.cxx + +libcmis_c_@LIBCMIS_API_VERSION@_la_LDFLAGS = -export-dynamic -no-undefined -version-info 6:0:0 + +libcmis_c_@LIBCMIS_API_VERSION@_la_LIBADD = \ + ../libcmis/libcmis-@LIBCMIS_API_VERSION@.la \ + $(XML2_LIBS) \ + $(CURL_LIBS) \ + $(BOOST_SMART_PTR_LIBS) \ + $(BOOST_DATE_TIME_LIBS) diff --git a/src/libcmis-c/allowable-actions.cxx b/src/libcmis-c/allowable-actions.cxx new file mode 100644 index 0000000..f08f366 --- /dev/null +++ b/src/libcmis-c/allowable-actions.cxx @@ -0,0 +1,56 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/allowable-actions.h> + +#include "internals.hxx" + +void libcmis_allowable_actions_free( libcmis_AllowableActionsPtr allowable ) +{ + delete allowable; +} + + +bool libcmis_allowable_actions_isAllowed( libcmis_AllowableActionsPtr allowable, + libcmis_allowable_actions_Type action ) +{ + bool result = false; + if ( allowable != NULL && allowable->handle.get( ) != NULL ) + result = allowable->handle->isAllowed( libcmis::ObjectAction::Type( action ) ); + return result; +} + + +bool libcmis_allowable_actions_isDefined( libcmis_AllowableActionsPtr allowable, + libcmis_allowable_actions_Type action ) +{ + bool result = false; + if ( allowable != NULL && allowable->handle.get( ) != NULL ) + result = allowable->handle->isDefined( libcmis::ObjectAction::Type( action ) ); + return result; +} diff --git a/src/libcmis-c/document.cxx b/src/libcmis-c/document.cxx new file mode 100644 index 0000000..74d04d9 --- /dev/null +++ b/src/libcmis-c/document.cxx @@ -0,0 +1,448 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/document.h> + +#include "internals.hxx" + +using namespace std; +using libcmis::DocumentPtr; +using libcmis::FolderPtr; +using libcmis::PropertyPtrMap; +using boost::dynamic_pointer_cast; + +void libcmis_vector_document_free( libcmis_vector_document_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_document_size( libcmis_vector_document_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_DocumentPtr libcmis_vector_document_get( libcmis_vector_document_Ptr vector, size_t i ) +{ + libcmis_DocumentPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::DocumentPtr handle = vector->handle[i]; + item = new( nothrow ) libcmis_document( ); + item->handle = handle; + } + return item; +} + +bool libcmis_is_document( libcmis_ObjectPtr object ) +{ + bool isDocument = false; + if ( object != NULL && object->handle.get( ) != NULL ) + { + DocumentPtr document = dynamic_pointer_cast< libcmis::Document >( object->handle ); + isDocument = document.get( ) != NULL; + } + return isDocument; +} + + +libcmis_DocumentPtr libcmis_document_cast( libcmis_ObjectPtr object ) +{ + libcmis_DocumentPtr document = NULL; + + if ( object != NULL && object->handle.get( ) != NULL && + libcmis_is_document( object ) ) + { + document = new ( nothrow ) libcmis_document( ); + document->handle = object->handle; + } + + return document; +} + + +void libcmis_document_free( libcmis_DocumentPtr document ) +{ + delete document; +} + + +libcmis_vector_folder_Ptr libcmis_document_getParents( libcmis_DocumentPtr document, libcmis_ErrorPtr error ) +{ + libcmis_vector_folder_Ptr parents = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + vector< libcmis::FolderPtr > handles = doc->getParents( ); + parents = new libcmis_vector_folder( ); + parents->handle = handles; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return parents; +} + + +void libcmis_document_getContentStream( + libcmis_DocumentPtr document, + libcmis_writeFn writeFn, + void* userData, + libcmis_ErrorPtr error ) +{ + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + boost::shared_ptr< istream > stream = doc->getContentStream( ); + + stream->seekg( 0 ); + int bufSize = 2048; + char* buf = new char[ bufSize ]; + while ( !stream->eof( ) ) + { + stream->read( buf, bufSize ); + size_t read = stream->gcount( ); + writeFn( ( const void * )buf, size_t( 1 ), read, userData ); + } + delete[] buf; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + catch ( ... ) + { + } + } +} + + +void libcmis_document_setContentStream( + libcmis_DocumentPtr document, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* fileName, + bool overwrite, + libcmis_ErrorPtr error ) +{ + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + boost::shared_ptr< std::ostream > stream( new stringstream( ) ); + + size_t bufSize = 2048; + char* buf = new char[ bufSize ]; + size_t read = 0; + do + { + read = readFn( ( void * )buf, size_t( 1 ), bufSize, userData ); + stream->write( buf, read ); + } while ( read == bufSize ); + delete[] buf; + + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + doc->setContentStream( stream, contentType, fileName, overwrite ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + } +} + + +char* libcmis_document_getContentType( libcmis_DocumentPtr document ) +{ + char* value = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + value = strdup( doc->getContentType( ).c_str( ) ); + } + return value; +} + + +char* libcmis_document_getContentFilename( libcmis_DocumentPtr document ) +{ + char* value = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + value = strdup( doc->getContentFilename( ).c_str( ) ); + } + return value; +} + + +long libcmis_document_getContentLength( libcmis_DocumentPtr document ) +{ + long value = 0; + if ( document != NULL && document->handle.get( ) != NULL ) + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + value = doc->getContentLength( ); + } + return value; +} + + +libcmis_DocumentPtr libcmis_document_checkOut( libcmis_DocumentPtr document, libcmis_ErrorPtr error ) +{ + libcmis_DocumentPtr pwc = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + libcmis::DocumentPtr handle = doc->checkOut( ); + pwc= new libcmis_document( ); + pwc->handle = handle; + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + error->badAlloc = true; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } + return pwc; +} + + +void libcmis_document_cancelCheckout( libcmis_DocumentPtr document, libcmis_ErrorPtr error ) +{ + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + doc->cancelCheckout( ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + +libcmis_DocumentPtr libcmis_document_checkIn( + libcmis_DocumentPtr document, + bool isMajor, + const char* comment, + libcmis_vector_property_Ptr properties, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* filename, + libcmis_ErrorPtr error ) +{ + libcmis_DocumentPtr newVersion = NULL; + + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + // Create the ostream + boost::shared_ptr< std::ostream > stream( new stringstream( ) ); + + size_t bufSize = 2048; + char * buf = new char[ bufSize ]; + size_t read = 0; + do + { + read = readFn( ( void * )buf, size_t( 1 ), bufSize, userData ); + stream->write( buf, read ); + } while ( read == bufSize ); + delete[] buf; + + // Create the property map + PropertyPtrMap propertiesMap; + if ( properties != NULL ) + { + for ( vector< libcmis::PropertyPtr >::iterator it = properties->handle.begin( ); + it != properties->handle.end( ); ++it ) + { + string id = ( *it )->getPropertyType( )->getId( ); + propertiesMap.insert( pair< string, libcmis::PropertyPtr >( id, *it ) ); + } + } + + libcmis::DocumentPtr handle = doc->checkIn( isMajor, comment, propertiesMap, + stream, contentType, filename ); + newVersion = new libcmis_document( ); + newVersion->handle = handle; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + } + return newVersion; +} + +libcmis_vector_document_Ptr libcmis_document_getAllVersions( + libcmis_DocumentPtr document, + libcmis_ErrorPtr error ) +{ + libcmis_vector_document_Ptr result = NULL; + if ( document != NULL && document->handle.get( ) != NULL ) + { + try + { + DocumentPtr doc = dynamic_pointer_cast< libcmis::Document >( document->handle ); + if ( doc ) + { + std::vector< libcmis::DocumentPtr > handles = doc->getAllVersions( ); + result = new libcmis_vector_document( ); + result->handle = handles; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} diff --git a/src/libcmis-c/error.cxx b/src/libcmis-c/error.cxx new file mode 100644 index 0000000..8fdb681 --- /dev/null +++ b/src/libcmis-c/error.cxx @@ -0,0 +1,74 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/error.h> + +#include <stdlib.h> +#include <string.h> + +#include "internals.hxx" + +using namespace std; + +libcmis_ErrorPtr libcmis_error_create( ) +{ + libcmis_ErrorPtr error = new( nothrow ) libcmis_error( ); + return error; +} + + +void libcmis_error_free( libcmis_ErrorPtr error ) +{ + if ( error != NULL ) + { + free( error->message ); + free( error->type ); + delete error; + } +} + +const char* libcmis_error_getMessage( libcmis_ErrorPtr error ) +{ + if ( error != NULL ) + { + if ( error->badAlloc ) + return "Failed to allocate memory"; + else + return error->message; + } + else + return ""; +} + +const char* libcmis_error_getType( libcmis_ErrorPtr error ) +{ + if ( error != NULL ) + return error->type; + else + return NULL; +} diff --git a/src/libcmis-c/folder.cxx b/src/libcmis-c/folder.cxx new file mode 100644 index 0000000..8d7555a --- /dev/null +++ b/src/libcmis-c/folder.cxx @@ -0,0 +1,369 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/folder.h> + +#include "internals.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; +using libcmis::FolderPtr; +using boost::dynamic_pointer_cast; + +void libcmis_vector_folder_free( libcmis_vector_folder_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_folder_size( libcmis_vector_folder_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_FolderPtr libcmis_vector_folder_get( libcmis_vector_folder_Ptr vector, size_t i ) +{ + libcmis_FolderPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::FolderPtr handle = vector->handle[i]; + item = new ( nothrow ) libcmis_folder( ); + if ( item ) + item->handle = handle; + } + return item; +} + + +bool libcmis_is_folder( libcmis_ObjectPtr object ) +{ + bool isFolder = false; + if ( object != NULL && object->handle.get( ) != NULL ) + { + libcmis::FolderPtr folder = boost::dynamic_pointer_cast< libcmis::Folder >( object->handle ); + isFolder = folder.get( ) != NULL; + } + return isFolder; +} + + +libcmis_FolderPtr libcmis_folder_cast( libcmis_ObjectPtr object ) +{ + libcmis_FolderPtr folder = NULL; + + if ( object != NULL && object->handle.get( ) != NULL ) + { + libcmis::FolderPtr handle = boost::dynamic_pointer_cast< libcmis::Folder >( object->handle ); + if ( handle.get( ) != NULL ) + { + folder = new ( nothrow ) libcmis_folder( ); + if ( folder ) + folder->handle = handle; + } + } + + return folder; +} + + +void libcmis_folder_free( libcmis_FolderPtr folder ) +{ + delete folder; +} + + +libcmis_FolderPtr libcmis_folder_getParent( libcmis_FolderPtr folder, libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr parent = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + try + { + FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + libcmis::FolderPtr handle = folderHandle->getFolderParent( ); + if ( handle.get( ) != NULL ) + { + parent = new libcmis_folder( ); + parent->handle = handle; + } + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return parent; +} + + +libcmis_vector_object_Ptr libcmis_folder_getChildren( libcmis_FolderPtr folder, libcmis_ErrorPtr error ) +{ + libcmis_vector_object_Ptr result = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + try + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + std::vector< libcmis::ObjectPtr > handles = folderHandle->getChildren( ); + result = new libcmis_vector_object( ); + result->handle = handles; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} + + +char* libcmis_folder_getPath( libcmis_FolderPtr folder ) +{ + char* path = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + path = strdup( folderHandle->getPath( ).c_str( ) ); + } + return path; +} + + +bool libcmis_folder_isRootFolder( libcmis_FolderPtr folder ) +{ + bool isRoot = false; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + isRoot = folderHandle->isRootFolder( ); + } + return isRoot; +} + +libcmis_FolderPtr libcmis_folder_createFolder( + libcmis_FolderPtr folder, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr result = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + try + { + PropertyPtrMap mappedProperties; + if ( properties != NULL ) + { + size_t size = properties->handle.size( ); + for ( size_t i = 0; i < size; ++i ) + { + libcmis::PropertyPtr property = properties->handle[i]; + if ( property.get( ) != NULL ) + { + string id = property->getPropertyType( )->getId( ); + mappedProperties.insert( pair< string, libcmis::PropertyPtr >( id, property ) ); + } + } + } + + libcmis::FolderPtr handle = folderHandle->createFolder( mappedProperties ); + result = new libcmis_folder( ); + result->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + } + return result; +} + + +libcmis_DocumentPtr libcmis_folder_createDocument( + libcmis_FolderPtr folder, + libcmis_vector_property_Ptr properties, + libcmis_readFn readFn, + void* userData, + const char* contentType, + const char* filename, + libcmis_ErrorPtr error ) +{ + libcmis_DocumentPtr created = NULL; + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + try + { + // Create the ostream + boost::shared_ptr< std::ostream > stream( new stringstream( ) ); + + size_t bufSize = 2048; + char* buf = new char[ bufSize ]; + size_t read = 0; + do + { + read = readFn( ( void * )buf, size_t( 1 ), bufSize, userData ); + stream->write( buf, read ); + } while ( read == bufSize ); + delete[] buf; + + // Create the property map + PropertyPtrMap propertiesMap; + if ( properties != NULL ) + { + for ( vector< libcmis::PropertyPtr >::iterator it = properties->handle.begin( ); + it != properties->handle.end( ); ++it ) + { + string id = ( *it )->getPropertyType( )->getId( ); + propertiesMap.insert( pair< string, libcmis::PropertyPtr >( id, *it ) ); + } + } + + libcmis::DocumentPtr handle = folderHandle->createDocument( propertiesMap, stream, contentType, filename ); + created = new libcmis_document( ); + created->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + catch ( const exception& e ) + { + if ( error != NULL ) + error->message = strdup( e.what() ); + } + } + } + return created; +} + + +libcmis_vector_string_Ptr libcmis_folder_removeTree( libcmis_FolderPtr folder, + bool allVersion, + libcmis_folder_UnfileObjects unfile, + bool continueOnError, + libcmis_ErrorPtr error ) +{ + libcmis_vector_string_Ptr failed = NULL; + try + { + failed = new libcmis_vector_string( ); + if ( folder != NULL && folder->handle.get( ) != NULL ) + { + libcmis::FolderPtr folderHandle = dynamic_pointer_cast< libcmis::Folder >( folder->handle ); + if ( folder ) + { + vector< string > handle = folderHandle->removeTree( allVersion, + libcmis::UnfileObjects::Type( unfile ), continueOnError ); + failed->handle = handle; + } + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + return failed; +} diff --git a/src/libcmis-c/internals.hxx b/src/libcmis-c/internals.hxx new file mode 100644 index 0000000..e4a5b6b --- /dev/null +++ b/src/libcmis-c/internals.hxx @@ -0,0 +1,242 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _LIBCMIS_INTERNALS_H_ +#define _LIBCMIS_INTERNALS_H_ + +#include <vector> + +#include <libcmis/allowable-actions.hxx> +#include <libcmis/document.hxx> +#include <libcmis/exception.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> +#include <libcmis/property.hxx> +#include <libcmis/repository.hxx> +#include <libcmis/session.hxx> +#include <libcmis/session-factory.hxx> + +std::string createString( char* str ); + +struct libcmis_error +{ + char* message; + char* type; + bool badAlloc; + + libcmis_error( ) : message( NULL ), type( NULL ), badAlloc( false ) { } +}; + +struct libcmis_session +{ + libcmis::Session* handle; + libcmis::AuthProviderPtr provider; + + // Constructors + + libcmis_session( ) : + handle( NULL ), + provider( ) + { + } + + libcmis_session( const libcmis_session& copy ) : + handle( copy.handle ), + provider( copy.provider ) + { + } + + libcmis_session& operator=( const libcmis_session& copy ) + { + if ( this != © ) + { + handle = copy.handle; + provider = copy.provider; + } + return *this; + } +}; + +struct libcmis_repository +{ + libcmis::RepositoryPtr handle; + + libcmis_repository( ) : handle( ) { } +}; + +struct libcmis_object +{ + libcmis::ObjectPtr handle; + + libcmis_object( ) : handle( ) { } + virtual ~libcmis_object( ) { } +}; + +struct libcmis_object_type +{ + libcmis::ObjectTypePtr handle; + + libcmis_object_type( ) : handle( ) { } +}; + +struct libcmis_allowable_actions +{ + libcmis::AllowableActionsPtr handle; + + libcmis_allowable_actions( ) : handle ( ) { } +}; + +struct libcmis_property_type +{ + libcmis::PropertyTypePtr handle; + + libcmis_property_type( ) : handle( ) { } +}; + +struct libcmis_property +{ + libcmis::PropertyPtr handle; + + libcmis_property( ) : handle( ) { } +}; + +struct libcmis_folder : public libcmis_object +{ + libcmis_folder( ) : libcmis_object( ) { } +}; + +struct libcmis_document : public libcmis_object +{ + libcmis_document( ) : libcmis_object( ) { } +}; + +struct libcmis_oauth2data +{ + libcmis::OAuth2DataPtr handle; + + libcmis_oauth2data( ) : handle( ) { } +}; + +struct libcmis_rendition +{ + libcmis::RenditionPtr handle; + + libcmis_rendition( ) : handle( ) { } +}; + +struct libcmis_vector_bool +{ + std::vector< bool > handle; + + libcmis_vector_bool( ) : handle( ) { } +}; + +struct libcmis_vector_string +{ + std::vector< std::string > handle; + + libcmis_vector_string( ) : handle( ) { } +}; + +struct libcmis_vector_long +{ + std::vector< long > handle; + + libcmis_vector_long( ) : handle( ) { } +}; + +struct libcmis_vector_double +{ + std::vector< double > handle; + + libcmis_vector_double( ) : handle( ) { } +}; + +struct libcmis_vector_time +{ + std::vector< boost::posix_time::ptime > handle; + + libcmis_vector_time( ) : handle( ) { } +}; + +struct libcmis_vector_object_type +{ + std::vector< libcmis::ObjectTypePtr > handle; + + libcmis_vector_object_type( ) : handle( ) { } +}; + +struct libcmis_vector_property_type +{ + std::vector< libcmis::PropertyTypePtr > handle; + + libcmis_vector_property_type( ) : handle( ) { } +}; + +struct libcmis_vector_property +{ + std::vector< libcmis::PropertyPtr > handle; + + libcmis_vector_property( ) : handle( ) { } +}; + +struct libcmis_vector_object +{ + std::vector< libcmis::ObjectPtr > handle; + + libcmis_vector_object( ) : handle( ) { } +}; + +struct libcmis_vector_folder +{ + std::vector< libcmis::FolderPtr > handle; + + libcmis_vector_folder( ) : handle( ) { } +}; + +struct libcmis_vector_document +{ + std::vector< libcmis::DocumentPtr > handle; + + libcmis_vector_document( ) : handle( ) { } +}; + +struct libcmis_vector_repository +{ + std::vector< libcmis::RepositoryPtr > handle; + + libcmis_vector_repository( ) : handle( ) { } +}; + +struct libcmis_vector_rendition +{ + std::vector< libcmis::RenditionPtr > handle; + + libcmis_vector_rendition( ) : handle( ) { } +}; + +#endif diff --git a/src/libcmis-c/oauth2-data.cxx b/src/libcmis-c/oauth2-data.cxx new file mode 100644 index 0000000..9b7c69f --- /dev/null +++ b/src/libcmis-c/oauth2-data.cxx @@ -0,0 +1,110 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/oauth2-data.h> + +#include "internals.hxx" + +using namespace std; + + +libcmis_OAuth2DataPtr libcmis_oauth2data_create( + char* authUrl, char* tokenUrl, char* scope, char* redirectUri, + char* clientId, char* clientSecret ) +{ + libcmis_OAuth2DataPtr data = new( nothrow ) libcmis_oauth2data( ); + + if ( NULL != data ) + data->handle.reset( new libcmis::OAuth2Data( + authUrl, tokenUrl, scope, redirectUri, + clientId, clientSecret ) ); + return data; +} + + +void libcmis_oauth2data_free( libcmis_OAuth2DataPtr oauth2 ) +{ + delete oauth2; +} + + +bool libcmis_oauth2data_isComplete( libcmis_OAuth2DataPtr oauth2 ) +{ + bool result = false; + if ( oauth2 != NULL && oauth2->handle != NULL ) + result = oauth2->handle->isComplete(); + return result; +} + + +const char* libcmis_oauth2data_getAuthUrl( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getAuthUrl().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getTokenUrl( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getTokenUrl().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getClientId( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getClientId().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getClientSecret( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getClientSecret().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getScope( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getScope().c_str(); + return NULL; +} + + +const char* libcmis_oauth2data_getRedirectUri( libcmis_OAuth2DataPtr oauth2 ) +{ + if ( oauth2 != NULL && oauth2->handle != NULL ) + return oauth2->handle->getRedirectUri().c_str(); + return NULL; +} diff --git a/src/libcmis-c/object-type.cxx b/src/libcmis-c/object-type.cxx new file mode 100644 index 0000000..f1b3b9f --- /dev/null +++ b/src/libcmis-c/object-type.cxx @@ -0,0 +1,388 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/object-type.h> + +#include "internals.hxx" + +using namespace std; + + +void libcmis_vector_object_type_free( libcmis_vector_object_type_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_object_type_size( libcmis_vector_object_type_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_ObjectTypePtr libcmis_vector_object_type_get( libcmis_vector_object_type_Ptr vector, size_t i ) +{ + libcmis_ObjectTypePtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::ObjectTypePtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_object_type( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_object_type_free( libcmis_ObjectTypePtr type ) +{ + delete type; +} + + +char* libcmis_object_type_getId( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getLocalName( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getLocalNamespace( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalNamespace( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getQueryName( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getQueryName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getDisplayName( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getDisplayName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_type_getDescription( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getDescription( ).c_str( ) ); + else + return NULL; +} + + +libcmis_ObjectTypePtr libcmis_object_type_getParentType( + libcmis_ObjectTypePtr type, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectTypePtr result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + try + { + libcmis::ObjectTypePtr handle = type->handle->getParentType( ); + if ( handle.get ( ) ) + { + result = new libcmis_object_type( ); + result->handle = handle; + } + } + catch( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + + return result; +} + + +libcmis_ObjectTypePtr libcmis_object_type_getBaseType( + libcmis_ObjectTypePtr type, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectTypePtr result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + try + { + libcmis::ObjectTypePtr handle = type->handle->getBaseType( ); + result = new libcmis_object_type( ); + result->handle = handle; + } + catch( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + + return result; +} + + +char* libcmis_object_type_getParentTypeId( libcmis_ObjectTypePtr type ) +{ + char* result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + result = strdup( type->handle->getParentTypeId( ).c_str() ); + } + + return result; +} + + +char* libcmis_object_type_getBaseTypeId( libcmis_ObjectTypePtr type ) +{ + char* result = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + result = strdup( type->handle->getBaseTypeId( ).c_str() ); + } + + return result; +} + + +libcmis_vector_object_type_Ptr libcmis_object_type_getChildren( + libcmis_ObjectTypePtr type, libcmis_ErrorPtr error ) +{ + libcmis_vector_object_type_Ptr children = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + try + { + std::vector< libcmis::ObjectTypePtr > types = type->handle->getChildren( ); + children = new libcmis_vector_object_type( ); + children->handle = types; + } + catch( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + + return children; +} + + +bool libcmis_object_type_isCreatable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isCreatable( ); + return value; +} + + +bool libcmis_object_type_isFileable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isFileable( ); + return value; +} + + +bool libcmis_object_type_isQueryable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isQueryable( ); + return value; +} + + +bool libcmis_object_type_isFulltextIndexed( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isFulltextIndexed( ); + return value; +} + + +bool libcmis_object_type_isIncludedInSupertypeQuery( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isIncludedInSupertypeQuery( ); + return value; +} + + +bool libcmis_object_type_isControllablePolicy( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isControllablePolicy( ); + return value; +} + + +bool libcmis_object_type_isControllableACL( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isControllableACL( ); + return value; +} + + +bool libcmis_object_type_isVersionable( libcmis_ObjectTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isVersionable( ); + return value; +} + + +libcmis_object_type_ContentStreamAllowed libcmis_object_type_getContentStreamAllowed( libcmis_ObjectTypePtr type ) +{ + libcmis_object_type_ContentStreamAllowed result = libcmis_NotAllowed; + if ( type != NULL && type->handle.get( ) != NULL ) + { + libcmis::ObjectType::ContentStreamAllowed value = type->handle->getContentStreamAllowed( ); + result = libcmis_object_type_ContentStreamAllowed( value ); + } + return result; +} + + +libcmis_vector_property_type_Ptr libcmis_object_type_getPropertiesTypes( libcmis_ObjectTypePtr type ) +{ + libcmis_vector_property_type_Ptr propertyTypes = NULL; + if ( type != NULL && type->handle != NULL ) + { + map< string, libcmis::PropertyTypePtr >& handles = type->handle->getPropertiesTypes( ); + propertyTypes = new ( nothrow ) libcmis_vector_property_type( ); + if ( propertyTypes ) + { + int i = 0; + for ( map< string, libcmis::PropertyTypePtr >::iterator it = handles.begin( ); + it != handles.end( ); ++it, ++i ) + { + propertyTypes->handle.push_back( it->second ); + } + } + } + + return propertyTypes; +} + +libcmis_PropertyTypePtr libcmis_object_type_getPropertyType( libcmis_ObjectTypePtr type, const char* id ) +{ + libcmis_PropertyTypePtr propertyType = NULL; + if ( type != NULL && type->handle != NULL ) + { + map< string, libcmis::PropertyTypePtr >& handles = type->handle->getPropertiesTypes( ); + map< string, libcmis::PropertyTypePtr >::iterator it = handles.find( string( id ) ); + if ( it != handles.end( ) ) + { + propertyType = new ( nothrow ) libcmis_property_type( ); + if ( propertyType ) + propertyType->handle = it->second; + } + } + + return propertyType; +} + + +char* libcmis_object_type_toString( libcmis_ObjectTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->toString( ).c_str( ) ); + else + return NULL; +} + + diff --git a/src/libcmis-c/object.cxx b/src/libcmis-c/object.cxx new file mode 100644 index 0000000..323cb31 --- /dev/null +++ b/src/libcmis-c/object.cxx @@ -0,0 +1,512 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/object.h> + +#include <libcmis-c/folder.h> + +#include "internals.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; +using boost::dynamic_pointer_cast; + +namespace +{ + string lcl_stdString( const char* str ) + { + string result; + if ( str ) + result = string( str ); + return result; + } + + PropertyPtrMap lcl_createPropertiesMap( libcmis_vector_property_Ptr properties ) + { + PropertyPtrMap propertiesMap; + if ( properties ) + { + for ( vector< libcmis::PropertyPtr >::iterator it = properties->handle.begin( ); + it != properties->handle.end( ); ++it ) + { + libcmis::PropertyPtr propHandle = *it; + propertiesMap[ propHandle->getPropertyType()->getId( ) ] = propHandle; + } + } + return propertiesMap; + } +} + +void libcmis_vector_object_free( libcmis_vector_object_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_object_size( libcmis_vector_object_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_ObjectPtr libcmis_vector_object_get( libcmis_vector_object_Ptr vector, size_t i ) +{ + libcmis_ObjectPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::ObjectPtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_object( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_object_free( libcmis_ObjectPtr object ) +{ + delete object; +} + + +char* libcmis_object_getId( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_getName( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getName( ).c_str( ) ); + else + return NULL; +} + +libcmis_vector_string_Ptr libcmis_object_getPaths( libcmis_ObjectPtr object ) +{ + libcmis_vector_string_Ptr c_paths = NULL; + if ( object != NULL && object->handle != NULL ) + { + std::vector< std::string > paths = object->handle->getPaths( ); + c_paths = new ( nothrow ) libcmis_vector_string( ); + if ( c_paths ) + c_paths->handle = paths; + } + return c_paths; +} + +char* libcmis_object_getBaseType( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getBaseType( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_getType( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getType( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_object_getCreatedBy( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getCreatedBy( ).c_str( ) ); + else + return NULL; +} + + +time_t libcmis_object_getCreationDate( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + { + tm time = boost::posix_time::to_tm( object->handle->getCreationDate( ) ); + return mktime( &time ); + } + else + return 0; +} + + +char* libcmis_object_getLastModifiedBy( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getLastModifiedBy( ).c_str( ) ); + else + return NULL; +} + + +time_t libcmis_object_getLastModificationDate( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + { + tm time = boost::posix_time::to_tm( object->handle->getLastModificationDate( ) ); + return mktime( &time ); + } + else + return 0; +} + + +char* libcmis_object_getChangeToken( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getChangeToken( ).c_str( ) ); + else + return NULL; +} + +char* libcmis_object_getThumbnailUrl( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->getThumbnailUrl( ).c_str( ) ); + else + return NULL; +} + +libcmis_vector_rendition_Ptr libcmis_object_getRenditions( libcmis_ObjectPtr object, + libcmis_ErrorPtr error ) +{ + libcmis_vector_rendition_Ptr result = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + try + { + std::vector< libcmis::RenditionPtr > handles = object->handle->getRenditions( ); + result = new libcmis_vector_rendition( ); + result->handle = handles; + } + + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} + +bool libcmis_object_isImmutable( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return object->handle->isImmutable( ); + else + return true; +} + +libcmis_vector_string_Ptr libcmis_object_getSecondaryTypes( libcmis_ObjectPtr object ) +{ + libcmis_vector_string_Ptr c_types = NULL; + if ( object != NULL && object->handle != NULL ) + { + vector< string > types = object->handle->getSecondaryTypes( ); + c_types = new ( nothrow ) libcmis_vector_string( ); + if ( c_types ) + c_types->handle = types; + } + return c_types; +} + +libcmis_ObjectPtr +libcmis_object_addSecondaryType( libcmis_ObjectPtr object, + const char* id, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr updated = NULL; + if ( object != NULL && object->handle != NULL && properties != NULL ) + { + try + { + PropertyPtrMap propertiesMap = lcl_createPropertiesMap( properties ); + libcmis::ObjectPtr result = object->handle->addSecondaryType( + lcl_stdString( id ), + propertiesMap ); + updated = new libcmis_object( ); + updated->handle = result; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return updated; +} + +libcmis_ObjectPtr +libcmis_object_removeSecondaryType( libcmis_ObjectPtr object, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr updated = NULL; + if ( object != NULL && object->handle != NULL ) + { + try + { + libcmis::ObjectPtr result = object->handle->removeSecondaryType( + lcl_stdString( id ) ); + updated = new libcmis_object( ); + updated->handle = result; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return updated; +} + +libcmis_vector_property_Ptr libcmis_object_getProperties( libcmis_ObjectPtr object ) +{ + libcmis_vector_property_Ptr properties = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + PropertyPtrMap& handles = object->handle->getProperties( ); + properties = new ( nothrow ) libcmis_vector_property( ); + if ( properties ) + { + int i = 0; + for ( PropertyPtrMap::iterator it = handles.begin( ); + it != handles.end( ); ++it, ++i ) + { + properties->handle.push_back( it->second ); + } + } + } + return properties; +} + + +libcmis_PropertyPtr libcmis_object_getProperty( libcmis_ObjectPtr object, const char* name ) +{ + libcmis_PropertyPtr property = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + PropertyPtrMap& handles = object->handle->getProperties( ); + PropertyPtrMap::iterator it = handles.find( lcl_stdString( name ) ); + if ( it != handles.end( ) ) + { + property = new ( nothrow ) libcmis_property( ); + if ( property ) + property->handle = it->second; + } + } + return property; +} + + +libcmis_ObjectPtr libcmis_object_updateProperties( + libcmis_ObjectPtr object, + libcmis_vector_property_Ptr properties, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr result = NULL; + if ( object != NULL && object->handle != NULL && properties != NULL ) + { + try + { + // Build the map of changed properties + PropertyPtrMap propertiesMap = lcl_createPropertiesMap( properties ); + libcmis::ObjectPtr handle = object->handle->updateProperties( propertiesMap ); + result = new libcmis_object( ); + result->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return result; +} + + +libcmis_ObjectTypePtr libcmis_object_getTypeDescription( libcmis_ObjectPtr object ) +{ + libcmis_ObjectTypePtr result = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + result = new ( nothrow ) libcmis_object_type( ); + if ( result ) + result->handle = object->handle->getTypeDescription( ); + } + return result; +} + + +libcmis_AllowableActionsPtr libcmis_object_getAllowableActions( libcmis_ObjectPtr object ) +{ + libcmis_AllowableActionsPtr result = NULL; + if ( object != NULL && object->handle.get( ) != NULL ) + { + result = new ( nothrow ) libcmis_allowable_actions( ); + if ( result ) + result->handle = object->handle->getAllowableActions( ); + } + return result; +} + + +void libcmis_object_refresh( libcmis_ObjectPtr object, libcmis_ErrorPtr error ) +{ + if ( object != NULL && object->handle != NULL ) + { + try + { + object->handle->refresh( ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + + +time_t libcmis_object_getRefreshTimestamp( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return object->handle->getRefreshTimestamp( ); + else + return 0; +} + + +void libcmis_object_remove( libcmis_ObjectPtr object, bool allVersions, libcmis_ErrorPtr error ) +{ + if ( object != NULL && object->handle != NULL ) + { + try + { + object->handle->remove( allVersions ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + + +void libcmis_object_move( libcmis_ObjectPtr object, + libcmis_FolderPtr source, + libcmis_FolderPtr dest, + libcmis_ErrorPtr error ) +{ + if ( object != NULL && object->handle != NULL ) + { + try + { + libcmis::FolderPtr sourceHandle; + if ( source != NULL ) + sourceHandle = dynamic_pointer_cast< libcmis::Folder >( source->handle ); + libcmis::FolderPtr destHandle; + if ( dest != NULL ) + destHandle = dynamic_pointer_cast< libcmis::Folder >( dest->handle ); + + object->handle->move( sourceHandle, destHandle ); + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } +} + + +char* libcmis_object_toString( libcmis_ObjectPtr object ) +{ + if ( object != NULL && object->handle != NULL ) + return strdup( object->handle->toString( ).c_str( ) ); + else + return NULL; +} diff --git a/src/libcmis-c/property-type.cxx b/src/libcmis-c/property-type.cxx new file mode 100644 index 0000000..3f23939 --- /dev/null +++ b/src/libcmis-c/property-type.cxx @@ -0,0 +1,201 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/property-type.h> + +#include "internals.hxx" + +void libcmis_vector_property_type_free( libcmis_vector_property_type* vector ) +{ + delete vector; +} + + +size_t libcmis_vector_property_type_size( libcmis_vector_property_type* vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_PropertyTypePtr libcmis_vector_property_type_get( libcmis_vector_property_type* vector, size_t i ) +{ + libcmis_PropertyTypePtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::PropertyTypePtr type = vector->handle[i]; + item = new ( std::nothrow ) libcmis_property_type( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_property_type_free( libcmis_PropertyTypePtr type ) +{ + delete type; +} + + +char* libcmis_property_type_getId( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getLocalName( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getLocalNamespace( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getLocalNamespace( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getDisplayName( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getDisplayName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_property_type_getQueryName( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getQueryName( ).c_str( ) ); + else + return NULL; +} + + +libcmis_property_type_Type libcmis_property_type_getType( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return libcmis_property_type_Type( type->handle->getType( ) ); + else + return libcmis_String; +} + + +char* libcmis_property_type_getXmlType( libcmis_PropertyTypePtr type ) +{ + if ( type != NULL && type->handle.get( ) != NULL ) + return strdup( type->handle->getXmlType( ).c_str( ) ); + else + return NULL; +} + + +bool libcmis_property_type_isMultiValued( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isMultiValued( ); + return value; +} + + +bool libcmis_property_type_isUpdatable( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isUpdatable( ); + return value; +} + + +bool libcmis_property_type_isInherited( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isInherited( ); + return value; +} + + +bool libcmis_property_type_isRequired( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isRequired( ); + return value; +} + + +bool libcmis_property_type_isQueryable( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isQueryable( ); + return value; +} + + +bool libcmis_property_type_isOrderable( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isOrderable( ); + return value; +} + + +bool libcmis_property_type_isOpenChoice( libcmis_PropertyTypePtr type ) +{ + bool value = false; + if ( type != NULL && type->handle.get( ) != NULL ) + value = type->handle->isOpenChoice( ); + return value; +} + +void libcmis_property_type_update( libcmis_PropertyTypePtr propDef, + libcmis_vector_object_type_Ptr types ) +{ + if ( propDef != NULL && propDef->handle.get( ) != NULL && types != NULL ) + { + std::vector< libcmis::ObjectTypePtr > typesHandle = types->handle; + propDef->handle->update( typesHandle ); + } +} diff --git a/src/libcmis-c/property.cxx b/src/libcmis-c/property.cxx new file mode 100644 index 0000000..2886d55 --- /dev/null +++ b/src/libcmis-c/property.cxx @@ -0,0 +1,200 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/property.h> + +#include "internals.hxx" + +using namespace std; + + +libcmis_vector_property_Ptr libcmis_vector_property_create( ) +{ + return new ( nothrow ) libcmis_vector_property( ); +} + + +void libcmis_vector_property_free( libcmis_vector_property_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_property_size( libcmis_vector_property_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_PropertyPtr libcmis_vector_property_get( libcmis_vector_property_Ptr vector, size_t i ) +{ + libcmis_PropertyPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::PropertyPtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_property( ); + if ( item ) + item->handle = type; + } + return item; +} + + +void libcmis_vector_property_append( libcmis_vector_property_Ptr vector, libcmis_PropertyPtr item ) +{ + if ( vector != NULL && + item != NULL && item->handle.get( ) != NULL ) + { + vector->handle.push_back( item->handle ); + } +} + + +libcmis_PropertyPtr libcmis_property_create( libcmis_PropertyTypePtr type, const char** strValues, size_t size ) +{ + libcmis_PropertyPtr property = NULL; + if ( type != NULL && type->handle.get( ) != NULL ) + { + property = new ( nothrow ) libcmis_property( ); + if ( property ) + { + vector< string > values; + for ( size_t i = 0; i < size; ++i ) + values.push_back( string( strValues[i] ) ); + libcmis::PropertyPtr prop( new ( nothrow ) libcmis::Property( type->handle, values ) ); + property->handle = prop; + } + } + + return property; +} + + +void libcmis_property_free( libcmis_PropertyPtr property ) +{ + delete property; +} + + +libcmis_PropertyTypePtr libcmis_property_getPropertyType( libcmis_PropertyPtr property ) +{ + libcmis_PropertyTypePtr type = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + libcmis::PropertyTypePtr handle = property->handle->getPropertyType( ); + type = new ( nothrow ) libcmis_property_type( ); + if ( type ) + type->handle = handle; + } + return type; +} + + +libcmis_vector_time_Ptr libcmis_property_getDateTimes( libcmis_PropertyPtr property ) +{ + libcmis_vector_time_Ptr times = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< boost::posix_time::ptime > handles = property->handle->getDateTimes( ); + times = new ( nothrow ) libcmis_vector_time( ); + if ( times ) + times->handle = handles; + } + return times; +} + + +libcmis_vector_bool_Ptr libcmis_property_getBools( libcmis_PropertyPtr property ) +{ + libcmis_vector_bool_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< bool > handles = property->handle->getBools( ); + values = new ( nothrow ) libcmis_vector_bool( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +libcmis_vector_string_Ptr libcmis_property_getStrings( libcmis_PropertyPtr property ) +{ + libcmis_vector_string_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< string > handles = property->handle->getStrings( ); + values = new ( nothrow ) libcmis_vector_string( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +libcmis_vector_long_Ptr libcmis_property_getLongs( libcmis_PropertyPtr property ) +{ + libcmis_vector_long_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< long > handles = property->handle->getLongs( ); + values = new ( nothrow ) libcmis_vector_long( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +libcmis_vector_double_Ptr libcmis_property_getDoubles( libcmis_PropertyPtr property ) +{ + libcmis_vector_double_Ptr values = NULL; + if ( property != NULL && property->handle.get( ) != NULL ) + { + vector< double > handles = property->handle->getDoubles( ); + values = new ( nothrow ) libcmis_vector_double( ); + if ( values ) + values->handle = handles; + } + return values; +} + + +void libcmis_property_setValues( libcmis_PropertyPtr property, const char** strValues, size_t size ) +{ + if ( property != NULL && property->handle.get() != NULL ) + { + vector< string > values; + for ( size_t i = 0; i < size; ++i ) + values.push_back( string( strValues[i] ) ); + property->handle->setValues( values ); + } +} diff --git a/src/libcmis-c/rendition.cxx b/src/libcmis-c/rendition.cxx new file mode 100644 index 0000000..8adfd42 --- /dev/null +++ b/src/libcmis-c/rendition.cxx @@ -0,0 +1,110 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/rendition.h> + +#include "internals.hxx" + +using namespace std; + +void libcmis_rendition_free( libcmis_RenditionPtr rendition ) +{ + delete rendition; +} + +bool libcmis_rendition_isThumbnail( libcmis_RenditionPtr rendition ) +{ + bool result = false; + if ( rendition != NULL && rendition->handle != NULL ) + result = rendition->handle->isThumbnail(); + return result; +} + +const char* libcmis_rendition_getStreamId( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getStreamId().c_str(); + return NULL; +} + +const char* libcmis_rendition_getMimeType( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getMimeType().c_str(); + return NULL; +} + +const char* libcmis_rendition_getKind( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getKind().c_str(); + return NULL; +} + +const char* libcmis_rendition_getUrl( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getUrl().c_str(); + return NULL; +} + +const char* libcmis_rendition_getTitle( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getTitle().c_str(); + return NULL; +} + +long libcmis_rendition_getLength( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getLength(); + return -1; +} + +long libcmis_rendition_getWidth( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getWidth(); + return -1; +} + +long libcmis_rendition_getHeight( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getHeight(); + return -1; +} + +const char* libcmis_rendition_getRenditionDocumentId( libcmis_RenditionPtr rendition ) +{ + if ( rendition != NULL && rendition->handle != NULL ) + return rendition->handle->getRenditionDocumentId().c_str(); + return NULL; +} + diff --git a/src/libcmis-c/repository.cxx b/src/libcmis-c/repository.cxx new file mode 100644 index 0000000..41169a1 --- /dev/null +++ b/src/libcmis-c/repository.cxx @@ -0,0 +1,208 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/repository.h> + +#include "internals.hxx" + +using std::nothrow; + +void libcmis_vector_repository_free( libcmis_vector_Repository_Ptr vector ) +{ + delete vector; +} + + +size_t libcmis_vector_repository_size( libcmis_vector_Repository_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + + +libcmis_RepositoryPtr libcmis_vector_repository_get( libcmis_vector_Repository_Ptr vector, size_t i ) +{ + libcmis_RepositoryPtr item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + { + libcmis::RepositoryPtr type = vector->handle[i]; + item = new ( nothrow ) libcmis_repository( ); + if ( item ) + item->handle = type; + } + return item; +} + + +libcmis_RepositoryPtr libcmis_repository_create( xmlNodePtr node ) +{ + libcmis_RepositoryPtr repository = new ( nothrow ) libcmis_repository( ); + + if ( repository ) + { + libcmis::RepositoryPtr handle( new ( nothrow ) libcmis::Repository( node ) ); + repository->handle = handle; + } + + return repository; +} + + +void libcmis_repository_free( libcmis_RepositoryPtr repository ) +{ + delete repository; +} + + +char* libcmis_repository_getId( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getName( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getDescription( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getDescription( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getVendorName( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getVendorName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getProductName( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getProductName( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getProductVersion( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getProductVersion( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getRootId( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getRootId( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getCmisVersionSupported( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL ) + return strdup( repository->handle->getCmisVersionSupported( ).c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getThinClientUri( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL && + repository->handle->getThinClientUri( ).get( ) != NULL ) + return strdup( repository->handle->getThinClientUri( )->c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getPrincipalAnonymous( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL && + repository->handle->getPrincipalAnonymous( ).get( ) != NULL ) + return strdup( repository->handle->getPrincipalAnonymous( )->c_str( ) ); + else + return NULL; +} + + +char* libcmis_repository_getPrincipalAnyone( libcmis_RepositoryPtr repository ) +{ + if ( repository != NULL && repository->handle != NULL && + repository->handle->getPrincipalAnyone( ).get( ) != NULL ) + return strdup( repository->handle->getPrincipalAnyone( )->c_str( ) ); + else + return NULL; +} + +char* libcmis_repository_getCapability( + libcmis_RepositoryPtr repository, + libcmis_repository_capability_Type capability ) +{ + if ( repository != NULL && repository->handle != NULL ) + { + std::string value = repository->handle->getCapability( ( libcmis::Repository::Capability ) capability ); + return strdup( value.c_str( ) ); + } + else + return NULL; +} + +bool libcmis_repository_getCapabilityAsBool( + libcmis_RepositoryPtr repository, + libcmis_repository_capability_Type capability ) +{ + if ( repository != NULL && repository->handle != NULL ) + { + return repository->handle->getCapabilityAsBool( ( libcmis::Repository::Capability ) capability ); + } + else + return false; +} diff --git a/src/libcmis-c/session-factory.cxx b/src/libcmis-c/session-factory.cxx new file mode 100644 index 0000000..cee4532 --- /dev/null +++ b/src/libcmis-c/session-factory.cxx @@ -0,0 +1,239 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/session-factory.h> + +#include <map> +#include <string> +#include <stdlib.h> + +#include <libcmis/session-factory.hxx> + +#include <libcmis-c/session.h> +#include <libcmis-c/vectors.h> + +#include "internals.hxx" + +using namespace std; + +namespace +{ + size_t const CRED_MAX_LEN = 1024; + + class WrapperAuthProvider : public libcmis::AuthProvider + { + private: + libcmis_authenticationCallback m_callback; + + public: + WrapperAuthProvider( libcmis_authenticationCallback callback ) : + m_callback( callback ) + { + } + virtual ~WrapperAuthProvider( ) { }; + + virtual bool authenticationQuery( string& username, string& password ); + }; + + bool WrapperAuthProvider::authenticationQuery( string& username, string& password ) + { + /* NOTE: As I understand this, the callback is responsible for + * filling the correct username and password (possibly using + * the passed values as defaults in some dialog or so). But then + * there is no guarantee that the new username/password will + * not be longer than the present one, in which case it will + * not fit into the available space! For now, use a buffer size + * big enough for practical purposes. + * + * It might be a better idea to change the callback's signature + * to bool ( * )( char** username, char** password ) + * and make it the callee's responsibility to reallocate the + * strings if it needs to. + */ + char user[CRED_MAX_LEN]; + strncpy(user, username.c_str( ), sizeof( user ) ); + user[CRED_MAX_LEN - 1] = '\0'; + char pass[CRED_MAX_LEN]; + strncpy(pass, password.c_str( ), sizeof( pass ) ); + pass[CRED_MAX_LEN - 1] = '\0'; + + bool result = m_callback( user, pass ); + + // Update the username and password with the input + username = user; + password = pass; + + return result; + } + + + class WrapperCertHandler : public libcmis::CertValidationHandler + { + private: + libcmis_certValidationCallback m_callback; + public: + WrapperCertHandler( libcmis_certValidationCallback callback ) : + m_callback( callback ) + { + } + virtual ~WrapperCertHandler( ) { }; + + virtual bool validateCertificate( vector< string > certificatesChain ); + }; + + bool WrapperCertHandler::validateCertificate( vector< string > certificatesChain ) + { + libcmis_vector_string_Ptr chain = new ( nothrow ) libcmis_vector_string( ); + if ( chain ) + chain->handle = certificatesChain; + + bool result = m_callback( chain ); + + libcmis_vector_string_free( chain ); + return result; + } +} + +std::string createString( char* str ) +{ + if ( str ) + return string( str ); + else + return string( ); +} + +void libcmis_setAuthenticationCallback( libcmis_authenticationCallback callback ) +{ + libcmis::AuthProviderPtr provider( new ( nothrow ) WrapperAuthProvider( callback ) ); + if ( provider ) + libcmis::SessionFactory::setAuthenticationProvider( provider ); +} + +void libcmis_setCertValidationCallback( libcmis_certValidationCallback callback ) +{ + libcmis::CertValidationHandlerPtr handler( new ( nothrow )WrapperCertHandler( callback ) ); + if ( handler ) + libcmis::SessionFactory::setCertificateValidationHandler( handler ); +} + +void libcmis_setOAuth2AuthCodeProvider( libcmis_oauth2AuthCodeProvider callback ) +{ + libcmis::SessionFactory::setOAuth2AuthCodeProvider( callback ); +} + +libcmis_oauth2AuthCodeProvider libcmis_getOAuth2AuthCodeProvider( ) +{ + return libcmis::SessionFactory::getOAuth2AuthCodeProvider( ); +} + +void libcmis_setProxySettings( char* proxy, char* noProxy, + char* proxyUser, char* proxyPass ) +{ + libcmis::SessionFactory::setProxySettings( string( proxy ), string( noProxy ), + string( proxyUser ), string( proxyPass ) ); +} + +const char* libcmis_getProxy( ) +{ + return libcmis::SessionFactory::getProxy( ).c_str(); +} + +const char* libcmis_getNoProxy( ) +{ + return libcmis::SessionFactory::getNoProxy( ).c_str(); +} + +const char* libcmis_getProxyUser( ) +{ + return libcmis::SessionFactory::getProxyUser( ).c_str(); +} + +const char* libcmis_getProxyPass( ) +{ + return libcmis::SessionFactory::getProxyPass( ).c_str(); +} + +libcmis_SessionPtr libcmis_createSession( + char* bindingUrl, + char* repositoryId, + char* username, + char* password, + bool noSslCheck, + libcmis_OAuth2DataPtr oauth2, + bool verbose, + libcmis_ErrorPtr error ) +{ + libcmis_SessionPtr session = NULL; + + try + { + libcmis::OAuth2DataPtr oauth2Handle; + if ( oauth2 != NULL ) + oauth2Handle = oauth2->handle; + + libcmis::Session* handle = libcmis::SessionFactory::createSession( + createString( bindingUrl ), + createString( username ), + createString( password ), + createString( repositoryId ), noSslCheck, oauth2Handle, verbose ); + session = new libcmis_session( ); + session->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + + return session; +} + +libcmis_vector_Repository_Ptr libcmis_getRepositories( + char* bindingUrl, + char* username, + char* password, + bool verbose, + libcmis_ErrorPtr error ) +{ + libcmis_SessionPtr session = libcmis_createSession( + bindingUrl, NULL, username, password, false, NULL, verbose, error ); + libcmis_vector_Repository_Ptr repositories = libcmis_session_getRepositories( session ); + libcmis_session_free( session ); + return repositories; +} diff --git a/src/libcmis-c/session.cxx b/src/libcmis-c/session.cxx new file mode 100644 index 0000000..df6b503 --- /dev/null +++ b/src/libcmis-c/session.cxx @@ -0,0 +1,305 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/session.h> + +#include <utility> + +#include "internals.hxx" + +using namespace std; + +void libcmis_session_free( libcmis_SessionPtr session ) +{ + if ( session != NULL ) + { + delete session->handle; + delete session; + } +} + +libcmis_RepositoryPtr libcmis_session_getRepository( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ) +{ + libcmis_RepositoryPtr repository = NULL; + + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::RepositoryPtr handle = session->handle->getRepository( ); + if ( handle ) + { + repository = new ( nothrow ) libcmis_repository( ); + if ( repository ) + repository->handle = handle; + } + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + } + + return repository; +} + +libcmis_vector_Repository_Ptr libcmis_session_getRepositories( libcmis_SessionPtr session ) +{ + libcmis_vector_Repository_Ptr result = NULL; + if ( session != NULL && session->handle != NULL ) + { + vector< libcmis::RepositoryPtr > handles = session->handle->getRepositories(); + result = new ( nothrow ) libcmis_vector_repository( ); + if ( result ) + result->handle = handles; + } + + return result; +} + +bool libcmis_session_setRepository( libcmis_SessionPtr session, const char* id ) +{ + bool success = false; + if ( session && session->handle && id ) + { + success = session->handle->setRepository( id ); + } + return success; +} + +libcmis_FolderPtr libcmis_session_getRootFolder( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr folder = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::FolderPtr handle = session->handle->getRootFolder( ); + folder = new libcmis_folder( ); + folder->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return folder; +} + + +libcmis_ObjectPtr libcmis_session_getObject( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr object = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::ObjectPtr handle = session->handle->getObject( string( id ) ); + object = new libcmis_object( ); + object->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return object; +} + + +libcmis_ObjectPtr libcmis_session_getObjectByPath( + libcmis_SessionPtr session, + const char* path, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectPtr object = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::ObjectPtr handle = session->handle->getObjectByPath( string( path ) ); + object = new libcmis_object( ); + object->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return object; +} + + +libcmis_FolderPtr libcmis_session_getFolder( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_FolderPtr folder = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::FolderPtr handle = session->handle->getFolder( string( id ) ); + folder = new libcmis_folder( ); + folder->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return folder; +} + + +libcmis_ObjectTypePtr libcmis_session_getType( + libcmis_SessionPtr session, + const char* id, + libcmis_ErrorPtr error ) +{ + libcmis_ObjectTypePtr type = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + libcmis::ObjectTypePtr handle = session->handle->getType( string( id ) ); + type = new libcmis_object_type( ); + type->handle = handle; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return type; +} + +libcmis_vector_object_type_Ptr libcmis_session_getBaseTypes( + libcmis_SessionPtr session, + libcmis_ErrorPtr error ) +{ + libcmis_vector_object_type_Ptr types = NULL; + if ( session != NULL && session->handle != NULL ) + { + try + { + vector< libcmis::ObjectTypePtr > handles = session->handle->getBaseTypes( ); + types = new libcmis_vector_object_type( ); + types->handle = handles; + } + catch ( const libcmis::Exception& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->type = strdup( e.getType().c_str() ); + } + } + catch ( const bad_alloc& e ) + { + if ( error != NULL ) + { + error->message = strdup( e.what() ); + error->badAlloc = true; + } + } + } + return types; +} diff --git a/src/libcmis-c/vectors.cxx b/src/libcmis-c/vectors.cxx new file mode 100644 index 0000000..e520751 --- /dev/null +++ b/src/libcmis-c/vectors.cxx @@ -0,0 +1,139 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis-c/vectors.h> + +#include "internals.hxx" + +void libcmis_vector_bool_free( libcmis_vector_bool_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_bool_size( libcmis_vector_bool_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +bool libcmis_vector_bool_get( libcmis_vector_bool_Ptr vector, size_t i ) +{ + bool item = false; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i]; + return item; +} + +void libcmis_vector_string_free( libcmis_vector_string_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_string_size( libcmis_vector_string_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +const char* libcmis_vector_string_get( libcmis_vector_string_Ptr vector, size_t i ) +{ + const char* item = NULL; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i].c_str( ); + return item; +} + +void libcmis_vector_long_free( libcmis_vector_long_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_long_size( libcmis_vector_long_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +long libcmis_vector_long_get( libcmis_vector_long_Ptr vector, size_t i ) +{ + long item = 0; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i]; + return item; +} + +void libcmis_vector_double_free( libcmis_vector_double_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_double_size( libcmis_vector_double_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +double libcmis_vector_double_get( libcmis_vector_double_Ptr vector, size_t i ) +{ + double item = 0.0; + if ( vector != NULL && i < vector->handle.size( ) ) + item = vector->handle[i]; + return item; +} + +void libcmis_vector_time_free( libcmis_vector_time_Ptr vector ) +{ + delete vector; +} + +size_t libcmis_vector_time_size( libcmis_vector_time_Ptr vector ) +{ + size_t size = 0; + if ( vector != NULL ) + size = vector->handle.size( ); + return size; +} + +time_t libcmis_vector_time_get( libcmis_vector_time_Ptr vector, size_t i ) +{ + time_t item = 0; + if ( vector != NULL && i < vector->handle.size( ) ) + { + tm time = boost::posix_time::to_tm( vector->handle[i] ); + item = mktime( &time ); + } + return item; +} diff --git a/src/libcmis/Makefile.am b/src/libcmis/Makefile.am new file mode 100644 index 0000000..bc924f9 --- /dev/null +++ b/src/libcmis/Makefile.am @@ -0,0 +1,147 @@ +AM_CXXFLAGS = \ + -I$(top_srcdir)/inc \ + -I$(top_srcdir)/src/libcmis \ + $(XML2_CFLAGS) \ + $(CURL_CFLAGS) \ + $(BOOST_CPPFLAGS) + +if ENABLE_VISIBILITY +AM_CXXFLAGS += -fvisibility=hidden +endif + +noinst_LTLIBRARIES = libcmis.la + +lib_LTLIBRARIES = libcmis-@LIBCMIS_API_VERSION@.la + +libcmis_@LIBCMIS_API_VERSION@_la_SOURCES = \ + dummy.cxx + +libcmis_la_CPPFLAGS = -DLIBCMIS_BUILD +if ENABLE_VISIBILITY +libcmis_la_CPPFLAGS += -DLIBCMIS_VISIBILITY +endif + +libcmis_la_SOURCES = \ + allowable-actions.cxx \ + atom-document.cxx \ + atom-document.hxx \ + atom-folder.cxx \ + atom-folder.hxx \ + atom-object-type.cxx \ + atom-object-type.hxx \ + atom-object.cxx \ + atom-object.hxx \ + atom-session.cxx \ + atom-session.hxx \ + atom-workspace.cxx \ + atom-workspace.hxx \ + base-session.cxx \ + base-session.hxx \ + document.cxx \ + folder.cxx \ + gdrive-allowable-actions.hxx \ + gdrive-document.cxx \ + gdrive-document.hxx \ + gdrive-folder.cxx \ + gdrive-folder.hxx \ + gdrive-object-type.cxx \ + gdrive-object-type.hxx \ + gdrive-object.cxx \ + gdrive-object.hxx \ + gdrive-property.cxx \ + gdrive-property.hxx \ + gdrive-repository.cxx \ + gdrive-repository.hxx \ + gdrive-session.cxx \ + gdrive-session.hxx \ + gdrive-utils.cxx \ + gdrive-utils.hxx \ + http-session.cxx \ + http-session.hxx \ + json-utils.cxx \ + json-utils.hxx \ + oauth2-data.cxx \ + oauth2-handler.cxx \ + oauth2-handler.hxx \ + oauth2-providers.cxx \ + oauth2-providers.hxx \ + object-type.cxx \ + object.cxx \ + onedrive-allowable-actions.hxx \ + onedrive-document.cxx \ + onedrive-document.hxx \ + onedrive-folder.cxx \ + onedrive-folder.hxx \ + onedrive-object-type.cxx \ + onedrive-object-type.hxx \ + onedrive-object.cxx \ + onedrive-object.hxx \ + onedrive-property.cxx \ + onedrive-property.hxx \ + onedrive-repository.cxx \ + onedrive-repository.hxx \ + onedrive-session.cxx \ + onedrive-session.hxx \ + onedrive-utils.cxx \ + onedrive-utils.hxx \ + property-type.cxx \ + property.cxx \ + rendition.cxx \ + repository.cxx \ + session-factory.cxx \ + sharepoint-allowable-actions.hxx \ + sharepoint-document.cxx \ + sharepoint-document.hxx \ + sharepoint-folder.cxx \ + sharepoint-folder.hxx \ + sharepoint-object-type.cxx \ + sharepoint-object-type.hxx \ + sharepoint-object.cxx \ + sharepoint-object.hxx \ + sharepoint-property.cxx \ + sharepoint-property.hxx \ + sharepoint-repository.cxx \ + sharepoint-repository.hxx \ + sharepoint-session.cxx \ + sharepoint-session.hxx \ + sharepoint-utils.cxx \ + sharepoint-utils.hxx \ + ws-document.cxx \ + ws-document.hxx \ + ws-folder.cxx \ + ws-folder.hxx \ + ws-navigationservice.cxx \ + ws-navigationservice.hxx \ + ws-object-type.cxx \ + ws-object-type.hxx \ + ws-object.cxx \ + ws-object.hxx \ + ws-objectservice.cxx \ + ws-objectservice.hxx \ + ws-relatedmultipart.cxx \ + ws-relatedmultipart.hxx \ + ws-repositoryservice.cxx \ + ws-repositoryservice.hxx \ + ws-requests.cxx \ + ws-requests.hxx \ + ws-session.cxx \ + ws-session.hxx \ + ws-soap.cxx \ + ws-soap.hxx \ + ws-versioningservice.cxx \ + ws-versioningservice.hxx \ + xml-utils.cxx + +# -version-info current:revision:age see https://autotools.info/libtool/version.html +# Always increase the revision value. +# Increase the current value whenever an interface has been added, removed or changed. +# Increase the age value only if the changes made to the ABI are backward compatible. +libcmis_@LIBCMIS_API_VERSION@_la_LDFLAGS = -export-dynamic -no-undefined -version-info 7:1:1 + +libcmis_@LIBCMIS_API_VERSION@_la_LIBADD = \ + libcmis.la \ + $(XML2_LIBS) \ + $(CURL_LIBS) \ + $(BOOST_SMART_PTR_LIBS) \ + $(BOOST_DATE_TIME_LDFLAGS) \ + $(BOOST_DATE_TIME_LIBS) diff --git a/src/libcmis/allowable-actions.cxx b/src/libcmis/allowable-actions.cxx new file mode 100644 index 0000000..533069c --- /dev/null +++ b/src/libcmis/allowable-actions.cxx @@ -0,0 +1,294 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/allowable-actions.hxx> + +#include <libcmis/object.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + ObjectAction::ObjectAction( xmlNodePtr node ) : + m_type( ObjectAction::DeleteObject ), + m_enabled( false ), + m_valid( false ) + { + try + { + m_type = parseType( string( ( char* ) node->name ) ); + m_valid = true; + } + catch ( const Exception& ) + { + m_valid = false; + } + + // Invalid xsd:bool will be mean false... not sure what the spec says + try + { + xmlChar* content = xmlNodeGetContent( node ); + m_enabled = parseBool( string( ( char* )content ) ); + xmlFree( content ); + } + catch ( const Exception& ) + { + m_enabled = false; + } + } + + ObjectAction::Type ObjectAction::parseType( string type ) + { + Type value = DeleteObject; + if ( type == "canDeleteObject" ) + value = DeleteObject; + else if ( type == "canUpdateProperties" ) + value = UpdateProperties; + else if ( type == "canGetFolderTree" ) + value = GetFolderTree; + else if ( type == "canGetProperties" ) + value = GetProperties; + else if ( type == "canGetObjectRelationships" ) + value = GetObjectRelationships; + else if ( type == "canGetObjectParents" ) + value = GetObjectParents; + else if ( type == "canGetFolderParent" ) + value = GetFolderParent; + else if ( type == "canGetDescendants" ) + value = GetDescendants; + else if ( type == "canMoveObject" ) + value = MoveObject; + else if ( type == "canDeleteContentStream" ) + value = DeleteContentStream; + else if ( type == "canCheckOut" ) + value = CheckOut; + else if ( type == "canCancelCheckOut" ) + value = CancelCheckOut; + else if ( type == "canCheckIn" ) + value = CheckIn; + else if ( type == "canSetContentStream" ) + value = SetContentStream; + else if ( type == "canGetAllVersions" ) + value = GetAllVersions; + else if ( type == "canAddObjectToFolder" ) + value = AddObjectToFolder; + else if ( type == "canRemoveObjectFromFolder" ) + value = RemoveObjectFromFolder; + else if ( type == "canGetContentStream" ) + value = GetContentStream; + else if ( type == "canApplyPolicy" ) + value = ApplyPolicy; + else if ( type == "canGetAppliedPolicies" ) + value = GetAppliedPolicies; + else if ( type == "canRemovePolicy" ) + value = RemovePolicy; + else if ( type == "canGetChildren" ) + value = GetChildren; + else if ( type == "canCreateDocument" ) + value = CreateDocument; + else if ( type == "canCreateFolder" ) + value = CreateFolder; + else if ( type == "canCreateRelationship" ) + value = CreateRelationship; + else if ( type == "canDeleteTree" ) + value = DeleteTree; + else if ( type == "canGetRenditions" ) + value = GetRenditions; + else if ( type == "canGetACL" ) + value = GetACL; + else if ( type == "canApplyACL" ) + value = ApplyACL; + else + throw Exception( "Invalid AllowableAction type: " + type ); + + return value; + } + + AllowableActions::AllowableActions( ) : + m_states( ) + { + } + + AllowableActions::AllowableActions( xmlNodePtr node ) : + m_states( ) + { + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + // Check for non text children... "\n" is also a node ;) + if ( !xmlNodeIsText( child ) ) + { + ObjectAction action( child ); + if ( action.isValid( ) ) + m_states.insert( pair< libcmis::ObjectAction::Type, bool >( + action.getType( ), + action.isEnabled() ) ); + } + } + } + + AllowableActions::AllowableActions( const AllowableActions& copy ) : + m_states( copy.m_states ) + { + } + + AllowableActions::~AllowableActions( ) + { + m_states.clear(); + } + + AllowableActions& AllowableActions::operator=( const AllowableActions& copy ) + { + if ( this != © ) + m_states = copy.m_states; + + return *this; + } + + bool AllowableActions::isAllowed( ObjectAction::Type action ) + { + bool allowed = false; + + map< ObjectAction::Type, bool>::iterator it = m_states.find( action ); + if ( it != m_states.end() ) + allowed = it->second; + + return allowed; + } + + bool AllowableActions::isDefined( ObjectAction::Type action ) + { + map< ObjectAction::Type, bool>::iterator it = m_states.find( action ); + return it != m_states.end(); + } + + // LCOV_EXCL_START + string AllowableActions::toString( ) + { + stringstream buf; + + for ( map< ObjectAction::Type, bool >::iterator it = m_states.begin( ); + it != m_states.end( ); ++it ) + { + switch ( it->first ) + { + case ObjectAction::DeleteObject: + buf << "canDeleteObject"; + break; + case ObjectAction::UpdateProperties: + buf << "canUpdateProperties"; + break; + case ObjectAction::GetFolderTree: + buf << "canGetFolderTree"; + break; + case ObjectAction::GetProperties: + buf << "canGetProperties"; + break; + case ObjectAction::GetObjectRelationships: + buf << "canGetObjectRelationships"; + break; + case ObjectAction::GetObjectParents: + buf << "canGetObjectParents"; + break; + case ObjectAction::GetFolderParent: + buf << "canGetFolderParent"; + break; + case ObjectAction::GetDescendants: + buf << "canGetDescendants"; + break; + case ObjectAction::MoveObject: + buf << "canMoveObject"; + break; + case ObjectAction::DeleteContentStream: + buf << "canDeleteContentStream"; + break; + case ObjectAction::CheckOut: + buf << "canCheckOut"; + break; + case ObjectAction::CancelCheckOut: + buf << "canCancelCheckOut"; + break; + case ObjectAction::CheckIn: + buf << "canCheckIn"; + break; + case ObjectAction::SetContentStream: + buf << "canSetContentStream"; + break; + case ObjectAction::GetAllVersions: + buf << "canGetAllVersions"; + break; + case ObjectAction::AddObjectToFolder: + buf << "canAddObjectToFolder"; + break; + case ObjectAction::RemoveObjectFromFolder: + buf << "canRemoveObjectFromFolder"; + break; + case ObjectAction::GetContentStream: + buf << "canGetContentStream"; + break; + case ObjectAction::ApplyPolicy: + buf << "canApplyPolicy"; + break; + case ObjectAction::GetAppliedPolicies: + buf << "canGetAppliedPolicies"; + break; + case ObjectAction::RemovePolicy: + buf << "canRemovePolicy"; + break; + case ObjectAction::GetChildren: + buf << "canGetChildren"; + break; + case ObjectAction::CreateDocument: + buf << "canCreateDocument"; + break; + case ObjectAction::CreateFolder: + buf << "canCreateFolder"; + break; + case ObjectAction::CreateRelationship: + buf << "canCreateRelationship"; + break; + case ObjectAction::DeleteTree: + buf << "canDeleteTree"; + break; + case ObjectAction::GetRenditions: + buf << "canGetRenditions"; + break; + case ObjectAction::GetACL: + buf << "canGetACL"; + break; + case ObjectAction::ApplyACL: + buf << "canApplyACL"; + break; + } + buf << ": " << it->second << endl; + } + + return buf.str( ); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/atom-document.cxx b/src/libcmis/atom-document.cxx new file mode 100644 index 0000000..e0400b1 --- /dev/null +++ b/src/libcmis/atom-document.cxx @@ -0,0 +1,480 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-document.hxx" + +#include <algorithm> +#include <stdlib.h> +#include <sstream> + +#include <curl/curl.h> + +#include <libcmis/xml-utils.hxx> + +#include "atom-session.hxx" + +using namespace std; +using namespace libcmis; + +AtomDocument::AtomDocument( AtomPubSession* session ) : + libcmis::Object( session ), + libcmis::Document( session ), + AtomObject( session ), + m_contentUrl( ) +{ +} + + +AtomDocument::AtomDocument( AtomPubSession* session, xmlNodePtr entryNd ) : + libcmis::Object( session ), + libcmis::Document( session ), + AtomObject( session ), + m_contentUrl( ) +{ + xmlDocPtr doc = libcmis::wrapInDoc( entryNd ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + +AtomDocument::~AtomDocument( ) +{ +} + +vector< libcmis::FolderPtr > AtomDocument::getParents( ) +{ + AtomLink* parentsLink = getLink( "up", "" ); + + if ( ( NULL == parentsLink ) || + ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetObjectParents ) ) ) + throw libcmis::Exception( string( "GetObjectParents not allowed on node " ) + getId() ); + + vector< libcmis::FolderPtr > parents; + + string buf; + try + { + buf = getSession()->httpGetRequest( parentsLink->getHref( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), parentsLink->getHref( ).c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + xmlDocPtr entryDoc = libcmis::wrapInDoc( node ); + libcmis::ObjectPtr object = getSession()->createObjectFromEntryDoc( entryDoc ); + libcmis::FolderPtr folder = boost::dynamic_pointer_cast< libcmis::Folder >( object ); + + if ( folder.get() ) + parents.push_back( folder ); + xmlFreeDoc( entryDoc ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse folder infos" ); + } + xmlFreeDoc( doc ); + + return parents; +} + +boost::shared_ptr< istream > AtomDocument::getContentStream( string /*streamId*/ ) +{ + if ( getAllowableActions().get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetContentStream ) ) + throw libcmis::Exception( string( "GetContentStream is not allowed on document " ) + getId() ); + + boost::shared_ptr< istream > stream; + try + { + stream = getSession()->httpGetRequest( m_contentUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + return stream; +} + +void AtomDocument::setContentStream( boost::shared_ptr< ostream > os, string contentType, string fileName, bool overwrite ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + if ( getAllowableActions().get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetContentStream ) ) + throw libcmis::Exception( string( "SetContentStream is not allowed on document " ) + getId() ); + + string overwriteStr( "false" ); + if ( overwrite ) + overwriteStr = "true"; + + string urlPattern( m_contentUrl ); + if ( urlPattern.find( '?' ) != string::npos ) + urlPattern += "&"; + else + urlPattern += "?"; + urlPattern += "overwriteFlag={overwriteFlag}"; + + map< string, string > params; + params["overwriteFlag"] = overwriteStr; + + // Use the changeToken if set on the object + if ( !getChangeToken().empty() ) + { + urlPattern += "&changeToken={changeToken}"; + params["changeToken"] = getChangeToken(); + } + + string putUrl = getSession()->createUrl( urlPattern, params ); + + bool tryBase64 = false; + do + { + try + { + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + if ( tryBase64 ) + { + tryBase64 = false; + + // Encode the content + stringstream* encodedIn = new stringstream( ); + libcmis::EncodedData encoder( encodedIn ); + encoder.setEncoding( "base64" ); + + int bufLength = 1000; + char* buf = new char[ bufLength ]; + do + { + is->read( buf, bufLength ); + int size = is->gcount( ); + encoder.encode( buf, 1, size ); + } while ( !is->eof( ) && !is->fail( ) ); + delete[] buf; + encoder.finish( ); + + encodedIn->seekg( 0, ios_base::beg ); + encodedIn->clear( ); + + is.reset( encodedIn ); + } + vector< string > headers; + headers.push_back( string( "Content-Type: " ) + contentType ); + if ( !fileName.empty( ) ) + headers.push_back( string( "Content-Disposition: attachment; filename=" ) + fileName ); + getSession()->httpPutRequest( putUrl, *is, headers ); + + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + throw libcmis::Exception( "Document content wasn't set for some reason" ); + refresh( ); + } + catch ( const CurlException& e ) + { + // SharePoint wants base64 encoded content... let's try to figure out + // if we falled in that case. + if ( !tryBase64 && e.getHttpStatus() == 400 ) + tryBase64 = true; + else + throw e.getCmisException( ); + } + } + while ( tryBase64 ); +} + +libcmis::DocumentPtr AtomDocument::checkOut( ) +{ + if ( ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::CheckOut ) ) ) + throw libcmis::Exception( string( "CanCheckout not allowed on document " ) + getId() ); + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + // Create a document with only the needed properties + PropertyPtrMap props; + PropertyPtrMap::iterator it = getProperties( ).find( string( "cmis:objectId" ) ); + if ( it != getProperties( ).end( ) ) + { + props.insert( *it ); + } + + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, props, stream, string( ) ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + libcmis::HttpResponsePtr resp; + string urlPattern = getSession()->getAtomRepository( )->getCollectionUrl( Collection::CheckedOut ); + if ( urlPattern.find( "?" ) != string::npos ) + urlPattern += "&"; + else + urlPattern += "?"; + urlPattern += "objectId={objectId}"; + + map< string, string > params; + params[ "objectId" ] = getId( ); + string checkedOutUrl = getSession( )->createUrl( urlPattern, params ); + + try + { + resp = getSession( )->httpPostRequest( checkedOutUrl, is, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string respBuf = resp->getStream( )->str(); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), checkedOutUrl.c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc, AtomPubSession::RESULT_DOCUMENT ); + xmlFreeDoc( doc ); + + libcmis::DocumentPtr pwc = boost::dynamic_pointer_cast< libcmis::Document >( created ); + if ( !pwc.get( ) ) + throw libcmis::Exception( string( "Created object is not a document: " ) + created->getId( ) ); + + return pwc; +} + +void AtomDocument::cancelCheckout( ) +{ + if ( ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::CancelCheckOut ) ) ) + throw libcmis::Exception( string( "CanCancelCheckout not allowed on document " ) + getId() ); + + string url = getInfosUrl( ); + + // Use working-copy link if provided as a workaround + // for some non-compliant repositories + AtomLink* link = getLink( "working-copy", "application/atom+xml;type=entry" ); + if ( link ) + url = link->getHref( ); + + try + { + getSession( )->httpDeleteRequest( url ); + } + catch ( CurlException const& e ) + { + throw e.getCmisException( ); + } +} + +libcmis::DocumentPtr AtomDocument::checkIn( bool isMajor, string comment, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > stream, string contentType, string ) +{ + if ( ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::CheckIn ) ) ) + throw libcmis::Exception( string( "CanCheckIn not allowed on document " ) + getId() ); + + string urlPattern = getInfosUrl( ); + + // Use working-copy link if provided as a workaround + // for some non-compliant repositories + AtomLink* link = getLink( "working-copy", "application/atom+xml;type=entry" ); + if ( link ) + urlPattern = link->getHref( ); + + if ( urlPattern.find( "?" ) != string::npos ) + urlPattern += "&"; + else + urlPattern += "?"; + urlPattern += "checkin=true&major={major}&checkinComment={checkinComment}"; + + map< string, string > params; + + string majorStr = "false"; + if ( isMajor ) + majorStr = "true"; + params[ "major" ] = majorStr; + params[ "checkinComment" ] = comment; + string checkInUrl = getSession( )->createUrl( urlPattern, params ); + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + AtomObject::writeAtomEntry( writer, properties, stream, contentType ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + // Run the request + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( string( "Content-Type: application/atom+xml;type=entry" ) ); + response = getSession( )->httpPutRequest( checkInUrl, is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // Get the returned entry and update using it + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), checkInUrl.c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + + libcmis::ObjectPtr newVersion = getSession( )->createObjectFromEntryDoc( doc, AtomPubSession::RESULT_DOCUMENT ); + + if ( newVersion->getId( ) == getId( ) ) + refreshImpl( doc ); + xmlFreeDoc( doc ); + + return boost::dynamic_pointer_cast< libcmis::Document >( newVersion ); +} + +vector< libcmis::DocumentPtr > AtomDocument::getAllVersions( ) +{ + if ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetAllVersions ) ) + throw libcmis::Exception( string( "GetAllVersions not allowed on node " ) + getId() ); + + vector< libcmis::DocumentPtr > versions; + AtomLink* link = getLink( "version-history", string( ) ); + if ( link != NULL ) + { + string pageUrl = link->getHref( ); + + string buf; + try + { + buf = getSession()->httpGetRequest( pageUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), pageUrl.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + // Get the entries + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + xmlDocPtr entryDoc = libcmis::wrapInDoc( node ); + libcmis::ObjectPtr cmisObject = getSession()->createObjectFromEntryDoc( entryDoc ); + libcmis::DocumentPtr cmisDoc = boost::dynamic_pointer_cast< libcmis::Document >( cmisObject ); + + if ( cmisDoc.get() ) + versions.push_back( cmisDoc ); + xmlFreeDoc( entryDoc ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse versions infos" ); + } + xmlFreeDoc( doc ); + + } + return versions; +} + +void AtomDocument::extractInfos( xmlDocPtr doc ) +{ + AtomObject::extractInfos( doc ); + + // Get the content url + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + if ( NULL != doc ) + { + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//atom:content" ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlNodePtr contentNd = xpathObj->nodesetval->nodeTab[0]; + xmlChar* src = xmlGetProp( contentNd, BAD_CAST( "src" ) ); + m_contentUrl = string( ( char* ) src ); + xmlFree( src ); + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } +} diff --git a/src/libcmis/atom-document.hxx b/src/libcmis/atom-document.hxx new file mode 100644 index 0000000..40d7109 --- /dev/null +++ b/src/libcmis/atom-document.hxx @@ -0,0 +1,66 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_DOCUMENT_HXX_ +#define _ATOM_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "atom-object.hxx" + +class AtomDocument : public libcmis::Document, public AtomObject +{ + private: + std::string m_contentUrl; + + public: + AtomDocument( AtomPubSession* session ); + AtomDocument( AtomPubSession* session, xmlNodePtr entryNd ); + ~AtomDocument( ); + + virtual std::vector< libcmis::FolderPtr > getParents( ); + + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, std::string contentType, + std::string fileName, bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + virtual libcmis::DocumentPtr checkIn( bool isMajor, std::string comment, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); + + protected: + virtual void extractInfos( xmlDocPtr doc ); +}; + +#endif diff --git a/src/libcmis/atom-folder.cxx b/src/libcmis/atom-folder.cxx new file mode 100644 index 0000000..5e41194 --- /dev/null +++ b/src/libcmis/atom-folder.cxx @@ -0,0 +1,322 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-folder.hxx" + +#include <sstream> + +#include <boost/shared_ptr.hpp> + +#include <libcmis/xml-utils.hxx> + +#include "atom-document.hxx" +#include "atom-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace +{ +} + +AtomFolder::AtomFolder( AtomPubSession* session, xmlNodePtr entryNd ) : + libcmis::Object( session ), + libcmis::Folder( session ), + AtomObject( session ) +{ + xmlDocPtr doc = libcmis::wrapInDoc( entryNd ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + + +AtomFolder::~AtomFolder( ) +{ +} + +vector< libcmis::ObjectPtr > AtomFolder::getChildren( ) +{ + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + + // Some servers aren't giving the GetChildren properly... if not defined, we need to try + // as we may have the right to proceed. + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && + ( !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetChildren ) && + getAllowableActions()->isDefined( libcmis::ObjectAction::GetChildren ) ) ) ) + throw libcmis::Exception( string( "GetChildren not allowed on node " ) + getId() ); + + vector< libcmis::ObjectPtr > children; + + string pageUrl = childrenLink->getHref( ); + + bool hasNext = true; + while ( hasNext ) + { + string buf; + try + { + buf = getSession()->httpGetRequest( pageUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), pageUrl.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + // Check if there is a next link to handled paged results + const string& nextReq( "/atom:feed/atom:link[@rel='next']/attribute::href" ); + string nextHref = libcmis::getXPathValue( xpathCtx, nextReq ); + hasNext = !nextHref.empty( ); + if ( hasNext ) + pageUrl = nextHref; + + // Get the page entries + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + xmlDocPtr entryDoc = libcmis::wrapInDoc( node ); + libcmis::ObjectPtr cmisObject = getSession()->createObjectFromEntryDoc( entryDoc ); + + if ( cmisObject.get() ) + children.push_back( cmisObject ); + xmlFreeDoc( entryDoc ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse folder infos" ); + } + xmlFreeDoc( doc ); + } + + return children; +} + +libcmis::FolderPtr AtomFolder::createFolder( const PropertyPtrMap& properties ) +{ + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateFolder ) ) ) + throw libcmis::Exception( string( "CreateFolder not allowed on folder " ) + getId(), "permissionDenied" ); + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + // Copy and remove the readonly properties before serializing + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, properties, stream, string( ) ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + libcmis::HttpResponsePtr response; + try + { + response = getSession( )->httpPostRequest( childrenLink->getHref( ), is, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + /* 409 here is more likely to be a constraint error */ + if ( e.getHttpStatus() == 409 ) { + throw libcmis::Exception( e.what(), "constraint" ); + } + throw e.getCmisException( ); + } + + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc, AtomPubSession::RESULT_FOLDER ); + xmlFreeDoc( doc ); + + libcmis::FolderPtr newFolder = boost::dynamic_pointer_cast< libcmis::Folder >( created ); + if ( !newFolder.get( ) ) + throw libcmis::Exception( string( "Created object is not a folder: " ) + created->getId( ), "constraint" ); + + return newFolder; +} + +libcmis::DocumentPtr AtomFolder::createDocument( const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType, string ) +{ + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateDocument ) && + getAllowableActions()->isDefined( libcmis::ObjectAction::CreateDocument ) ) ) + throw libcmis::Exception( string( "CreateDocument not allowed on folder " ) + getId() ); + + stringstream ss; + xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(libcmis::stringstream_write_callback, NULL, &ss, NULL); + xmlTextWriterPtr writer = xmlNewTextWriter(buf); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + AtomObject::writeAtomEntry( writer, properties, os, contentType ); + + xmlTextWriterEndDocument( writer ); + xmlFreeTextWriter( writer ); + + libcmis::HttpResponsePtr response; + try + { + response = getSession( )->httpPostRequest( childrenLink->getHref( ), ss, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string respBuf = response->getStream( )->str( ); + boost::shared_ptr< xmlDoc > doc( xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, XML_PARSE_NOERROR ), xmlFreeDoc ); + if ( !doc ) + { + // We may not have the created document entry in the response body: this is + // the behaviour of some servers, but the standard says we need to look for + // the Location header. + map< string, string >& headers = response->getHeaders( ); + map< string, string >::iterator it = headers.find( "Location" ); + + // Some servers like Lotus Live aren't sending Location header, but Content-Location + if ( it == headers.end( ) ) + it = headers.find( "Content-Location" ); + + if ( it != headers.end() ) + { + try + { + response = getSession( )->httpGetRequest( it->second ); + respBuf = response->getStream( )->str( ); + doc.reset( xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, XML_PARSE_NOERROR ), xmlFreeDoc ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + } + + // if doc is still NULL after that, then throw an exception + if ( !doc ) + throw libcmis::Exception( "Missing expected response from server" ); + } + + libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc.get(), AtomPubSession::RESULT_DOCUMENT ); + + libcmis::DocumentPtr newDocument = boost::dynamic_pointer_cast< libcmis::Document >( created ); + if ( !newDocument.get( ) ) + throw libcmis::Exception( string( "Created object is not a document: " ) + created->getId( ) ); + + return newDocument; +} + +vector< string > AtomFolder::removeTree( bool allVersions, libcmis::UnfileObjects::Type unfile, + bool continueOnError ) +{ + AtomLink* treeLink = getLink( "down", "application/cmistree+xml" ); + if ( NULL == treeLink ) + treeLink = getLink( "http://docs.oasis-open.org/ns/cmis/link/200908/foldertree", "application/cmistree+xml" ); + + if ( ( NULL == treeLink ) || ( getAllowableActions( ).get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::DeleteTree ) ) ) + throw libcmis::Exception( string( "DeleteTree not allowed on folder " ) + getId() ); + + try + { + string deleteUrl = treeLink->getHref( ); + if ( deleteUrl.find( '?' ) != string::npos ) + deleteUrl += "&"; + else + deleteUrl += "?"; + + // Add the all versions parameter + string allVersionsStr = "TRUE"; + if ( !allVersions ) + allVersionsStr = "FALSE"; + deleteUrl += "allVersions=" + allVersionsStr; + + // Add the unfileObjects parameter + string unfileStr; + switch ( unfile ) + { + case libcmis::UnfileObjects::Delete: + unfileStr = "delete"; + break; + case libcmis::UnfileObjects::DeleteSingleFiled: + unfileStr = "deletesinglefiled"; + break; + case libcmis::UnfileObjects::Unfile: + unfileStr = "unfile"; + break; + default: + break; + } + deleteUrl += "&unfileObjects=" + unfileStr; + + // Add the continueOnFailure parameter + string continueOnErrorStr = "TRUE"; + if ( !continueOnError ) + continueOnErrorStr = "FALSE"; + deleteUrl += "&continueOnFailure=" + continueOnErrorStr; + + getSession( )->httpDeleteRequest( deleteUrl ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // TODO Implement getting the failedIDs using a GET request on the same URL + return vector< string >( ); +} diff --git a/src/libcmis/atom-folder.hxx b/src/libcmis/atom-folder.hxx new file mode 100644 index 0000000..8137487 --- /dev/null +++ b/src/libcmis/atom-folder.hxx @@ -0,0 +1,56 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_FOLDER_HXX_ +#define _ATOM_FOLDER_HXX_ + +#include <string> + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "atom-object.hxx" + +class AtomFolder : public libcmis::Folder, public AtomObject +{ + public: + AtomFolder( AtomPubSession* session, xmlNodePtr entryNd ); + ~AtomFolder( ); + + // virtual pure methods from Folder + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( const std::map< std::string, libcmis::PropertyPtr >& properties ); + virtual libcmis::DocumentPtr createDocument( const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType, std::string fileName ); + + virtual std::vector< std::string > removeTree( bool allVersion = true, + libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/atom-object-type.cxx b/src/libcmis/atom-object-type.cxx new file mode 100644 index 0000000..8f50d04 --- /dev/null +++ b/src/libcmis/atom-object-type.cxx @@ -0,0 +1,167 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-object-type.hxx" + +#include <sstream> + +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace boost; + + +AtomObjectType::AtomObjectType( AtomPubSession* session, string id ) : + libcmis::ObjectType( ), + m_session( session ), + m_selfUrl( ), + m_childrenUrl( ) +{ + m_id = id; + refresh( ); +} + +AtomObjectType::AtomObjectType( AtomPubSession* session, xmlNodePtr entryNd ) : + libcmis::ObjectType( ), + m_session( session ), + m_selfUrl( ), + m_childrenUrl( ) +{ + xmlDocPtr doc = libcmis::wrapInDoc( entryNd ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + +AtomObjectType::AtomObjectType( const AtomObjectType& copy ) : + libcmis::ObjectType( copy ), + m_session( copy.m_session ), + m_selfUrl( copy.m_selfUrl ), + m_childrenUrl( copy.m_childrenUrl ) +{ +} + +AtomObjectType::~AtomObjectType( ) +{ +} + +AtomObjectType& AtomObjectType::operator=( const AtomObjectType& copy ) +{ + if ( this != © ) + { + libcmis::ObjectType::operator=( copy ); + m_session = copy.m_session; + m_selfUrl = copy.m_selfUrl; + m_childrenUrl = copy.m_childrenUrl; + } + + return *this; +} + +libcmis::ObjectTypePtr AtomObjectType::getParentType( ) +{ + return m_session->getType( m_parentTypeId ); +} + +libcmis::ObjectTypePtr AtomObjectType::getBaseType( ) +{ + return m_session->getType( m_baseTypeId ); +} + +vector< libcmis::ObjectTypePtr > AtomObjectType::getChildren( ) +{ + return m_session->getChildrenTypes( m_childrenUrl ); +} + +void AtomObjectType::refreshImpl( xmlDocPtr doc ) +{ + bool createdDoc = ( NULL == doc ); + if ( createdDoc ) + { + string pattern = m_session->getAtomRepository()->getUriTemplate( UriTemplate::TypeById ); + map< string, string > vars; + vars[URI_TEMPLATE_VAR_ID] = getId( ); + string url = m_session->createUrl( pattern, vars ); + + string buf; + try + { + buf = m_session->httpGetRequest( url )->getStream()->str(); + } + catch ( const CurlException& e ) + { + if ( ( e.getErrorCode( ) == CURLE_HTTP_RETURNED_ERROR ) && + ( e.getHttpStatus( ) == 404 ) ) + { + string msg = "No such type: "; + msg += getId( ); + throw libcmis::Exception( msg, "objectNotFound" ); + } + else + throw e.getCmisException( ); + } + + doc = xmlReadMemory( buf.c_str(), buf.size(), m_selfUrl.c_str(), NULL, 0 ); + + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + } + + extractInfos( doc ); + + if ( createdDoc ) + xmlFreeDoc( doc ); +} + +void AtomObjectType::extractInfos( xmlDocPtr doc ) +{ + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + + // Register the Service Document namespaces + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + // Get the self URL + string selfUrlReq( "//atom:link[@rel='self']/attribute::href" ); + m_selfUrl = libcmis::getXPathValue( xpathCtx, selfUrlReq ); + + // Get the children URL + string childrenUrlReq( "//atom:link[@rel='down' and @type='application/atom+xml;type=feed']/attribute::href" ); + m_childrenUrl = libcmis::getXPathValue( xpathCtx, childrenUrlReq ); + + // Get the cmisra:type node + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:type" ), xpathCtx ); + if ( NULL != xpathObj && NULL != xpathObj->nodesetval && xpathObj->nodesetval->nodeNr ) + { + xmlNodePtr typeNode = xpathObj->nodesetval->nodeTab[0]; + initializeFromNode( typeNode ); + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); +} diff --git a/src/libcmis/atom-object-type.hxx b/src/libcmis/atom-object-type.hxx new file mode 100644 index 0000000..c4c80dd --- /dev/null +++ b/src/libcmis/atom-object-type.hxx @@ -0,0 +1,63 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_OBJECT_TYPE_HXX_ +#define _ATOM_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "atom-session.hxx" + +class AtomObjectType : public libcmis::ObjectType +{ + private: + AtomPubSession* m_session; + + std::string m_selfUrl; + std::string m_childrenUrl; + + public: + AtomObjectType( AtomPubSession* session, std::string id ); + AtomObjectType( AtomPubSession* session, xmlNodePtr node ); + AtomObjectType( const AtomObjectType& copy ); + virtual ~AtomObjectType( ); + + AtomObjectType& operator=( const AtomObjectType& copy ); + + virtual void refresh( ) { refreshImpl( NULL ); } + + virtual libcmis::ObjectTypePtr getParentType( ); + virtual libcmis::ObjectTypePtr getBaseType( ); + virtual std::vector< libcmis::ObjectTypePtr > getChildren( ); + + private: + + void refreshImpl( xmlDocPtr doc ); + void extractInfos( xmlDocPtr doc ); +}; + +#endif diff --git a/src/libcmis/atom-object.cxx b/src/libcmis/atom-object.cxx new file mode 100644 index 0000000..65332f6 --- /dev/null +++ b/src/libcmis/atom-object.cxx @@ -0,0 +1,486 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-object.hxx" + +#include <algorithm> +#include <locale> +#include <sstream> + +#include <boost/algorithm/string.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <libcmis/xml-utils.hxx> + +#include "atom-document.hxx" +#include "atom-folder.hxx" +#include "atom-object-type.hxx" +#include "atom-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace +{ + class MatchLink + { + private: + string m_rel; + string m_type; + + public: + MatchLink( string rel, string type ) : m_rel( rel ), m_type( type ) { } + bool operator() ( const AtomLink &link ) + { + bool matchesRel = link.getRel( ) == m_rel; + + // Some implementations (xcmis) put extra spaces into the type attribute + // (e.g. "application/atom+xml; type=feed" instead of "application/atom+xml;type=feed") + string linkType = link.getType( ); + linkType.erase( remove_if( linkType.begin(), linkType.end(), boost::is_space() ), linkType.end() ); + + // Some implementation (SharePoint) are omitting the type attribute + bool matchesType = m_type.empty( ) || linkType.empty() || ( linkType == m_type ); + return matchesRel && matchesType; + } + }; +} + +AtomObject::AtomObject( AtomPubSession* session ) : + libcmis::Object( session ), + m_links( ) +{ +} + +AtomObject::AtomObject( const AtomObject& copy ) : + libcmis::Object( copy ), + m_links( copy.m_links ) +{ +} + +AtomObject& AtomObject::operator=( const AtomObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + m_links = copy.m_links; + } + + return *this; +} + +AtomObject::~AtomObject( ) +{ +} + +libcmis::ObjectPtr AtomObject::updateProperties( const PropertyPtrMap& properties ) +{ + if ( getAllowableActions().get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::UpdateProperties ) ) + throw libcmis::Exception( string( "UpdateProperties is not allowed on object " ) + getId() ); + + // No need to send HTTP request if there is nothing to update + if ( properties.empty( ) ) + { + libcmis::ObjectPtr object; + if ( getBaseType( ) == "cmis:document" ) + { + const AtomDocument& thisDoc = dynamic_cast< const AtomDocument& >( *this ); + object.reset( new AtomDocument( thisDoc ) ); + } + else if ( getBaseType( ) == "cmis:folder" ) + { + const AtomFolder& thisFolder = dynamic_cast< const AtomFolder& >( *this ); + object.reset( new AtomFolder( thisFolder ) ); + } + return object; + } + + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + // Copy and remove the readonly properties before serializing + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, properties, stream, string( ) ); + + xmlTextWriterEndDocument( writer ); + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/atom+xml;type=entry" ); + response = getSession( )->httpPutRequest( getInfosUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + libcmis::ObjectPtr updated = getSession( )->createObjectFromEntryDoc( doc ); + if ( updated->getId( ) == getId( ) ) + refreshImpl( doc ); + xmlFreeDoc( doc ); + + return updated; +} + +libcmis::AllowableActionsPtr AtomObject::getAllowableActions( ) +{ + if ( !m_allowableActions ) + { + // For some reason we had no allowable actions before, get them now. + AtomLink* link = getLink( "http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions", "application/cmisallowableactions+xml" ); + if ( link ) + { + try + { + libcmis::HttpResponsePtr response = getSession()->httpGetRequest( link->getHref() ); + string buf = response->getStream()->str(); + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), link->getHref().c_str(), NULL, 0 ); + xmlNodePtr actionsNode = xmlDocGetRootElement( doc ); + if ( actionsNode ) + m_allowableActions.reset( new libcmis::AllowableActions( actionsNode ) ); + + xmlFreeDoc( doc ); + } + catch ( CurlException& ) + { + } + } + } + + return libcmis::Object::getAllowableActions(); +} + +void AtomObject::refreshImpl( xmlDocPtr doc ) +{ + bool createdDoc = ( NULL == doc ); + if ( createdDoc ) + { + string buf; + try + { + buf = getSession()->httpGetRequest( getInfosUrl() )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + doc = xmlReadMemory( buf.c_str(), buf.size(), getInfosUrl().c_str(), NULL, 0 ); + + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + + } + + // Cleanup the structures before setting them again + m_typeDescription.reset( ); + m_properties.clear( ); + m_allowableActions.reset( ); + m_links.clear( ); + m_renditions.clear( ); + + extractInfos( doc ); + + if ( createdDoc ) + xmlFreeDoc( doc ); +} + +void AtomObject::remove( bool allVersions ) +{ + if ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::DeleteObject ) ) + throw libcmis::Exception( string( "DeleteObject not allowed on object " ) + getId() ); + + try + { + string deleteUrl = getInfosUrl( ); + if ( deleteUrl.find( '?' ) != string::npos ) + deleteUrl += "&"; + else + deleteUrl += "?"; + + string allVersionsStr = "TRUE"; + if ( !allVersions ) + allVersionsStr = "FALSE"; + deleteUrl += "allVersions=" + allVersionsStr; + + getSession( )->httpDeleteRequest( deleteUrl ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +void AtomObject::move( boost::shared_ptr< libcmis::Folder > source, boost::shared_ptr< libcmis::Folder > destination ) +{ + AtomFolder* atomDestination = dynamic_cast< AtomFolder* > ( destination.get() ); + + if ( NULL == atomDestination ) + throw libcmis::Exception( string( "Destination is not an AtomFolder" ) ); + + AtomLink* destChildrenLink = atomDestination->getLink( "down", "application/atom+xml;type=feed" ); + + if ( ( NULL == destChildrenLink ) || ( getAllowableActions().get() && + !getAllowableActions()->isAllowed( libcmis::ObjectAction::MoveObject ) ) ) + throw libcmis::Exception( string( "MoveObject not allowed on object " ) + getId() ); + + // create object xml + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + boost::shared_ptr< ostream > stream; + AtomObject::writeAtomEntry( writer, getProperties( ), stream, string( ) ); + xmlTextWriterEndDocument( writer ); + + string str( ( const char * )xmlBufferContent( buf ) ); + istringstream is( str ); + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + // create post url + string postUrl = destChildrenLink->getHref(); + if ( postUrl.find( '?' ) != string::npos ) + postUrl += "&"; + else + postUrl += "?"; + postUrl += "sourceFolderId={sourceFolderId}"; + // Session::CreateUrl is used to properly escape the id + map< string, string > params; + params[ "sourceFolderId" ] = source->getId(); + postUrl = getSession( )->createUrl( postUrl, params ); + + // post it + libcmis::HttpResponsePtr response; + try + { + response = getSession( )->httpPostRequest( postUrl, is, "application/atom+xml;type=entry" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // refresh self from response + string respBuf = response->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, 0 ); + if ( NULL == doc ) + throw libcmis::Exception( "Failed to parse object infos" ); + refreshImpl( doc ); + xmlFreeDoc( doc ); +} + +string AtomObject::getInfosUrl( ) +{ + AtomLink* selfLink = getLink( "self", "application/atom+xml;type=entry" ); + if ( NULL != selfLink ) + return selfLink->getHref( ); + return string( ); +} + +void AtomObject::extractInfos( xmlDocPtr doc ) +{ + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + m_links.clear( ); + m_renditions.clear( ); + + // Get all the atom links + string linksReq( "//atom:link" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( linksReq.c_str() ), xpathCtx ); + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + try + { + AtomLink link( node ); + // Add to renditions if alternate link + if ( link.getRel( ) == "alternate" ) + { + string kind; + map< string, string >::iterator it = link.getOthers().find( "renditionKind" ); + if ( it != link.getOthers( ).end() ) + kind = it->second; + + string title; + it = link.getOthers().find( "title" ); + if ( it != link.getOthers( ).end( ) ) + title = it->second; + + long length = -1; + it = link.getOthers( ).find( "length" ); + if ( it != link.getOthers( ).end( ) ) + length = libcmis::parseInteger( it->second ); + + libcmis::RenditionPtr rendition( new libcmis::Rendition( + string(), link.getType(), kind, + link.getHref( ), title, length ) ); + + m_renditions.push_back( rendition ); + } + else + m_links.push_back( node ); + } + catch ( const libcmis::Exception& ) + { + // Broken or incomplete link... don't add it + } + } + } + xmlXPathFreeObject( xpathObj ); + + + xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:object" ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[0]; + initializeFromNode( node ); + } + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); +} + +AtomPubSession* AtomObject::getSession( ) +{ + return dynamic_cast< AtomPubSession* >( m_session ); +} + +void AtomObject::writeAtomEntry( xmlTextWriterPtr writer, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType ) +{ + AtomObject tmp( NULL ); + PropertyPtrMap propertiesCopy( properties ); + tmp.m_properties.swap( propertiesCopy ); + + xmlTextWriterStartElement( writer, BAD_CAST( "atom:entry" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:atom" ), BAD_CAST( NS_ATOM_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmisra" ), BAD_CAST( NS_CMISRA_URL ) ); + + if ( !tmp.getCreatedBy( ).empty( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "atom:author" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "atom:name" ), BAD_CAST( tmp.getCreatedBy( ).c_str( ) ) ); + xmlTextWriterEndElement( writer ); + } + + xmlTextWriterWriteElement( writer, BAD_CAST( "atom:title" ), BAD_CAST( tmp.getName( ).c_str( ) ) ); + + boost::posix_time::ptime now( boost::posix_time::second_clock::universal_time( ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "atom:updated" ), BAD_CAST( libcmis::writeDateTime( now ).c_str( ) ) ); + + if ( os.get( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "cmisra:content" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmisra:mediatype" ), BAD_CAST( contentType.c_str() ) ); + xmlTextWriterStartElement(writer, BAD_CAST( "cmisra:base64" ) ); + + libcmis::EncodedData encoder( writer ); + encoder.setEncoding( "base64" ); + istream is( os->rdbuf( ) ); + int bufLength = 1000; + char* buf = new char[ bufLength ]; + do + { + is.read( buf, bufLength ); + int size = is.gcount( ); + encoder.encode( buf, 1, size ); + } while ( !is.eof( ) && !is.fail( ) ); + delete[] buf; + encoder.finish( ); + xmlTextWriterEndElement( writer ); // "cmisra:base64" + + xmlTextWriterEndElement( writer ); + } + + xmlTextWriterStartElement( writer, BAD_CAST( "cmisra:object" ) ); + + tmp.toXml( writer ); + + xmlTextWriterEndElement( writer ); // cmisra:object + + xmlTextWriterEndElement( writer ); // atom:entry +} + +AtomLink* AtomObject::getLink( std::string rel, std::string type ) +{ + AtomLink* link = NULL; + vector< AtomLink >::iterator it = find_if( m_links.begin(), m_links.end(), MatchLink( rel, type ) ); + if ( it != m_links.end() ) + link = &( *it ); + return link; +} + +AtomLink::AtomLink( xmlNodePtr node ): + m_rel( ), m_type( ), m_id( ), m_href( ), m_others( ) +{ + xmlAttrPtr prop = node->properties; + while ( prop != NULL ) + { + xmlChar* xmlStr = xmlGetProp( node, prop->name ); + string value( ( char * ) xmlStr ); + + if ( xmlStrEqual( prop->name, BAD_CAST( "id" ) ) ) + m_id = value; + else if ( xmlStrEqual( prop->name, BAD_CAST( "type" ) ) ) + m_type = value; + else if ( xmlStrEqual( prop->name, BAD_CAST( "rel" ) ) ) + m_rel = value; + else if ( xmlStrEqual( prop->name, BAD_CAST( "href" ) ) ) + m_href = value; + else + m_others[ string( ( char * ) prop->name ) ] = value; + + free( xmlStr ); + prop = prop->next; + } +} diff --git a/src/libcmis/atom-object.hxx b/src/libcmis/atom-object.hxx new file mode 100644 index 0000000..5d54dab --- /dev/null +++ b/src/libcmis/atom-object.hxx @@ -0,0 +1,106 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_OBJECT_HXX_ +#define _ATOM_OBJECT_HXX_ + +#include <map> +#include <ostream> + +#include <libcmis/object.hxx> + +class AtomPubSession; + +class AtomLink +{ + private: + std::string m_rel; + std::string m_type; + std::string m_id; + std::string m_href; + std::map< std::string, std::string > m_others; + + public: + AtomLink( xmlNodePtr node ); + + std::string getRel( ) const { return m_rel; } + std::string getType( ) const { return m_type; } + std::string getId( ) const { return m_id; } + bool hasId( ) const { return !m_id.empty( ); } + std::string getHref( ) const { return m_href; } + std::map< std::string, std::string >& getOthers( ) { return m_others; } +}; + +class AtomObject : public virtual libcmis::Object +{ + private: + + std::vector< AtomLink > m_links; + + public: + AtomObject( AtomPubSession* session ); + AtomObject( const AtomObject& copy ); + ~AtomObject( ); + + AtomObject& operator=( const AtomObject& copy ); + + // Overridden methods from libcmis::Object + virtual libcmis::ObjectPtr updateProperties( + const std::map< std::string, libcmis::PropertyPtr >& properties ); + + virtual libcmis::AllowableActionsPtr getAllowableActions( ); + + /** Reload the data from the server. + */ + virtual void refresh( ) { refreshImpl( NULL ); } + + virtual void remove( bool allVersion = true ); + + virtual void move( boost::shared_ptr< libcmis::Folder > source, boost::shared_ptr< libcmis::Folder > destination ); + + static void writeAtomEntry( xmlTextWriterPtr writer, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType ); + + protected: + + std::string getInfosUrl( ); + virtual void refreshImpl( xmlDocPtr doc ); + virtual void extractInfos( xmlDocPtr doc ); + + AtomPubSession* getSession( ); + + /** Get the atom link corresponding to the given relation and type or NULL + if no link matched those criteria. + + \param rel the relation to match + \param type the type to match or the empty string to match all types. + */ + AtomLink* getLink( std::string rel, std::string type ); +}; + +#endif diff --git a/src/libcmis/atom-session.cxx b/src/libcmis/atom-session.cxx new file mode 100644 index 0000000..7478c5f --- /dev/null +++ b/src/libcmis/atom-session.cxx @@ -0,0 +1,349 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-session.hxx" + +#include <string> + +#include <boost/algorithm/string.hpp> +#include <boost/shared_ptr.hpp> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/xml-utils.hxx> + +#include "atom-document.hxx" +#include "atom-folder.hxx" +#include "atom-object-type.hxx" + +using namespace std; + +AtomPubSession::AtomPubSession( string atomPubUrl, string repositoryId, + string username, string password, bool noSslCheck, + libcmis::OAuth2DataPtr oauth2, bool verbose ) : + BaseSession( atomPubUrl, repositoryId, username, password, noSslCheck, oauth2, verbose ), + m_repository( ) +{ + libcmis::HttpResponsePtr response; + initialize( response ); +} + +AtomPubSession::AtomPubSession( string atomPubUrl, string repositoryId, + const HttpSession& httpSession, libcmis::HttpResponsePtr response ) : + BaseSession( atomPubUrl, repositoryId, httpSession ), + m_repository( ) +{ + initialize( response ); +} + +AtomPubSession::AtomPubSession( ) : + BaseSession( ), + m_repository( ) +{ +} + +AtomPubSession::~AtomPubSession( ) +{ +} + +void AtomPubSession::parseServiceDocument( const string& buf ) +{ + // parse the content + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( buf.c_str(), buf.size(), m_bindingUrl.c_str(), NULL, 0 ), xmlFreeDoc ); + + if ( bool( doc ) ) + { + // Check that we have an AtomPub service document + xmlNodePtr root = xmlDocGetRootElement( doc.get() ); + if ( !xmlStrEqual( root->name, BAD_CAST( "service" ) ) ) + throw libcmis::Exception( "Not an atompub service document" ); + + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc.get() ); + + // Register the Service Document namespaces + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + string workspacesXPath( "//app:workspace" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( workspacesXPath.c_str() ), xpathCtx ); + + if ( xpathObj != NULL ) + { + int nbWorkspaces = 0; + if ( xpathObj->nodesetval ) + nbWorkspaces = xpathObj->nodesetval->nodeNr; + + for ( int i = 0; i < nbWorkspaces; i++ ) + { + try + { + AtomRepositoryPtr ws( new AtomRepository( xpathObj->nodesetval->nodeTab[i] ) ); + + // Check if we have a repository set + if ( m_repositoryId.empty( ) && i == 0 ) + m_repositoryId = ws->getId( ); + + // SharePoint is case insensitive for the id... + if ( boost::to_lower_copy( ws->getId( ) ) == boost::to_lower_copy( m_repositoryId ) ) + m_repository = ws; + + m_repositories.push_back( ws ); + } + catch ( const libcmis::Exception& ) + { + // Invalid repository, don't take care of this + } + } + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + else + throw libcmis::Exception( "Failed to parse service document" ); +} + +void AtomPubSession::initialize( libcmis::HttpResponsePtr response ) +{ + if ( m_repositories.empty() ) + { + // Pull the content from sAtomPubUrl + string buf; + if ( response ) + { + buf = response->getStream( )->str( ); + } + else + { + try + { + buf = httpGetRequest( m_bindingUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + } + + parseServiceDocument( buf ); + } +} + +AtomRepositoryPtr AtomPubSession::getAtomRepository( ) +{ + return m_repository; +} + +libcmis::RepositoryPtr AtomPubSession::getRepository( ) +{ + return getAtomRepository( ); +} + +bool AtomPubSession::setRepository( string repositoryId ) +{ + vector< libcmis::RepositoryPtr > repos = getRepositories( ); + bool found = false; + for ( vector< libcmis::RepositoryPtr >::iterator it = repos.begin(); + it != repos.end() && !found; ++it ) + { + libcmis::RepositoryPtr repo = *it; + if ( repo->getId() == repositoryId ) + { + AtomRepositoryPtr atomRepo = boost::dynamic_pointer_cast< AtomRepository >( repo ); + m_repository = atomRepo; + m_repositoryId = repositoryId; + found = true; + } + } + return found; +} + +libcmis::ObjectPtr AtomPubSession::createObjectFromEntryDoc( xmlDocPtr doc, ResultObjectType res ) +{ + libcmis::ObjectPtr cmisObject; + + if ( NULL != doc ) + { + // Get the atom:entry node + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval && ( 0 < xpathObj->nodesetval->nodeNr ) ) + { + // Get the entry's base type + string baseTypeReq = "//atom:entry[1]//cmis:propertyId[@propertyDefinitionId='cmis:baseTypeId']/cmis:value/text()"; + string baseType = libcmis::getXPathValue( xpathCtx, baseTypeReq ); + + xmlNodePtr node = xpathObj->nodesetval->nodeTab[0]; + if ( res == RESULT_FOLDER || baseType == "cmis:folder" ) + { + cmisObject.reset( new AtomFolder( this, node ) ); + } + else if ( res == RESULT_DOCUMENT || baseType == "cmis:document" ) + { + cmisObject.reset( new AtomDocument( this, node ) ); + } + else + { + // Not a valid CMIS atom entry... weird + } + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + + return cmisObject; +} + +libcmis::ObjectPtr AtomPubSession::getObject( string id ) +{ + string pattern = getAtomRepository()->getUriTemplate( UriTemplate::ObjectById ); + map< string, string > vars; + vars[URI_TEMPLATE_VAR_ID] = id; + vars[string( "includeAllowableActions" )] = string( "true" ); + string url = createUrl( pattern, vars ); + + try + { + string buf = httpGetRequest( url )->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), url.c_str(), NULL, 0 ); + libcmis::ObjectPtr cmisObject = createObjectFromEntryDoc( doc ); + xmlFreeDoc( doc ); + return cmisObject; + } + catch ( const CurlException& e ) + { + if ( ( e.getErrorCode( ) == CURLE_HTTP_RETURNED_ERROR ) && + ( e.getHttpStatus( ) == 404 ) ) + { + string msg = "No such node: "; + msg += id; + throw libcmis::Exception( msg, "objectNotFound" ); + } + else + throw e.getCmisException(); + } +} + +libcmis::ObjectPtr AtomPubSession::getObjectByPath( string path ) +{ + string pattern = getAtomRepository()->getUriTemplate( UriTemplate::ObjectByPath ); + map< string, string > vars; + vars[URI_TEMPLATE_VAR_PATH] = path; + vars[string( "includeAllowableActions" )] = string( "true" ); + string url = createUrl( pattern, vars ); + + try + { + string buf = httpGetRequest( url )->getStream( )->str( ); + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), url.c_str(), NULL, 0 ); + libcmis::ObjectPtr cmisObject = createObjectFromEntryDoc( doc ); + xmlFreeDoc( doc ); + return cmisObject; + } + catch ( const CurlException& e ) + { + if ( ( e.getErrorCode( ) == CURLE_HTTP_RETURNED_ERROR ) && + ( e.getHttpStatus( ) == 404 ) ) + { + string msg = "No node corresponding to path: "; + msg += path; + throw libcmis::Exception( msg, "objectNotFound" ); + } + else + throw e.getCmisException(); + } +} + +libcmis::ObjectTypePtr AtomPubSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new AtomObjectType( this, id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > AtomPubSession::getBaseTypes( ) +{ + string url = getAtomRepository( )->getCollectionUrl( Collection::Types ); + return getChildrenTypes( url ); +} + +vector< libcmis::ObjectTypePtr > AtomPubSession::getChildrenTypes( string url ) +{ + vector< libcmis::ObjectTypePtr > children; + string buf; + try + { + buf = httpGetRequest( url )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), url.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + if ( NULL != xpathCtx ) + { + const string& entriesReq( "//atom:entry" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( entriesReq.c_str() ), xpathCtx ); + + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + libcmis::ObjectTypePtr type( new AtomObjectType( this, node ) ); + children.push_back( type ); + } + } + + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + } + else + { + throw libcmis::Exception( "Failed to parse type children infos" ); + } + xmlFreeDoc( doc ); + + return children; +} diff --git a/src/libcmis/atom-session.hxx b/src/libcmis/atom-session.hxx new file mode 100644 index 0000000..6220591 --- /dev/null +++ b/src/libcmis/atom-session.hxx @@ -0,0 +1,90 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_SESSION_HXX_ +#define _ATOM_SESSION_HXX_ + +#include "base-session.hxx" +#include "atom-workspace.hxx" + +class AtomPubSession : public BaseSession +{ + private: + AtomRepositoryPtr m_repository; + + public: + enum ResultObjectType { RESULT_DYNAMIC, RESULT_FOLDER, RESULT_DOCUMENT }; + AtomPubSession( std::string sAtomPubUrl, std::string repositoryId, + std::string username, std::string password, bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), + bool verbose =false ); + + /** This constructor uses the response of an HTTP request made + before to spare some HTTP request. This constructor has mostly + been designed for the SessionFactory use. + */ + AtomPubSession( std::string sAtomPubUrl, std::string repositoryId, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ); + ~AtomPubSession( ); + + AtomRepositoryPtr getAtomRepository( ); + + // Utility methods + + libcmis::ObjectPtr createObjectFromEntryDoc( xmlDocPtr doc, ResultObjectType res=RESULT_DYNAMIC ); + + std::vector< libcmis::ObjectTypePtr > getChildrenTypes( std::string url ); + + // Override session methods + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string repositoryId ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + protected: + + /** Defaults constructor shouldn't be used + */ + AtomPubSession( ); + AtomPubSession( const AtomPubSession& copy ) = delete; + AtomPubSession& operator=( const AtomPubSession& copy ) = delete; + + void parseServiceDocument( const std::string& buf ); + + void initialize( libcmis::HttpResponsePtr response ); +}; + +#endif diff --git a/src/libcmis/atom-workspace.cxx b/src/libcmis/atom-workspace.cxx new file mode 100644 index 0000000..fb9af8d --- /dev/null +++ b/src/libcmis/atom-workspace.cxx @@ -0,0 +1,228 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Cédric Bosdonnat <cbosdo@users.sourceforge.net> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "atom-workspace.hxx" + +#include <libcmis/xml-utils.hxx> + +using namespace std; + +AtomRepository::AtomRepository( xmlNodePtr wsNode ): + Repository( ), + m_collections( ), + m_uriTemplates( ) +{ + if ( wsNode != NULL ) + { + xmlDocPtr doc = libcmis::wrapInDoc( wsNode ); + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + // Get the collections + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//app:collection" ), xpathCtx ); + if ( NULL != xpathObj ) + readCollections( xpathObj->nodesetval ); + xmlXPathFreeObject( xpathObj ); + + // Get the URI templates + xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:uritemplate" ), xpathCtx ); + if ( NULL != xpathObj ) + readUriTemplates( xpathObj->nodesetval ); + xmlXPathFreeObject( xpathObj ); + + // Get the repository infos + xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmisra:repositoryInfo" ), xpathCtx ); + if ( NULL != xpathObj ) + initializeFromNode( xpathObj->nodesetval->nodeTab[0] ); + xmlXPathFreeObject( xpathObj ); + + } + xmlXPathFreeContext( xpathCtx ); + xmlFreeDoc( doc ); + } +} + +AtomRepository::AtomRepository( const AtomRepository& rCopy ) : + Repository( rCopy ), + m_collections( rCopy.m_collections ), + m_uriTemplates( rCopy.m_uriTemplates ) +{ +} + +AtomRepository::~AtomRepository( ) +{ + m_collections.clear( ); + m_uriTemplates.clear( ); +} + +AtomRepository& AtomRepository::operator= ( const AtomRepository& rCopy ) +{ + if ( this != &rCopy ) + { + m_collections = rCopy.m_collections; + m_uriTemplates = rCopy.m_uriTemplates; + } + + return *this; +} + +string AtomRepository::getCollectionUrl( Collection::Type type ) +{ + return m_collections[ type ]; +} + +string AtomRepository::getUriTemplate( UriTemplate::Type type ) +{ + return m_uriTemplates[ type ]; +} + +void AtomRepository::readCollections( xmlNodeSetPtr nodeSet ) +{ + int size = 0; + if ( nodeSet ) + size = nodeSet->nodeNr; + + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = nodeSet->nodeTab[i]; + + // Look for the href property + xmlChar* href = xmlGetProp( node, BAD_CAST( "href" ) ); + if ( href ) + { + string collectionRef( ( char* )href ); + xmlFree( href ); + + // Look for the cmisra:collectionType child + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + // SharePoint CMIS implementation doesn't follow the spec: + // the cmisra namespace is omitted + bool isCollectionType = xmlStrEqual( child->name, BAD_CAST( "collectionType" ) ); + if ( isCollectionType ) + { + xmlChar* content = xmlNodeGetContent( child ); + Collection::Type type = Collection::Root; + bool typeDefined = false; + + if ( xmlStrEqual( content, BAD_CAST( "root" ) ) ) + { + type = Collection::Root; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "types" ) ) ) + { + type = Collection::Types; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "query" ) ) ) + { + type = Collection::Query; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "checkedout" ) ) ) + { + type = Collection::CheckedOut; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "unfiled" ) ) ) + { + type = Collection::Unfiled; + typeDefined = true; + } + + if ( typeDefined ) + m_collections[ type ] = collectionRef; + + xmlFree( content ); + } + } + } + } +} + +void AtomRepository::readUriTemplates( xmlNodeSetPtr nodeSet ) +{ + int size = 0; + if ( nodeSet ) + size = nodeSet->nodeNr; + + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr node = nodeSet->nodeTab[i]; + + string templateUri; + UriTemplate::Type type = UriTemplate::ObjectById; + bool typeDefined = false; + + // Look for the cmisra:template and cmisra:type children + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + bool isTemplate = xmlStrEqual( child->name, BAD_CAST( "template" ) ); + bool isType = xmlStrEqual( child->name, BAD_CAST( "type" ) ); + + if ( isTemplate ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + templateUri = string( ( char * )content ); + xmlFree( content ); + } + else if ( isType ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( xmlStrEqual( content, BAD_CAST( "objectbyid" ) ) ) + { + type = UriTemplate::ObjectById; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "objectbypath" ) ) ) + { + type = UriTemplate::ObjectByPath; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "query" ) ) ) + { + type = UriTemplate::Query; + typeDefined = true; + } + else if ( xmlStrEqual( content, BAD_CAST( "typebyid" ) ) ) + { + type = UriTemplate::TypeById; + typeDefined = true; + } + xmlFree( content ); + } + } + + if ( !templateUri.empty() && typeDefined ) + m_uriTemplates[ type ] = templateUri; + } +} diff --git a/src/libcmis/atom-workspace.hxx b/src/libcmis/atom-workspace.hxx new file mode 100644 index 0000000..fa4582c --- /dev/null +++ b/src/libcmis/atom-workspace.hxx @@ -0,0 +1,91 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Cédric Bosdonnat <cbosdo@users.sourceforge.net> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ATOM_WORKSPACE_HXX_ +#define _ATOM_WORKSPACE_HXX_ + +#include <map> +#include <string> + +#include <boost/shared_ptr.hpp> +#include <curl/curl.h> +#include <libxml/xpath.h> + +#include <libcmis/exception.hxx> +#include <libcmis/repository.hxx> + +#define URI_TEMPLATE_VAR_ID std::string( "id" ) +#define URI_TEMPLATE_VAR_PATH std::string( "path" ) + +struct Collection { + enum Type + { + Root, + Types, + Query, + CheckedOut, + Unfiled + }; +}; + +struct UriTemplate { + enum Type + { + ObjectById, + ObjectByPath, + TypeById, + Query + }; +}; + +class AtomRepository : public libcmis::Repository +{ + private: + /// Collections URLs + std::map< Collection::Type, std::string > m_collections; + + /// URI templates + std::map< UriTemplate::Type, std::string > m_uriTemplates; + + public: + AtomRepository( xmlNodePtr wsNode = NULL ); + AtomRepository( const AtomRepository& rCopy ); + ~AtomRepository( ); + + AtomRepository& operator= ( const AtomRepository& rCopy ); + + std::string getCollectionUrl( Collection::Type ); + std::string getUriTemplate( UriTemplate::Type ); + + private: + void readCollections( xmlNodeSetPtr pNodeSet ); + void readUriTemplates( xmlNodeSetPtr pNodeSet ); +}; + +typedef boost::shared_ptr< AtomRepository > AtomRepositoryPtr; + +#endif diff --git a/src/libcmis/base-session.cxx b/src/libcmis/base-session.cxx new file mode 100644 index 0000000..a4311ca --- /dev/null +++ b/src/libcmis/base-session.cxx @@ -0,0 +1,146 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "base-session.hxx" + +#include <cctype> +#include <string> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/session-factory.hxx> +#include <libcmis/xml-utils.hxx> + +#include "oauth2-handler.hxx" + +using namespace std; + +BaseSession::BaseSession( string bindingUrl, string repositoryId, string username, + string password, bool noSslCheck, libcmis::OAuth2DataPtr oauth2, bool verbose ) : + Session( ), + HttpSession( username, password, noSslCheck, oauth2, verbose ), + m_bindingUrl( bindingUrl ), + m_repositoryId( repositoryId ), + m_repositories( ) +{ +} + +BaseSession::BaseSession( string sBindingUrl, string repository, + const HttpSession& httpSession ) : + Session( ), + HttpSession( httpSession ), + m_bindingUrl( sBindingUrl ), + m_repositoryId( repository ), + m_repositories( ) +{ +} + +BaseSession::BaseSession( ) : + Session( ), + HttpSession( ), + m_bindingUrl( ), + m_repositoryId( ), + m_repositories( ) +{ +} + +BaseSession::~BaseSession( ) +{ +} + +string BaseSession::createUrl( const string& pattern, map< string, string > variables ) +{ + string url( pattern ); + + // Decompose the pattern and replace the variables by their values + map< string, string >::iterator it = variables.begin( ); + while ( it != variables.end( ) ) + { + string name = "{"; + name += it->first; + name += "}"; + string value = it->second; + + // Search and replace the variable + size_t pos = url.find( name ); + if ( pos != string::npos ) + { + // Escape the URL by chunks + url = url.replace( pos, name.size(), libcmis::escape( value ) ); + } + + ++it; + } + + // Cleanup the remaining unset variables + size_t pos1 = url.find( '{' ); + while ( pos1 != string::npos ) + { + // look for the closing bracket + size_t pos2 = url.find( '}', pos1 ); + if ( pos2 != string::npos ) + url.erase( pos1, pos2 - pos1 + 1 ); + + pos1 = url.find( '{', pos1 - 1 ); + } + + return url; +} + + +void BaseSession::setNoSSLCertificateCheck( bool noCheck ) +{ + HttpSession::setNoSSLCertificateCheck( noCheck ); +} + +void BaseSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); + + oauth2Authenticate( ); +} + +vector< libcmis::RepositoryPtr > BaseSession::getRepositories( ) +{ + return m_repositories; +} + +libcmis::FolderPtr BaseSession::getRootFolder() +{ + return getFolder( getRootId() ); +} + +libcmis::FolderPtr BaseSession::getFolder( string id ) +{ + libcmis::ObjectPtr object = getObject( id ); + libcmis::FolderPtr folder = boost::dynamic_pointer_cast< libcmis::Folder >( object ); + return folder; +} diff --git a/src/libcmis/base-session.hxx b/src/libcmis/base-session.hxx new file mode 100644 index 0000000..60976d4 --- /dev/null +++ b/src/libcmis/base-session.hxx @@ -0,0 +1,104 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _BASE_SESSION_HXX_ +#define _BASE_SESSION_HXX_ + +#include <istream> +#include <sstream> +#include <vector> +#include <map> +#include <string> + +#include <curl/curl.h> +#include <libxml/xmlstring.h> +#include <libxml/xpath.h> + +#include <libcmis/exception.hxx> +#include <libcmis/oauth2-data.hxx> +#include <libcmis/session.hxx> +#include <libcmis/xml-utils.hxx> + +#include "http-session.hxx" + +class OAuth2Handler; + +class BaseSession : public libcmis::Session, + public HttpSession +{ + protected: + std::string m_bindingUrl; + std::string m_repositoryId; + + std::vector< libcmis::RepositoryPtr > m_repositories; + public: + BaseSession( std::string sBindingUrl, std::string repository, + std::string username, std::string password, + bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), bool verbose = false ); + + /** This constructor copies an existing http session. + This has been mostly designed for SessionFactory to save + a few HTTP requests when guessing the binding to use. + */ + BaseSession( std::string sBindingUrl, std::string repository, + const HttpSession& httpSession ); + + ~BaseSession( ); + + std::string& getRepositoryId( ) { return m_repositoryId; } + + // Utility methods + + std::string getRootId( ) { return getRepository()->getRootId( ); } + + std::string createUrl( const std::string& pattern, std::map< std::string, std::string > variables ); + + std::string getBindingUrl( ) { return m_bindingUrl; } + + // HttpSession overridden methods + + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + // Session methods + + virtual void setNoSSLCertificateCheck( bool noCheck ); + + virtual std::vector< libcmis::RepositoryPtr > getRepositories( ); + + virtual libcmis::FolderPtr getRootFolder(); + + virtual libcmis::FolderPtr getFolder( std::string id ); + + protected: + BaseSession( ); + + BaseSession( const BaseSession& copy ) = delete; + BaseSession& operator=( const BaseSession& copy ) = delete; +}; + +#endif diff --git a/src/libcmis/document.cxx b/src/libcmis/document.cxx new file mode 100644 index 0000000..b8a0d0d --- /dev/null +++ b/src/libcmis/document.cxx @@ -0,0 +1,107 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/document.hxx> + +#include <libcmis/folder.hxx> + +using namespace std; +using libcmis::PropertyPtrMap; + +namespace libcmis +{ + vector< string > Document::getPaths( ) + { + vector< string > paths; + try + { + vector< libcmis::FolderPtr > parents = getParents( ); + for ( vector< libcmis::FolderPtr >::iterator it = parents.begin( ); + it != parents.end(); ++it ) + { + string path = ( *it )->getPath( ); + if ( path.empty() ) + continue; + if ( path[path.size() - 1] != '/' ) + path += "/"; + path += getName( ); + paths.push_back( path ); + } + } + catch ( const libcmis::Exception& ) + { + // We may not have the permission to get the parents + } + return paths; + } + + string Document::getContentType( ) + { + return getStringProperty( "cmis:contentStreamMimeType" ); + } + + string Document::getContentFilename( ) + { + return getStringProperty( "cmis:contentStreamFileName" ); + } + + long Document::getContentLength( ) + { + long contentLength = 0; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:contentStreamLength" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getLongs( ).empty( ) ) + contentLength = it->second->getLongs( ).front( ); + return contentLength; + } + + // LCOV_EXCL_START + string Document::toString( ) + { + stringstream buf; + + buf << "Document Object:" << endl << endl; + buf << Object::toString(); + try + { + vector< libcmis::FolderPtr > parents = getParents( ); + buf << "Parents ids: "; + for ( vector< libcmis::FolderPtr >::iterator it = parents.begin(); it != parents.end(); ++it ) + buf << "'" << ( *it )->getId( ) << "' "; + buf << endl; + } + catch ( const libcmis::Exception& ) + { + } + buf << "Content Type: " << getContentType( ) << endl; + buf << "Content Length: " << getContentLength( ) << endl; + buf << "Content Filename: " << getContentFilename( ) << endl; + + return buf.str(); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/dummy.cxx b/src/libcmis/dummy.cxx new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/libcmis/dummy.cxx diff --git a/src/libcmis/folder.cxx b/src/libcmis/folder.cxx new file mode 100644 index 0000000..d4a5acd --- /dev/null +++ b/src/libcmis/folder.cxx @@ -0,0 +1,92 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/folder.hxx> + +#include <libcmis/session.hxx> + +using namespace std; + +namespace libcmis +{ + vector< string > Folder::getPaths( ) + { + vector< string > paths; + paths.push_back( getPath( ) ); + return paths; + } + + libcmis::FolderPtr Folder::getFolderParent( ) + { + if ( getAllowableActions( ).get() && !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetFolderParent ) ) + throw libcmis::Exception( string( "GetFolderParent not allowed on node " ) + getId() ); + + if ( m_session == NULL ) + throw libcmis::Exception( string( "Session not defined on the object... weird!" ) ); + + return m_session->getFolder( getParentId( ) ); + } + + string Folder::getParentId( ) + { + return getStringProperty( "cmis:parentId" ); + } + + string Folder::getPath( ) + { + return getStringProperty( "cmis:path" ); + } + + bool Folder::isRootFolder( ) + { + return getParentId( ).empty( ); + } + + // LCOV_EXCL_START + string Folder::toString( ) + { + stringstream buf; + + buf << "Folder Object:" << endl << endl; + buf << Object::toString(); + buf << "Path: " << getPath() << endl; + buf << "Folder Parent Id: " << getParentId( ) << endl; + buf << "Children [Name (Id)]:" << endl; + + vector< libcmis::ObjectPtr > children = getChildren( ); + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end(); ++it ) + { + libcmis::ObjectPtr child = *it; + buf << " " << child->getName() << " (" << child->getId() << ")" << endl; + } + + return buf.str(); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/gdrive-allowable-actions.hxx b/src/libcmis/gdrive-allowable-actions.hxx new file mode 100644 index 0000000..1a22d5d --- /dev/null +++ b/src/libcmis/gdrive-allowable-actions.hxx @@ -0,0 +1,102 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_ALLOWABLE_ACTIONS_HXX_ +#define _GDRIVE_ALLOWABLE_ACTIONS_HXX_ + +#include <libcmis/allowable-actions.hxx> + +class GdriveAllowableActions: public libcmis::AllowableActions +{ + public: + GdriveAllowableActions( bool isFolder ) : AllowableActions( ) + { + m_states.clear( ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::UpdateProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectRelationships, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectParents, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::MoveObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateRelationship, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyPolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAppliedPolicies, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemovePolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetACL, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyACL, true ) ); + + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderTree, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderParent, isFolder) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetDescendants, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CancelCheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckIn, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::SetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAllVersions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::AddObjectToFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemoveObjectFromFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetRenditions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetChildren, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateDocument, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateFolder, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteTree, isFolder ) ); + } +}; + +#endif diff --git a/src/libcmis/gdrive-document.cxx b/src/libcmis/gdrive-document.cxx new file mode 100644 index 0000000..ecb13d6 --- /dev/null +++ b/src/libcmis/gdrive-document.cxx @@ -0,0 +1,250 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-document.hxx" + +#include <libcmis/rendition.hxx> + +#include "gdrive-folder.hxx" +#include "gdrive-session.hxx" +#include "json-utils.hxx" +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveDocument::GDriveDocument( GDriveSession* session ) : + libcmis::Object( session), + libcmis::Document( session ), + GDriveObject( session ), + m_isGoogleDoc( false ) +{ +} + +GDriveDocument::GDriveDocument( GDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session), + libcmis::Document( session ), + GDriveObject( session, json, id, name ), + m_isGoogleDoc( false ) +{ + m_isGoogleDoc = getContentType( ).find( "google" ) != string::npos; + getRenditions( ); +} + +GDriveDocument::~GDriveDocument( ) +{ +} + +string GDriveDocument::getDownloadUrl( string streamId ) +{ + string streamUrl; + vector< RenditionPtr > renditions = getRenditions( ); + + if ( renditions.empty( ) ) + return streamUrl; + + if ( !streamId.empty( ) ) + { + // Find the rendition associated with the streamId + for ( vector< RenditionPtr >::iterator it = renditions.begin( ) ; + it != renditions.end(); ++it ) + { + if ( (*it)->getStreamId( ) == streamId ) + { + streamUrl = (*it)->getUrl( ); + break; + } + } + } + else + { + // Automatically find the rendition + + // Prefer ODF format + for ( vector< RenditionPtr >::iterator it = renditions.begin( ) ; + it != renditions.end(); ++it ) + if ( (*it)->getMimeType( ).find( "opendocument") != string::npos ) + return (*it)->getUrl( ); + + // Then MS format + for ( vector< RenditionPtr >::iterator it = renditions.begin( ) ; + it != renditions.end(); ++it ) + if ( (*it)->getMimeType( ).find( "officedocument") != string::npos ) + return (*it)->getUrl( ); + + // If not found, take the first one + streamUrl = renditions.front( )->getUrl( ); + + } + + return streamUrl; +} + +vector< libcmis::FolderPtr > GDriveDocument::getParents( ) +{ + vector< libcmis::FolderPtr > parents; + + vector< string > parentsId = getMultiStringProperty( "cmis:parentId" ); + + // Create folder objects from parent IDs + for ( vector< string >::iterator it = parentsId.begin( ); it != parentsId.end( ); ++it) + { + string parentId = ( *it ); + libcmis::ObjectPtr obj = getSession( )->getObject( parentId ); + libcmis::FolderPtr parent = boost::dynamic_pointer_cast< libcmis::Folder >( obj ); + parents.push_back( parent ); + } + return parents; +} + +boost::shared_ptr< istream > GDriveDocument::getContentStream( string streamId ) +{ + boost::shared_ptr< istream > stream; + string streamUrl = getDownloadUrl( streamId ); + if ( streamUrl.empty( ) ) + throw libcmis::Exception( "can not found stream url" ); + + try + { + stream = getSession( )->httpGetRequest( streamUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return stream; +} + +void GDriveDocument::uploadStream( boost::shared_ptr< ostream > os, + string contentType ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + string putUrl = GDRIVE_UPLOAD_LINK + getId( ) + "?uploadType=media"; + + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector <string> headers; + headers.push_back( string( "Content-Type: " ) + contentType ); + string res; + try + { + res = getSession()->httpPatchRequest( putUrl, *is, headers )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + throw libcmis::Exception( "Document content wasn't set for" + "some reason" ); + refresh( ); +} + +void GDriveDocument::setContentStream( boost::shared_ptr< ostream > os, + string contentType, + string fileName, + bool /*overwrite*/ ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + // TODO: when would the filename need an update? + if (!fileName.empty() && fileName != getContentFilename()) + std::cout << "filename change is not implemented in setContentStream" << std::endl; + + // Upload stream + uploadStream( os, contentType ); +} + +libcmis::DocumentPtr GDriveDocument::checkOut( ) +{ + // GDrive doesn't have CheckOut, so just return the same document here + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkout = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkout; +} + +void GDriveDocument::cancelCheckout( ) +{ + // Don't do anything since we don't have CheckOut +} + +libcmis::DocumentPtr GDriveDocument::checkIn( + bool /*isMajor*/, + std::string /*comment*/, + const PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ) +{ + // GDrive doesn't have CheckIn, so just upload the properties, + // the content stream and fetch the new document resource. + updateProperties( properties ); + setContentStream( stream, contentType, fileName ); + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkin = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkin; +} + + +vector< libcmis::DocumentPtr > GDriveDocument::getAllVersions( ) +{ + vector< libcmis::DocumentPtr > revisions; + string versionUrl = GDRIVE_METADATA_LINK + getId( ) + "/revisions"; + // Run the http request to get the properties definition + string res; + try + { + res = getSession()->httpGetRequest( versionUrl )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["revisions"].getList( ); + + string parentId = getStringProperty( "cmis:parentId" ); + + // Create document objects from Json objects + for(unsigned int i = 0; i < objs.size(); i++) + { + objs[i].add( "parents", GdriveUtils::createJsonFromParentId( parentId ) ); + libcmis::DocumentPtr revision( + new GDriveDocument( getSession(), objs[i], getId( ), getName( ) ) ); + + revisions.push_back( revision ); + } + return revisions; +} + diff --git a/src/libcmis/gdrive-document.hxx b/src/libcmis/gdrive-document.hxx new file mode 100644 index 0000000..aecf270 --- /dev/null +++ b/src/libcmis/gdrive-document.hxx @@ -0,0 +1,90 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_DOCUMENT_HXX_ +#define _GDRIVE_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/rendition.hxx> + +#include "gdrive-object.hxx" +#include "json-utils.hxx" + +class GDriveDocument : public libcmis::Document, public GDriveObject +{ + public: + GDriveDocument( GDriveSession* session ); + + // Create a GDrive document from Json properties. + // In case it's a revision, keep the ID and the name of the original file. + GDriveDocument( GDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + ~GDriveDocument( ); + + std::string getType( ) { return std::string( "cmis:document" );} + std::string getBaseType( ) { return std::string( "cmis:document" );} + + bool isGoogleDoc( ) { return m_isGoogleDoc; } + + /* Get the download Url associated to streamId, + automatically find ODF then MS format if no streamId is specified. + */ + std::string getDownloadUrl( std::string streamId = std::string( ) ); + + void uploadStream( boost::shared_ptr< std::ostream > os, + std::string contentType ); + + virtual std::vector< libcmis::FolderPtr > getParents( ); + virtual boost::shared_ptr< std::istream > getContentStream( + std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName, + bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + virtual libcmis::DocumentPtr checkIn( + bool isMajor, + std::string comment, + const std::map< std::string,libcmis::PropertyPtr >& + properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); + + private: + bool m_isGoogleDoc; +}; + +#endif diff --git a/src/libcmis/gdrive-folder.cxx b/src/libcmis/gdrive-folder.cxx new file mode 100644 index 0000000..26de89b --- /dev/null +++ b/src/libcmis/gdrive-folder.cxx @@ -0,0 +1,192 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-folder.hxx" + +#include "gdrive-session.hxx" +#include "gdrive-document.hxx" +#include "gdrive-property.hxx" +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveFolder::GDriveFolder( GDriveSession* session ): + libcmis::Object( session ), + libcmis::Folder( session ), + GDriveObject( session ) +{ +} + +GDriveFolder::GDriveFolder( GDriveSession* session, Json json ): + libcmis::Object( session ), + libcmis::Folder( session ), + GDriveObject( session, json ) +{ +} + +GDriveFolder::~GDriveFolder( ) +{ +} + +vector< libcmis::ObjectPtr > GDriveFolder::getChildren( ) +{ + vector< libcmis::ObjectPtr > children; + + // GDrive doesn't support fetch all the children in one query. + // Instead of sending multiple queries for children, + // we send a single query to search for objects where parents + // include the folderID. + string query = GDRIVE_METADATA_LINK + "?q=\"" + getId( ) + "\"+in+parents+and+trashed+=+false" + + "&fields=files(kind,id,name,parents,mimeType,createdTime,modifiedTime,thumbnailLink,size)"; + + string res; + try + { + res = getSession( )->httpGetRequest( query )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["files"].getList( ); + + // Create children objects from Json objects + for(unsigned int i = 0; i < objs.size(); i++) + { + ObjectPtr child; + if ( objs[i]["mimeType"].toString( ) == GDRIVE_FOLDER_MIME_TYPE ) + child.reset( new GDriveFolder( getSession( ), objs[i] ) ); + else + child.reset( new GDriveDocument( getSession( ), objs[i] ) ); + children.push_back( child ); + } + + return children; +} + +string GDriveFolder::uploadProperties( Json properties ) +{ + // URL for uploading meta data + string metaUrl = GDRIVE_METADATA_LINK + "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime"; + + // add parents to the properties + properties.add( "parents", GdriveUtils::createJsonFromParentId( getId( ) ) ); + + //upload metadata + std::istringstream is( properties.toString( ) ); + string response; + try + { + response = getSession()->httpPostRequest( metaUrl, is, "application/json" ) + ->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + return response; +} + +libcmis::FolderPtr GDriveFolder::createFolder( + const PropertyPtrMap& properties ) +{ + Json propsJson = GdriveUtils::toGdriveJson( properties ); + + // GDrive folder is a file with a different mime type. + string mimeType = GDRIVE_FOLDER_MIME_TYPE; + + // Add mimetype to the propsJson + Json jsonMimeType( mimeType.c_str( ) ); + propsJson.add( "mimeType", jsonMimeType ); + + // Upload meta-datas + string response = uploadProperties( propsJson ); + + Json jsonRes = Json::parse( response ); + libcmis::FolderPtr folderPtr( new GDriveFolder( getSession( ), jsonRes ) ); + + return folderPtr; +} + +libcmis::DocumentPtr GDriveFolder::createDocument( + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, + string contentType, string fileName ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + Json propsJson = GdriveUtils::toGdriveJson( properties ); + + if(!fileName.empty()) { + // use provided filename + Json jsonFilename( fileName.c_str( ) ); + + propsJson.add( "name", jsonFilename ); + } + if(!contentType.empty()) { + propsJson.add( "mimeType", Json(contentType.c_str())); + } + + // Upload meta-datas + string res = uploadProperties( propsJson); + + // parse the document + Json jsonRes = Json::parse( res ); + + boost::shared_ptr< GDriveDocument > + gDocument( new GDriveDocument( getSession( ), jsonRes ) ); + + // Upload stream + gDocument->uploadStream( os, contentType); + + return gDocument; +} + +vector< string > GDriveFolder::removeTree( + bool /*allVersions*/, + libcmis::UnfileObjects::Type /*unfile*/, + bool /*continueOnError*/ ) +{ + try + { + getSession( )->httpDeleteRequest( GDRIVE_METADATA_LINK + getId( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // Nothing to return here + return vector< string >( ); +} + diff --git a/src/libcmis/gdrive-folder.hxx b/src/libcmis/gdrive-folder.hxx new file mode 100644 index 0000000..aff4737 --- /dev/null +++ b/src/libcmis/gdrive-folder.hxx @@ -0,0 +1,66 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_FOLDER_HXX_ +#define _GDRIVE_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "gdrive-object.hxx" +#include "json-utils.hxx" + +class GDriveFolder : public libcmis::Folder, public GDriveObject +{ + public: + GDriveFolder( GDriveSession* session ); + GDriveFolder( GDriveSession* session, Json json ); + ~GDriveFolder( ); + + std::string getType( ) { return std::string( "cmis:folder" );} + std::string getBaseType( ) { return std::string( "cmis:folder" );} + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( + const libcmis::PropertyPtrMap& properties ); + + virtual libcmis::DocumentPtr createDocument( + const libcmis::PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName ); + + virtual std::vector< std::string > removeTree( + bool allVersion = true, + libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); + + std::string uploadProperties( Json properties ); +}; + +#endif diff --git a/src/libcmis/gdrive-object-type.cxx b/src/libcmis/gdrive-object-type.cxx new file mode 100644 index 0000000..75df2a2 --- /dev/null +++ b/src/libcmis/gdrive-object-type.cxx @@ -0,0 +1,141 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-object-type.hxx" + +GdriveObjectType::GdriveObjectType( const std::string& id ): ObjectType( ) +{ + m_id = id; + m_localName = "GoogleDrive Object Type"; + m_localNamespace = "GoogleDrive Object Type"; + m_displayName = "GoogleDrive Object Type"; + m_queryName = "GoogleDrive Object Type"; + m_description = "GoogleDrive Object Type"; + m_parentTypeId = id; + m_baseTypeId = id; + m_creatable = true; + m_versionable = true; + m_fulltextIndexed = true; + + libcmis::PropertyTypePtr idType(new libcmis::PropertyType( ) ); + idType->setId( "cmis:objectTypeId" ); + idType->setType( libcmis::PropertyType::String ); + m_propertiesTypes[ idType->getId( ) ] = idType; + + // create PropertyTypes which are updatable. + + // title + libcmis::PropertyTypePtr nameType( new libcmis::PropertyType( ) ); + nameType->setId( "cmis:name" ); + nameType->setType( libcmis::PropertyType::String ); + nameType->setUpdatable( true ); + m_propertiesTypes[ nameType->getId( ) ] = nameType; + + // mimeType + libcmis::PropertyTypePtr mimeType( new libcmis::PropertyType( ) ); + mimeType->setId( "cmis:contentStreamMimeType" ); + mimeType->setType( libcmis::PropertyType::String ); + mimeType->setUpdatable( false ); + m_propertiesTypes[ mimeType->getId( ) ] = mimeType; + + // parents + libcmis::PropertyTypePtr parentsType( new libcmis::PropertyType( ) ); + parentsType->setId( "cmis:parentId" ); + parentsType->setType( libcmis::PropertyType::String ); + parentsType->setUpdatable( false ); + parentsType->setMultiValued( true ); + m_propertiesTypes[ parentsType->getId( ) ] = parentsType; + + // labels + libcmis::PropertyTypePtr labelsType( new libcmis::PropertyType( ) ); + labelsType->setId( "labels" ); + labelsType->setType( libcmis::PropertyType::String ); + labelsType->setUpdatable( false ); + labelsType->setMultiValued( true ); + m_propertiesTypes[ labelsType->getId( ) ] = labelsType; + + // ownerNames + libcmis::PropertyTypePtr ownerNamesType( new libcmis::PropertyType( ) ); + ownerNamesType->setId( "ownerNames" ); + ownerNamesType->setType( libcmis::PropertyType::String ); + ownerNamesType->setUpdatable( false ); + ownerNamesType->setMultiValued( true ); + m_propertiesTypes[ ownerNamesType->getId( ) ] = ownerNamesType; + + // owners + libcmis::PropertyTypePtr ownersType( new libcmis::PropertyType( ) ); + ownersType->setId( "owners" ); + ownersType->setType( libcmis::PropertyType::String ); + ownersType->setUpdatable( false ); + ownersType->setMultiValued( true ); + m_propertiesTypes[ ownersType->getId( ) ] = ownersType; + + // export links + libcmis::PropertyTypePtr exportLinksType( new libcmis::PropertyType( ) ); + exportLinksType->setId( "exportLinks" ); + exportLinksType->setType( libcmis::PropertyType::String ); + exportLinksType->setUpdatable( false ); + exportLinksType->setMultiValued( true ); + m_propertiesTypes[ exportLinksType->getId( ) ] = exportLinksType; + + // description + libcmis::PropertyTypePtr descriptionType( new libcmis::PropertyType( ) ); + descriptionType->setId( "cmis:description" ); + descriptionType->setType( libcmis::PropertyType::String ); + descriptionType->setUpdatable( true ); + m_propertiesTypes[ descriptionType->getId( ) ] = descriptionType; + + // modifiedDate + libcmis::PropertyTypePtr modifiedDateType( new libcmis::PropertyType( ) ); + modifiedDateType->setId( "cmis:lastModificationDate" ); + modifiedDateType->setType( libcmis::PropertyType::DateTime ); + modifiedDateType->setUpdatable( true ); + m_propertiesTypes[ modifiedDateType->getId( ) ] = modifiedDateType; + + // lastViewedByMeDate + libcmis::PropertyTypePtr lastViewedByMeDateType( new libcmis::PropertyType( ) ); + lastViewedByMeDateType->setId( "lastViewedByMeDate" ); + lastViewedByMeDateType->setType( libcmis::PropertyType::DateTime ); + lastViewedByMeDateType->setUpdatable( true ); + m_propertiesTypes[ lastViewedByMeDateType->getId( ) ] = lastViewedByMeDateType; + +} + + +libcmis::ObjectTypePtr GdriveObjectType::getParentType( ) +{ + libcmis::ObjectTypePtr parentTypePtr( new GdriveObjectType( m_parentTypeId ) ); + return parentTypePtr; +} + +libcmis::ObjectTypePtr GdriveObjectType::getBaseType( ) +{ + libcmis::ObjectTypePtr baseTypePtr( new GdriveObjectType( m_baseTypeId ) ); + return baseTypePtr; +} + diff --git a/src/libcmis/gdrive-object-type.hxx b/src/libcmis/gdrive-object-type.hxx new file mode 100644 index 0000000..3f1a258 --- /dev/null +++ b/src/libcmis/gdrive-object-type.hxx @@ -0,0 +1,46 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_OBJECT_TYPE_HXX_ +#define _GDRIVE_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "json-utils.hxx" + +class GdriveObjectType: public libcmis::ObjectType +{ + public: + GdriveObjectType( const std::string& id ); + + virtual libcmis::ObjectTypePtr getParentType( ); + + virtual libcmis::ObjectTypePtr getBaseType( ); +}; + +#endif diff --git a/src/libcmis/gdrive-object.cxx b/src/libcmis/gdrive-object.cxx new file mode 100644 index 0000000..b472e2f --- /dev/null +++ b/src/libcmis/gdrive-object.cxx @@ -0,0 +1,276 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 SUSE <cbosdonnat@suse.com> + * 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-object.hxx" + +#include "gdrive-property.hxx" +#include "gdrive-allowable-actions.hxx" +#include "gdrive-repository.hxx" +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveObject::GDriveObject( GDriveSession* session ) : + libcmis::Object( session ) +{ +} + +GDriveObject::GDriveObject( GDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session ) +{ + initializeFromJson( json, id, name ); +} + +GDriveObject::GDriveObject( const GDriveObject& copy ) : + libcmis::Object( copy ) +{ +} + +GDriveObject& GDriveObject::operator=( const GDriveObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + return *this; +} + +void GDriveObject::initializeFromJson ( Json json, string id, string name ) +{ + Json::JsonObject objs = json.getObjects( ); + Json::JsonObject::iterator it; + for ( it = objs.begin( ); it != objs.end( ); ++it) + { + PropertyPtr property; + + // in case of a revision, get the ID and name of the original file + if ( !id.empty( ) && it->first == "id" ) + { + Json idJson( id.c_str( ) ); + property.reset( new GDriveProperty( "id", idJson ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + property.reset( new GDriveProperty( "revisionId", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + + Json nameJson( name.c_str( ) ); + property.reset( new GDriveProperty( "cmis:name", nameJson) ); + m_properties[ property->getPropertyType( )->getId()] = property; + property.reset( new GDriveProperty( "cmis:contentStreamFileName", nameJson) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + else + { + property.reset( new GDriveProperty( it->first, it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + + // we map "name" to both "cmis:name" and "cmis:getContentStreamFileName" + if ( it->first == "name" ) + { + property.reset( new GDriveProperty( "cmis:name", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + + // some revision keep the original file name. + if ( it->first == "originalFilename" ) + { + property.reset( new GDriveProperty( "cmis:name", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + property.reset( new GDriveProperty( "cmis:contentStreamFileName", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + + // In case of a revision, get the modified date as creation date + if ( it->first == "modifiedDate" && !id.empty( ) ) + { + property.reset( new GDriveProperty( "cmis:creationDate", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + // In case of a revision, get the last modifying user and the creator + if ( it->first == "lastModifyingUserName" && !id.empty( ) ) + { + property.reset( new GDriveProperty( "cmis:createdBy", it->second) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + } + } + m_refreshTimestamp = time( NULL ); + + // Create AllowableActions + bool isFolder = json["mimeType"].toString( ) == GDRIVE_FOLDER_MIME_TYPE; + m_allowableActions.reset( new GdriveAllowableActions( isFolder ) ); +} + +GDriveSession* GDriveObject::getSession( ) +{ + return dynamic_cast< GDriveSession* > ( m_session ); +} + +void GDriveObject::refreshImpl( Json json ) +{ + m_typeDescription.reset( ); + m_properties.clear( ); + initializeFromJson( json ); +} + +vector< RenditionPtr> GDriveObject::getRenditions( string /* filter */ ) +{ + if ( m_renditions.empty( ) ) + { + string downloadUrl = GDRIVE_METADATA_LINK + getId( ) + "?alt=media"; + string mimeType = getStringProperty( "cmis:contentStreamMimeType" ); + if ( !mimeType.empty( ) ) + { + RenditionPtr rendition( + new Rendition( mimeType, mimeType, mimeType, downloadUrl )); + m_renditions.push_back( rendition ); + } + + vector< string > exportLinks = getMultiStringProperty( "exportLinks" ); + for ( vector<string>::iterator it = exportLinks.begin( ); it != exportLinks.end( ); ++it) + { + int pos = (*it).find(":\""); + if ( pos == -1 ) continue; + mimeType = (*it).substr( 0, pos ); + string url = (*it).substr( pos + 2, (*it).length( ) - pos - 3 ); + RenditionPtr rendition( + new Rendition( mimeType, mimeType, mimeType, url ) ); + m_renditions.push_back( rendition ); + } + + // thumbnail link + string thumbnailLink = getStringProperty( "thumbnailLink" ); + if ( !thumbnailLink.empty( ) ) + { + mimeType = "cmis:thumbnail"; + RenditionPtr rendition( + new Rendition( mimeType, mimeType, mimeType, thumbnailLink )); + m_renditions.push_back( rendition ); + } + } + return m_renditions; +} + +libcmis::ObjectPtr GDriveObject::updateProperties( + const PropertyPtrMap& properties ) +{ + // Make Json object from properties + Json json = GdriveUtils::toGdriveJson( properties ); + + istringstream is( json.toString( )); + + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/json" ); + response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + libcmis::ObjectPtr updated( new GDriveObject ( getSession( ), jsonRes ) ); + + if ( updated->getId( ) == getId( ) ) + refreshImpl( jsonRes ); + + return updated; +} + +void GDriveObject::refresh( ) +{ + string res; + try + { + res = getSession()->httpGetRequest( getUrl( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json json = Json::parse( res ); + refreshImpl( json ); +} + +void GDriveObject::remove( bool /*allVersions*/ ) +{ + try + { + getSession( )->httpDeleteRequest( GDRIVE_METADATA_LINK + getId( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +void GDriveObject::move( FolderPtr /*source*/, FolderPtr destination ) +{ + Json parentsJson; + parentsJson.add( "addParents", Json(destination->getId( ).c_str()) ); + parentsJson.add( "removeParents", Json(getStringProperty( "cmis:parentId" ).c_str()) ); + + istringstream is( parentsJson.toString( ) ); + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/json" ); + response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + + refreshImpl( jsonRes ); +} + +string GDriveObject::getUrl( ) +{ + // thumbnailLink causes some operations to fail with internal server error, + // see https://issuetracker.google.com/issues/36760667 + return GDRIVE_METADATA_LINK + getId( ) + + "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime,size"; +} + +vector< string> GDriveObject::getMultiStringProperty( const string& propertyName ) +{ + vector< string > values; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + values = it->second->getStrings( ); + return values; +} + diff --git a/src/libcmis/gdrive-object.hxx b/src/libcmis/gdrive-object.hxx new file mode 100644 index 0000000..4a2225f --- /dev/null +++ b/src/libcmis/gdrive-object.hxx @@ -0,0 +1,87 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _GDRIVE_OBJECT_HXX_ +#define _GDRIVE_OBJECT_HXX_ + +#include <libcmis/object.hxx> + +#include "gdrive-session.hxx" +#include "json-utils.hxx" + +/** Class representing an object for Google Drive protocol. + + This class overrides quite a number of its parent class methods to + git the Google Drive API into the libcmis API. + + In order to reuse more of the base Object class, this class needs + to map the main properties (like id, name, etc) to CMIS property + ids (cmis:id, cmis:name, etc). + */ +class GDriveObject : public virtual libcmis::Object +{ + public: + GDriveObject( GDriveSession* session ); + + // Create a GDrive document from Json properties. + // In case it's a revision, keep the ID and the name of the original file. + GDriveObject( GDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + GDriveObject( const GDriveObject& copy ); + virtual ~GDriveObject( ) { } + + GDriveObject& operator=( const GDriveObject& copy ); + + void initializeFromJson( Json json, std::string id = std::string( ), + std::string name = std::string( ) ); + void refreshImpl( Json json ); + Json createJsonFromParentId( const std::string& parentId ); + + std::string getUrl( ); + std::string getUploadUrl( ); + std::vector< std::string > getMultiStringProperty( + const std::string& propertyName ); + + virtual std::vector< libcmis::RenditionPtr> getRenditions( std::string filter = std::string( ) ); + + virtual boost::shared_ptr< Object > updateProperties( + const libcmis::PropertyPtrMap& properties ); + + virtual void refresh( ); + + virtual void remove( bool allVersions = true ); + + virtual void move( boost::shared_ptr< libcmis::Folder > source, + boost::shared_ptr< libcmis::Folder > destination ); + + protected: + GDriveSession* getSession( ); + +}; + +#endif diff --git a/src/libcmis/gdrive-property.cxx b/src/libcmis/gdrive-property.cxx new file mode 100644 index 0000000..f75c2ed --- /dev/null +++ b/src/libcmis/gdrive-property.cxx @@ -0,0 +1,79 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-property.hxx" + +#include <libcmis/property-type.hxx> + +#include "gdrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +GDriveProperty::GDriveProperty( ) +{ +} + +GDriveProperty::~GDriveProperty( ) +{ +} + +GDriveProperty::GDriveProperty( const string& key, Json json ): + Property( ) +{ + PropertyTypePtr propertyType( new PropertyType( ) ); + string convertedKey = GdriveUtils::toCmisKey( key ); + propertyType->setId( convertedKey ); + propertyType->setLocalName( convertedKey ); + propertyType->setLocalNamespace( convertedKey ); + propertyType->setQueryName( convertedKey ); + propertyType->setDisplayName( key ); + propertyType->setTypeFromJsonType( json.getStrType( ) ); + propertyType->setUpdatable( GdriveUtils::checkUpdatable( key ) ); + propertyType->setMultiValued( GdriveUtils::checkMultiValued( key ) ); + + setPropertyType( propertyType ); + + vector< string > values = GdriveUtils::parseGdriveProperty( key, json ); + setValues( values ); +} + +GDriveProperty::GDriveProperty( const GDriveProperty& copy ) : + libcmis::Property( copy ) +{ +} + +GDriveProperty& GDriveProperty::operator=( const GDriveProperty& copy ) +{ + if ( this != © ) + { + libcmis::Property::operator=( copy ); + } + return *this; +} + diff --git a/src/libcmis/gdrive-property.hxx b/src/libcmis/gdrive-property.hxx new file mode 100644 index 0000000..4edab78 --- /dev/null +++ b/src/libcmis/gdrive-property.hxx @@ -0,0 +1,51 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_PROPERTY_HXX_ +#define _GDRIVE_PROPERTY_HXX_ + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +class GDriveProperty : public libcmis::Property +{ + public : + // Create a GDrive Property from a Json property with its key + GDriveProperty( const std::string& key, Json json); + ~GDriveProperty( ); + GDriveProperty( const GDriveProperty& copy); + GDriveProperty& operator=( const GDriveProperty& copy ); + + // Check if the property is updatable + bool checkUpdatable( const std::string& key ); + private : + // Avoid calling default constructor + GDriveProperty( ); +}; +#endif /* _GDRIVE_PROPERTY_HXX_ */ diff --git a/src/libcmis/gdrive-repository.cxx b/src/libcmis/gdrive-repository.cxx new file mode 100644 index 0000000..24b42b5 --- /dev/null +++ b/src/libcmis/gdrive-repository.cxx @@ -0,0 +1,55 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-repository.hxx" + +GdriveRepository::GdriveRepository( ) : + Repository( ) +{ + m_id = "GoogleDrive"; + m_name = "Google Drive"; + m_description = "Google Drive repository"; + m_productName = "Google Drive"; + m_productVersion = "v3"; + m_rootId = "root"; + + m_capabilities[ ACL ] = "discover"; + m_capabilities[ AllVersionsSearchable ] = "true"; + m_capabilities[ Changes ] = "all"; + m_capabilities[ GetDescendants ] = "true"; + m_capabilities[ GetFolderTree ] = "true"; + m_capabilities[ OrderBy ] = "custom"; + m_capabilities[ Multifiling ] = "true"; + m_capabilities[ PWCSearchable ] = "true"; + m_capabilities[ PWCUpdatable ] = "true"; + m_capabilities[ Query ] = "bothcombined"; + m_capabilities[ Renditions ] = "read"; + m_capabilities[ Unfiling ] = "false"; + m_capabilities[ VersionSpecificFiling ] = "false"; + m_capabilities[ Join ] = "none"; +} diff --git a/src/libcmis/gdrive-repository.hxx b/src/libcmis/gdrive-repository.hxx new file mode 100644 index 0000000..215a13c --- /dev/null +++ b/src/libcmis/gdrive-repository.hxx @@ -0,0 +1,41 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _GDRIVE_REPOSITORY_HXX_ +#define _GDRIVE_REPOSITORY_HXX_ + +#include <libcmis/repository.hxx> + +class GdriveRepository: public libcmis::Repository +{ + public: + GdriveRepository( ); +}; + +#endif + diff --git a/src/libcmis/gdrive-session.cxx b/src/libcmis/gdrive-session.cxx new file mode 100644 index 0000000..1ee748e --- /dev/null +++ b/src/libcmis/gdrive-session.cxx @@ -0,0 +1,254 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-session.hxx" + +#include <libcmis/object-type.hxx> + +#include "oauth2-handler.hxx" +#include "gdrive-document.hxx" +#include "gdrive-folder.hxx" +#include "gdrive-repository.hxx" +#include "gdrive-object-type.hxx" +#include "gdrive-utils.hxx" + +using namespace std; + +GDriveSession::GDriveSession ( string baseUrl, + string username, + string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose ) : + BaseSession( baseUrl, string(), username, password, false, + libcmis::OAuth2DataPtr(), verbose ) + +{ + // Add the dummy repository, even if we don't have OAuth2 + m_repositories.push_back( getRepository( ) ); + + if ( oauth2 && oauth2->isComplete( ) ){ + setOAuth2Data( oauth2 ); + } +} + +GDriveSession::GDriveSession() : + BaseSession() +{ +} + +GDriveSession::~GDriveSession() +{ +} + + +void GDriveSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); + + oauth2Authenticate( ); +} + +void GDriveSession::oauth2Authenticate() +{ + // treat the supplied password as refresh token + if (!m_password.empty()) + { + try + { + m_inOAuth2Authentication = true; + + m_oauth2Handler->setRefreshToken(m_password); + // Try to get new access tokens using the stored refreshtoken + m_oauth2Handler->refresh(); + m_inOAuth2Authentication = false; + } + catch (const CurlException &e) + { + m_inOAuth2Authentication = false; + // refresh token expired or invalid, trigger initial auth (that in turn will hit the fallback with copy'n'paste method) + BaseSession::oauth2Authenticate(); + } + } + else + { + BaseSession::oauth2Authenticate(); + } +} + +string GDriveSession::getRefreshToken() { + return HttpSession::getRefreshToken(); +} + +libcmis::RepositoryPtr GDriveSession::getRepository( ) +{ + // Return a dummy repository since GDrive doesn't have that notion + libcmis::RepositoryPtr repo( new GdriveRepository( ) ); + return repo; +} + +bool GDriveSession::setRepository( std::string ) +{ + return true; +} + +libcmis::ObjectPtr GDriveSession::getObject( string objectId ) +{ + if(objectId == "root") { + return getRootFolder(); + } + // Run the http request to get the properties definition + string res; + string objectLink = GDRIVE_METADATA_LINK + objectId + + "?fields=kind,id,name,parents,mimeType,createdTime,modifiedTime,thumbnailLink,size"; + try + { + res = httpGetRequest( objectLink )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + + // If we have a folder, then convert the object + // into a GDriveFolder otherwise, convert it + // into a GDriveDocument + libcmis::ObjectPtr object; + string kind = jsonRes["kind"].toString( ); + if ( kind == "drive#file" ) + { + string mimeType = jsonRes["mimeType"].toString( ); + + // Folder is a file with a special mimeType + if ( mimeType == GDRIVE_FOLDER_MIME_TYPE ) + object.reset( new GDriveFolder( this, jsonRes ) ); + else + object.reset( new GDriveDocument( this, jsonRes ) ); + } else if ( kind == "drive#revision" ) // A revision is a document too + { + object.reset( new GDriveDocument( this, jsonRes ) ); + } + else // not a folder nor file, maybe a permission or changes,... + object.reset( new GDriveObject( this, jsonRes ) ); + + return object; +} + +libcmis::ObjectPtr GDriveSession::getObjectByPath( string path ) +{ + size_t pos = 0; + size_t endpos = 0; + string objectId; + libcmis::ObjectPtr object; + + do + { + endpos = path.find( "/", pos ); + size_t len = path.length( ) - pos; + if ( endpos != string::npos ) + len = endpos - pos; + + string segment = path.substr( pos, len ); + if ( segment.empty( ) ) + { + // Root case or ignore double slashes + if ( pos == 0 ) + objectId = "root"; + else + continue; + } + else + { + // Normal child case + // Ask for the ID of the child if there is any + // somewhat flawed as names are not necessarily unique in GDrive... + string query = libcmis::escape("'" + objectId + "' in parents and trashed = false and name='" + segment + "'"); + + string childIdUrl = m_bindingUrl + "/files/?q=" + query + "&fields=files(id)"; + + string res; + try + { + res = httpGetRequest( childIdUrl )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + + // Did we get an id? + Json::JsonVector items = jsonRes["files"].getList(); + if ( items.empty( ) ) + throw libcmis::Exception( "Object not found: " + path, "objectNotFound" ); + + objectId = items[0]["id"].toString( ); + if ( objectId.empty( ) ) + throw libcmis::Exception( "Object not found: " + path, "objectNotFound" ); + } + + pos = endpos + 1; + } while ( endpos != string::npos ); + + return getObject( objectId ); +} + +libcmis::FolderPtr GDriveSession::getRootFolder() +{ + // permissions/scope with just drive.file don't allow to get it with the "root" alias/by its actual object-ID + Json propsJson; + + // GDrive folder is a file with a different mime type. + string mimeType = GDRIVE_FOLDER_MIME_TYPE; + + // Add mimetype to the propsJson + Json jsonMimeType( mimeType.c_str( ) ); + propsJson.add( "mimeType", jsonMimeType ); + propsJson.add( "id", "root" ); + + // Upload meta-datas + propsJson.add("cmis:name", "VirtualRoot"); + + libcmis::FolderPtr folderPtr( new GDriveFolder( this, propsJson ) ); + + return folderPtr; +} + +libcmis::ObjectTypePtr GDriveSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new GdriveObjectType( id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > GDriveSession::getBaseTypes( ) +{ + vector< libcmis::ObjectTypePtr > types; + // TODO Implement me + return types; +} diff --git a/src/libcmis/gdrive-session.hxx b/src/libcmis/gdrive-session.hxx new file mode 100644 index 0000000..d29d454 --- /dev/null +++ b/src/libcmis/gdrive-session.hxx @@ -0,0 +1,72 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _GDRIVE_SESSION_HXX_ +#define _GDRIVE_SESSION_HXX_ + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" + +class GDriveSession : public BaseSession +{ + public: + GDriveSession( std::string baseUrl, + std::string username, + std::string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose = false ); + + ~GDriveSession ( ); + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + virtual libcmis::FolderPtr getRootFolder(); + + virtual std::string getRefreshToken(); + + private: + GDriveSession( ); + GDriveSession( const GDriveSession& copy ) = delete; + GDriveSession& operator=( const GDriveSession& copy ) = delete; + + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + void oauth2Authenticate( ); +}; + +#endif /* _GDRIVE_SESSION_HXX_ */ diff --git a/src/libcmis/gdrive-utils.cxx b/src/libcmis/gdrive-utils.cxx new file mode 100644 index 0000000..3cc0288 --- /dev/null +++ b/src/libcmis/gdrive-utils.cxx @@ -0,0 +1,221 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "gdrive-utils.hxx" + +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +string GdriveUtils::toCmisKey( const string& key ) +{ + string convertedKey; + if ( key == "id") + convertedKey = "cmis:objectId"; + else if ( key == "ownerNames" ) + convertedKey = "cmis:createdBy"; + else if ( key == "description" ) + convertedKey = "cmis:description"; + else if ( key == "createdTime" ) + convertedKey = "cmis:creationDate"; + else if ( key == "lastModifyingUserName" ) + convertedKey = "cmis:lastModifiedBy"; + else if ( key == "modifiedTime" ) + convertedKey = "cmis:lastModificationDate"; + else if ( key == "name" ) + convertedKey = "cmis:contentStreamFileName"; + else if ( key == "mimeType" ) + convertedKey = "cmis:contentStreamMimeType"; + else if ( key == "size" ) + convertedKey = "cmis:contentStreamLength"; + else if ( key == "editable" ) + convertedKey = "cmis:isImmutable"; + else if ( key == "parents" ) + convertedKey = "cmis:parentId"; + else convertedKey = key; + return convertedKey; +} + +string GdriveUtils::toGdriveKey( const string& key ) +{ + string convertedKey; + if ( key == "cmis:objectId") + convertedKey = "id"; + else if ( key == "cmis:createdBy" ) + convertedKey = "ownerNames"; + else if ( key == "cmis:creationDate" ) + convertedKey = "createdTime"; + else if ( key == "cmis:description" ) + convertedKey = "description"; + else if ( key == "cmis:lastModifiedBy" ) + convertedKey = "lastModifyingUserName"; + else if ( key == "cmis:lastModificationDate" ) + convertedKey = "modifiedTime"; + else if ( key == "cmis:contentStreamFileName" ) + convertedKey = "name"; + else if ( key == "cmis:name" ) + convertedKey = "name"; + else if ( key == "cmis:contentStreamMimeType" ) + convertedKey = "mimeType"; + else if ( key == "cmis:contentStreamLength" ) + convertedKey = "size"; + else if ( key == "cmis:isImmutable" ) + convertedKey = "editable"; + else if ( key == "cmis:parentId" ) + convertedKey = "parents"; + else convertedKey = key; + return convertedKey; +} + +Json GdriveUtils::toGdriveJson( const PropertyPtrMap& properties ) +{ + Json propsJson; + + // check if cmis:name and cmis:contentStreamFileName has been duplicated + bool duplicated = false; + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + string key = it->first; + Json value( it->second ); + + // Convert the key back to the gdrive key + // take one of the two: cmis:name and cmis:contentStreamFileName + const bool isName = key == "cmis:name" || key == "cmis:contentStreamFileName"; + + if ( !isName || !duplicated ) + propsJson.add( toGdriveKey( key ), value ); + + if ( isName ) + duplicated = true; + } + + return propsJson; +} + +bool GdriveUtils::checkUpdatable( const string& key ) +{ + // taken from https://developers.google.com/drive/v2/reference/files + bool updatable = ( key == "name" || + key == "description" || + key == "modifiedTime" || + key == "lastViewedByMeDate" ); + return updatable; +} + +bool GdriveUtils::checkMultiValued( const string& key ) +{ + bool bMultiValued = ( key == "parents" || + key == "exportLinks" || + key == "labels" || + key == "ownersName" || + key == "owners"); + return bMultiValued; +} + +Json GdriveUtils::createJsonFromParentId( const string& parentId ) +{ + // parents is a Json array + Json firstParent; + firstParent.add( Json( parentId.c_str() ) ); + + return firstParent; +} + +vector< string > GdriveUtils::parseGdriveProperty( string key, Json json ) +{ + vector< string > values; + if ( key == "owners" ) + { + Json::JsonVector owners = json.getList( ); + for ( Json::JsonVector::iterator it = owners.begin( ); + it != owners.end( ); ++it ) + { + string ownerName = ( *it )["displayName"].toString( ); + values.push_back( ownerName); + } + } + else if ( key == "lastModifyingUser" ) + { + string ownerName = json["displayName"].toString( ); + values.push_back( ownerName); + } + else if ( key == "userPermission" ) + { + string ownerName = json["role"].toString( ); + values.push_back( ownerName); + } + else if ( key == "ownerNames" ) + { + Json::JsonVector owners = json.getList( ); + for ( Json::JsonVector::iterator it = owners.begin( ); + it != owners.end( ); ++it ) + { + string ownerName = ( *it )[""].toString( ); + values.push_back( ownerName); + } + } + else if ( key == "parents" ) + { + Json::JsonVector owners = json.getList( ); + for ( Json::JsonVector::iterator it = owners.begin( ); + it != owners.end( ); ++it ) + { + string ownerName = ( *it )["id"].toString( ); + values.push_back( ownerName); + } + } + else if ( key == "exportLinks" ) + { + Json::JsonObject exportLinks = json.getObjects( ); + for ( Json::JsonObject::iterator it = exportLinks.begin( ); + it != exportLinks.end( ); ++it ) + { + string mimeType = it->first; + string link = it->second.toString( ); + values.push_back( mimeType + ":\"" + link +"\""); + } + } + else if ( key == "labels" ) + { + Json::JsonObject labels = json.getObjects( ); + for ( Json::JsonObject::iterator it = labels.begin( ); + it != labels.end( ); ++it ) + { + string label = it->first; + string isSet = it->second.toString( ); + values.push_back( label + ": " + isSet ); + } + } + else values.push_back( json.toString( ) ); + return values; +} + diff --git a/src/libcmis/gdrive-utils.hxx b/src/libcmis/gdrive-utils.hxx new file mode 100644 index 0000000..06ad568 --- /dev/null +++ b/src/libcmis/gdrive-utils.hxx @@ -0,0 +1,67 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _GDRIVE_UTILS_HXX_ +#define _GDRIVE_UTILS_HXX_ + +#include <string> + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +static const std::string GDRIVE_FOLDER_MIME_TYPE = "application/vnd.google-apps.folder" ; +static const std::string GDRIVE_UPLOAD_LINK = "https://www.googleapis.com/upload/drive/v3/files/"; +static const std::string GDRIVE_METADATA_LINK = "https://www.googleapis.com/drive/v3/files/"; + +class GdriveUtils +{ + public : + + // Convert a GDrive Property key to a CMIS key + static std::string toCmisKey( const std::string& key); + + // Convert a CMIS key to GDrive key + static std::string toGdriveKey( const std::string& key ); + + // Convert CMIS properties to GDrive properties + static Json toGdriveJson( const libcmis::PropertyPtrMap& properties ); + + // Check if a property is updatable + static bool checkUpdatable( const std::string& key); + + // Check if a property has multiple values + static bool checkMultiValued( const std::string& key); + + // Create a Json array from a ParentId + static Json createJsonFromParentId( const std::string& parentId ); + + // Parse a Gdrive property value to CMIS values + static std::vector< std::string > parseGdriveProperty( std::string key, Json jsonValue ); +}; + +#endif diff --git a/src/libcmis/http-session.cxx b/src/libcmis/http-session.cxx new file mode 100644 index 0000000..f690914 --- /dev/null +++ b/src/libcmis/http-session.cxx @@ -0,0 +1,994 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "http-session.hxx" + +#include <cctype> +#include <memory> +#include <string> +#include <assert.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/session-factory.hxx> +#include <libcmis/xml-utils.hxx> + +#include "oauth2-handler.hxx" + +using namespace std; + +namespace +{ + size_t lcl_getHeaders( void *ptr, size_t size, size_t nmemb, void *userdata ) + { + libcmis::HttpResponse* response = static_cast< libcmis::HttpResponse* >( userdata ); + + string buf( ( const char* ) ptr, size * nmemb ); + + size_t sepPos = buf.find( ':' ); + if ( sepPos != string::npos ) + { + string name( buf, 0, sepPos ); + string value = buf.substr( sepPos + 1 ); + value = libcmis::trim( value ); + + response->getHeaders()[name] = value; + + if ( "Content-Transfer-Encoding" == name ) + response->getData( )->setEncoding( value ); + } + + return nmemb; + } + + size_t lcl_bufferData( void* buffer, size_t size, size_t nmemb, void* data ) + { + libcmis::EncodedData* encoded = static_cast< libcmis::EncodedData* >( data ); + encoded->decode( buffer, size, nmemb ); + return nmemb; + } + + size_t lcl_readStream( void* buffer, size_t size, size_t nmemb, void* data ) + { + istream& is = *( static_cast< istream* >( data ) ); + char* out = ( char * ) buffer; + is.read( out, size * nmemb ); + + return is.gcount( ) / size; + } + +#if (LIBCURL_VERSION_MAJOR < 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 85) + curlioerr lcl_ioctlStream( CURL* /*handle*/, int cmd, void* data ) + { + curlioerr errCode = CURLIOE_OK; + + switch ( cmd ) + { + case CURLIOCMD_RESTARTREAD: + { + istream& is = *( static_cast< istream* >( data ) ); + is.clear( ); + is.seekg( 0, ios::beg ); + + if ( !is.good() ) + { + fprintf ( stderr, "rewind failed\n" ); + errCode = CURLIOE_FAILRESTART; + } + } + break; + case CURLIOCMD_NOP: + break; + default: + errCode = CURLIOE_UNKNOWNCMD; + } + return errCode; + } +#endif + + int lcl_seekStream(void* data, curl_off_t offset, int origin) + { + std::ios_base::seekdir dir = {}; + switch (origin) + { + case SEEK_SET: dir = std::ios_base::beg; break; + case SEEK_CUR: dir = std::ios_base::cur; break; + case SEEK_END: dir = std::ios_base::end; break; + default: assert(false); break; + } + istream& is = *(static_cast<istream*>(data)); + is.clear(); + is.seekg(offset, dir); + if (!is.good()) + { + fprintf(stderr, "rewind failed\n"); + return CURL_SEEKFUNC_FAIL; + } + return CURL_SEEKFUNC_OK; + } + + template<typename T> + class ScopeGuard + { + public: + ScopeGuard(T &var, T newValue) + : m_var(var) + , m_origValue(var) + { + m_var = newValue; + } + + ~ScopeGuard() + { + m_var = m_origValue; + } + + private: + T& m_var; + const T m_origValue; + }; +} + +HttpSession::HttpSession( string username, string password, bool noSslCheck, + libcmis::OAuth2DataPtr oauth2, bool verbose, + libcmis::CurlInitProtocolsFunction initProtocolsFunction) : + m_curlHandle( NULL ), + m_CurlInitProtocolsFunction(initProtocolsFunction), + m_no100Continue( false ), + m_oauth2Handler( NULL ), + m_username( username ), + m_password( password ), + m_authProvided( false ), + m_verbose( verbose ), + m_noHttpErrors( false ), + m_noSSLCheck( noSslCheck ), + m_refreshedToken( false ), + m_inOAuth2Authentication( false ), + m_authMethod( CURLAUTH_ANY ) +{ + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); + + if ( oauth2 && oauth2->isComplete( ) ){ + setOAuth2Data( oauth2 ); + } +} + +HttpSession::HttpSession( const HttpSession& copy ) : + m_curlHandle( NULL ), + m_no100Continue( copy.m_no100Continue ), + m_oauth2Handler( copy.m_oauth2Handler ), + m_username( copy.m_username ), + m_password( copy.m_password ), + m_authProvided( copy.m_authProvided ), + m_verbose( copy.m_verbose ), + m_noHttpErrors( copy.m_noHttpErrors ), + m_noSSLCheck( copy.m_noSSLCheck ), + m_refreshedToken( false ), + m_inOAuth2Authentication( false ), + m_authMethod( copy.m_authMethod ) +{ + // Not sure how sharing curl handles is safe. + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); +} + +HttpSession::HttpSession( ) : + m_curlHandle( NULL ), + m_no100Continue( false ), + m_oauth2Handler( NULL ), + m_username( ), + m_password( ), + m_authProvided( false ), + m_verbose( false ), + m_noHttpErrors( false ), + m_noSSLCheck( false ), + m_refreshedToken( false ), + m_inOAuth2Authentication( false ), + m_authMethod( CURLAUTH_ANY ) +{ + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); +} + +HttpSession& HttpSession::operator=( const HttpSession& copy ) +{ + if ( this != © ) + { + curl_easy_cleanup( m_curlHandle ); + m_curlHandle = NULL; + m_no100Continue = copy.m_no100Continue; + m_oauth2Handler = copy.m_oauth2Handler; + m_username = copy.m_username; + m_password = copy.m_password; + m_authProvided = copy.m_authProvided; + m_verbose = copy.m_verbose; + m_noHttpErrors = copy.m_noHttpErrors; + m_noSSLCheck = copy.m_noSSLCheck; + m_refreshedToken = copy.m_refreshedToken; + m_inOAuth2Authentication = copy.m_inOAuth2Authentication; + m_authMethod = copy.m_authMethod; + + // Not sure how sharing curl handles is safe. + curl_global_init( CURL_GLOBAL_ALL ); + m_curlHandle = curl_easy_init( ); + } + + return *this; +} + +HttpSession::~HttpSession( ) +{ + if ( NULL != m_curlHandle ) + curl_easy_cleanup( m_curlHandle ); + delete( m_oauth2Handler ); +} + +string& HttpSession::getUsername( ) +{ + checkCredentials( ); + return m_username; +} + +string& HttpSession::getPassword( ) +{ + checkCredentials( ); + return m_password; +} + +libcmis::HttpResponsePtr HttpSession::httpGetRequest( string url ) +{ + checkOAuth2( url ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + // fix Cloudoku too many redirects error + // note: though curl doc says -1 is the default for MAXREDIRS, the error i got + // said it was 0 + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + try + { + httpRunRequest( url ); + response->getData( )->finish( ); + } + catch ( const CurlException& ) + { + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( getHttpStatus( ) == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpGetRequest( url ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + throw; + } + m_refreshedToken = false; + } + else throw; + } + m_refreshedToken = false; + + return response; +} + +libcmis::HttpResponsePtr HttpSession::httpPatchRequest( string url, istream& is, vector< string > headers ) +{ + checkOAuth2( url ); + + // Duplicate istream in case we need to retry + string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) ); + + istringstream isOriginal( isStr ), isBackup( isStr ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + // Get the stream length + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + curl_easy_setopt( m_curlHandle, CURLOPT_INFILESIZE, size ); + curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal ); + curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_UPLOAD, 1 ); + curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "PATCH" ); +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKFUNCTION, lcl_seekStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKDATA, &isOriginal ); +#else + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal ); +#endif + + // If we know for sure that 100-Continue won't be accepted, + // don't even try with it to save one HTTP request. + if ( m_no100Continue ) + headers.push_back( "Expect:" ); + try + { + httpRunRequest( url, headers ); + response->getData( )->finish(); + } + catch ( const CurlException& ) + { + long status = getHttpStatus( ); + /** If we had a HTTP 417 response, this is likely to be due to some + HTTP 1.0 proxy / server not accepting the "Expect: 100-continue" + header. Try to disable this header and try again. + */ + if ( status == 417 && !m_no100Continue) + { + // Remember that we don't want 100-Continue for the future requests + m_no100Continue = true; + response = httpPutRequest( url, isBackup, headers ); + } + + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpPutRequest( url, isBackup, headers ); + m_refreshedToken = false; + } + catch (const CurlException&) + { + m_refreshedToken = false; + throw; + } + } + // Has tried but failed + if ( ( status != 417 || m_no100Continue ) && + ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw; + } + m_refreshedToken = false; + return response; +} + +libcmis::HttpResponsePtr HttpSession::httpPutRequest( string url, istream& is, vector< string > headers ) +{ + checkOAuth2( url ); + + // Duplicate istream in case we need to retry + string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) ); + + istringstream isOriginal( isStr ), isBackup( isStr ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + // Get the stream length + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + curl_easy_setopt( m_curlHandle, CURLOPT_INFILESIZE, size ); + curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal ); + curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_UPLOAD, 1 ); +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKFUNCTION, lcl_seekStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKDATA, &isOriginal ); +#else + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal ); +#endif + + // If we know for sure that 100-Continue won't be accepted, + // don't even try with it to save one HTTP request. + if ( m_no100Continue ) + headers.push_back( "Expect:" ); + try + { + httpRunRequest( url, headers ); + response->getData( )->finish(); + } + catch ( const CurlException& ) + { + long status = getHttpStatus( ); + /** If we had a HTTP 417 response, this is likely to be due to some + HTTP 1.0 proxy / server not accepting the "Expect: 100-continue" + header. Try to disable this header and try again. + */ + if ( status == 417 && !m_no100Continue) + { + // Remember that we don't want 100-Continue for the future requests + m_no100Continue = true; + response = httpPutRequest( url, isBackup, headers ); + } + + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpPutRequest( url, isBackup, headers ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + m_refreshedToken = false; + throw; + } + } + // Has tried but failed + if ( ( status != 417 || m_no100Continue ) && + ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw; + } + m_refreshedToken = false; + return response; +} + +libcmis::HttpResponsePtr HttpSession::httpPostRequest( const string& url, istream& is, + const string& contentType, bool redirect ) +{ + checkOAuth2( url ); + + // Duplicate istream in case we need to retry + string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) ); + + istringstream isOriginal( isStr ), isBackup( isStr ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) ); + + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders ); + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() ); + + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20); + + // Get the stream length + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + curl_easy_setopt( m_curlHandle, CURLOPT_POSTFIELDSIZE, size ); + curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal ); + curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_POST, 1 ); +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKFUNCTION, lcl_seekStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_SEEKDATA, &isOriginal ); +#else + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream ); + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal ); +#endif + + vector< string > headers; + headers.push_back( string( "Content-Type:" ) + contentType ); + + // If we know for sure that 100-Continue won't be accepted, + // don't even try with it to save one HTTP request. + if ( m_no100Continue ) + headers.push_back( "Expect:" ); + try + { + httpRunRequest( url, headers, redirect ); + response->getData( )->finish(); + } + catch ( const CurlException& ) + { + + long status = getHttpStatus( ); + /** If we had a HTTP 417 response, this is likely to be due to some + HTTP 1.0 proxy / server not accepting the "Expect: 100-continue" + header. Try to disable this header and try again. + */ + if ( status == 417 && !m_no100Continue ) + { + // Remember that we don't want 100-Continue for the future requests + m_no100Continue = true; + response = httpPostRequest( url, isBackup, contentType, redirect ); + } + + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + response = httpPostRequest( url, isBackup, contentType, redirect ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + m_refreshedToken = false; + throw; + } + } + + // Has tried but failed + if ( ( status != 417 || m_no100Continue ) && + ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw; + } + m_refreshedToken = false; + + return response; +} + +void HttpSession::httpDeleteRequest( string url ) +{ + checkOAuth2( url ); + + // Reset the handle for the request + curl_easy_reset( m_curlHandle ); + initProtocols( ); + + curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "DELETE" ); + try + { + httpRunRequest( url ); + } + catch ( const CurlException& ) + { + // If the access token is expired, we get 401 error, + // Need to use the refresh token to get a new one. + if ( getHttpStatus( ) == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken ) + { + + // Refresh the token + oauth2Refresh(); + + // Resend the query + try + { + // Avoid infinite recursive call + m_refreshedToken = true; + httpDeleteRequest( url ); + m_refreshedToken = false; + } + catch (const CurlException& ) + { + m_refreshedToken = false; + throw; + } + } + else throw; + } + m_refreshedToken = false; +} + +void HttpSession::checkCredentials( ) +{ + // Check that we have the complete credentials + libcmis::AuthProviderPtr authProvider = libcmis::SessionFactory::getAuthenticationProvider(); + if ( authProvider && !m_authProvided && ( m_username.empty() || m_password.empty() ) ) + { + m_authProvided = authProvider->authenticationQuery( m_username, m_password ); + if ( !m_authProvided ) + { + throw CurlException( "User cancelled authentication request" ); + } + } +} + +void HttpSession::httpRunRequest( string url, vector< string > headers, bool redirect ) +{ + // Redirect + curl_easy_setopt( m_curlHandle, CURLOPT_FOLLOWLOCATION, redirect); + + // Activate the cookie engine + curl_easy_setopt( m_curlHandle, CURLOPT_COOKIEFILE, "" ); + + // Grab something from the web + curl_easy_setopt( m_curlHandle, CURLOPT_URL, url.c_str() ); + + // Set the headers + struct curl_slist *headers_slist = NULL; + for ( vector< string >::iterator it = headers.begin( ); it != headers.end( ); ++it ) + headers_slist = curl_slist_append( headers_slist, it->c_str( ) ); + + // If we are using OAuth2, then add the proper header with token to authenticate + // Otherwise, just set the credentials normally using in libcurl options + if ( m_oauth2Handler != NULL && !m_oauth2Handler->getHttpHeader( ).empty() ) + { + headers_slist = curl_slist_append( headers_slist, + m_oauth2Handler->getHttpHeader( ).c_str( ) ); + } + else if ( !getUsername().empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPAUTH, m_authMethod ); +#if LIBCURL_VERSION_VALUE >= 0x071301 + curl_easy_setopt( m_curlHandle, CURLOPT_USERNAME, getUsername().c_str() ); + curl_easy_setopt( m_curlHandle, CURLOPT_PASSWORD, getPassword().c_str() ); +#else + string userpwd = getUsername() + ":" + getPassword(); + curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, userpwd.c_str( ) ); +#endif + } + + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, headers_slist ); + + // Set the proxy configuration if any + if ( !libcmis::SessionFactory::getProxy( ).empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, libcmis::SessionFactory::getProxy( ).c_str() ); +#if LIBCURL_VERSION_VALUE >= 0x071304 + curl_easy_setopt( m_curlHandle, CURLOPT_NOPROXY, libcmis::SessionFactory::getNoProxy( ).c_str() ); +#endif + const string& proxyUser = libcmis::SessionFactory::getProxyUser( ); + const string& proxyPass = libcmis::SessionFactory::getProxyPass( ); + if ( !proxyUser.empty( ) && !proxyPass.empty( ) ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERNAME, proxyUser.c_str( ) ); + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYPASSWORD, proxyPass.c_str( ) ); +#else + string userpwd = proxyUser + ":" + proxyPass; + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, userpwd.c_str( ) ); +#endif + } + } + + // Get some feedback when something wrong happens + char errBuff[CURL_ERROR_SIZE]; + errBuff[0] = 0; + curl_easy_setopt( m_curlHandle, CURLOPT_ERRORBUFFER, errBuff ); + + // We want to get the response even if there is an Http error + if ( !m_noHttpErrors ) + curl_easy_setopt( m_curlHandle, CURLOPT_FAILONERROR, 1 ); + + if ( m_verbose ) + curl_easy_setopt( m_curlHandle, CURLOPT_VERBOSE, 1 ); + + // We want to get the certificate infos in error cases +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_CERTINFO, 1 ); +#endif + + if ( m_noSSLCheck ) + { +#if LIBCURL_VERSION_VALUE >= 0x070801 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); +#endif +#if LIBCURL_VERSION_VALUE >= 0x070402 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); +#endif + } + + // Perform the query + CURLcode errCode = curl_easy_perform( m_curlHandle ); + + // Free the headers list + curl_slist_free_all( headers_slist ); + + // Process the response + bool isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + if ( CURLE_OK != errCode && !( m_noHttpErrors && isHttpError ) ) + { + long httpError = 0; + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + + bool errorFixed = false; +#if LIBCURL_VERSION_VALUE >= 0X071301 + // If we had a bad certificate, then try to get more details + if ( CURLE_SSL_CACERT == errCode ) + { + vector< string > certificates; + + // We somehow need to rerun the request to get the certificate + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); + errCode = curl_easy_perform( m_curlHandle ); + + union { + struct curl_slist *to_info; + struct curl_certinfo *to_certinfo; + } ptr; + + ptr.to_info = NULL; + + CURLcode res = curl_easy_getinfo(m_curlHandle, CURLINFO_CERTINFO, &ptr.to_info); + + if ( !res && ptr.to_info ) + { + // We need the first certificate in the chain only + if ( ptr.to_certinfo->num_of_certs > 0 ) + { + struct curl_slist *slist; + + string certStart( "Cert:" ); + for ( slist = ptr.to_certinfo->certinfo[0]; slist; slist = slist->next ) + { + string data( slist->data ); + size_t startPos = data.find( certStart ); + if ( startPos == 0 ) + { + startPos = certStart.length(); + data = data.substr( startPos ); + certificates.push_back( data ); + } + } + } + } + + if ( !certificates.empty() ) + { + libcmis::CertValidationHandlerPtr validationHandler = + libcmis::SessionFactory::getCertificateValidationHandler( ); + bool ignoreCert = validationHandler && validationHandler->validateCertificate( certificates ); + if ( ignoreCert ) + { + m_noSSLCheck = true; + + isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + errorFixed = ( CURLE_OK == errCode || ( m_noHttpErrors && isHttpError ) ); + if ( !errorFixed ) + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + } + else + { + throw CurlException( "Invalid SSL certificate" ); + } + } + } +#endif + + if ( !errorFixed ) + throw CurlException( string( errBuff ), errCode, url, httpError ); + } +} + + +void HttpSession::checkOAuth2( string url ) +try +{ + if ( m_oauth2Handler ) + { + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( url ) ); + if ( m_oauth2Handler->getAccessToken().empty() && !m_inOAuth2Authentication ) + oauth2Authenticate( ); + } +} +catch ( const libcmis::Exception& e ) +{ + throw CurlException( e.what( ) ); +} + +long HttpSession::getHttpStatus( ) +{ + long status = 0; + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &status ); + + return status; +} + +void HttpSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); +} + +void HttpSession::oauth2Authenticate( ) +{ + string authCode; + + const ScopeGuard<bool> inOauth2Guard(m_inOAuth2Authentication, true); + + try + { + // Try to get the authentication code using the given provider. + authCode = m_oauth2Handler->oauth2Authenticate( ); + + // If that didn't work, call the fallback provider from SessionFactory + if ( authCode.empty( ) ) + { + libcmis::OAuth2AuthCodeProvider fallbackProvider = libcmis::SessionFactory::getOAuth2AuthCodeProvider( ); + if ( fallbackProvider != NULL ) + { + unique_ptr< char, void (*)( void * ) > code{ + fallbackProvider( m_oauth2Handler->getAuthURL().c_str(), getUsername().c_str(), getPassword().c_str() ), + free }; + if ( code ) + authCode = string( code.get() ); + } + } + } + catch ( const CurlException& e ) + { + // Thrown by getUsername() and getPassword() if user cancels the credentials request + throw e.getCmisException( ); + } + + // If still no auth code, then raise an exception + if ( authCode.empty( ) ) + throw libcmis::Exception( "Couldn't get OAuth authentication code", "permissionDenied" ); + + m_oauth2Handler->fetchTokens( string( authCode ) ); +} + +void HttpSession::setNoSSLCertificateCheck( bool noCheck ) +{ + m_noSSLCheck = noCheck; +} + +string HttpSession::getRefreshToken( ) +{ + string refreshToken; + if ( m_oauth2Handler ) + refreshToken = m_oauth2Handler->getRefreshToken( ); + return refreshToken; +} + +void HttpSession::oauth2Refresh( ) +try +{ + const ScopeGuard<bool> inOauth2Guard(m_inOAuth2Authentication, true); + m_oauth2Handler->refresh( ); +} +catch ( const libcmis::Exception& e ) +{ + throw CurlException( e.what() ); +} + +void HttpSession::initProtocols( ) +{ +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85) + auto const protocols = "https,http"; + curl_easy_setopt(m_curlHandle, CURLOPT_PROTOCOLS_STR, protocols); + curl_easy_setopt(m_curlHandle, CURLOPT_REDIR_PROTOCOLS_STR, protocols); +#else + const unsigned long protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS; + curl_easy_setopt(m_curlHandle, CURLOPT_PROTOCOLS, protocols); + curl_easy_setopt(m_curlHandle, CURLOPT_REDIR_PROTOCOLS, protocols); +#endif + if (m_CurlInitProtocolsFunction) + { + (*m_CurlInitProtocolsFunction)(m_curlHandle); + } +} + +const char* CurlException::what( ) const noexcept +{ + if ( !isCancelled( ) ) + { + stringstream buf; + buf << "CURL error - " << ( unsigned int ) m_code << ": "; + buf << m_message; + m_errorMessage = buf.str( ); + + return m_errorMessage.c_str( ); + } + + return m_message.c_str( ); +} + +libcmis::Exception CurlException::getCmisException( ) const +{ + string msg; + string type( "runtime" ); + + switch ( m_httpStatus ) + { + case 400: + msg = string( what() ) + string( ": " ) + m_url; + type = "invalidArgument"; + break; + case 401: + msg = "Authentication failure"; + type = "permissionDenied"; + break; + case 403: + msg = "Invalid credentials"; + type = "permissionDenied"; + break; + case 404: + msg = "Invalid URL: " + m_url; + type = "objectNotFound"; + break; + case 405: + msg = string( what() ) + string( ": " ) + m_url; + type = "notSupported"; + break; + case 409: + msg = "Editing conflict error"; + type = "updateConflict"; + break; + default: + msg = what(); + if ( !isCancelled( ) ) + msg += ": " + m_url; + else + type = "permissionDenied"; + break; + } + + return libcmis::Exception( msg, type ); +} diff --git a/src/libcmis/http-session.hxx b/src/libcmis/http-session.hxx new file mode 100644 index 0000000..34223b2 --- /dev/null +++ b/src/libcmis/http-session.hxx @@ -0,0 +1,179 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _HTTP_SESSION_HXX_ +#define _HTTP_SESSION_HXX_ + +#include <istream> +#include <sstream> +#include <vector> +#include <string> + +#include <curl/curl.h> +#include <libxml/xmlstring.h> +#include <libxml/xpath.h> + +#include <libcmis/exception.hxx> +#include <libcmis/oauth2-data.hxx> +#include <libcmis/xml-utils.hxx> + +class OAuth2Handler; + +namespace libcmis { + typedef void(*CurlInitProtocolsFunction)(CURL *); +} + +class CurlException : public std::exception +{ + private: + std::string m_message; + CURLcode m_code; + std::string m_url; + long m_httpStatus; + + bool m_cancelled; + + mutable std::string m_errorMessage; + + public: + CurlException( std::string message, CURLcode code, std::string url, long httpStatus ) : + exception( ), + m_message( message ), + m_code( code ), + m_url( url ), + m_httpStatus( httpStatus ), + m_cancelled( false ), + m_errorMessage( ) + { + } + + CurlException( std::string message ) : + exception( ), + m_message( message ), + m_code( CURLE_OK ), + m_url( ), + m_httpStatus( 0 ), + m_cancelled( true ), + m_errorMessage( ) + { + } + + ~CurlException( ) noexcept { } + virtual const char* what( ) const noexcept; + + CURLcode getErrorCode( ) const { return m_code; } + std::string getErrorMessage( ) const { return m_message; } + bool isCancelled( ) const { return m_cancelled; } + long getHttpStatus( ) const { return m_httpStatus; } + + libcmis::Exception getCmisException ( ) const; +}; + +class HttpSession +{ + protected: + CURL* m_curlHandle; + libcmis::CurlInitProtocolsFunction m_CurlInitProtocolsFunction = nullptr; + private: + bool m_no100Continue; + protected: + OAuth2Handler* m_oauth2Handler; + std::string m_username; + std::string m_password; + bool m_authProvided; + + bool m_verbose; + bool m_noHttpErrors; + bool m_noSSLCheck; + bool m_refreshedToken; + bool m_inOAuth2Authentication; + unsigned long m_authMethod; + public: + HttpSession( std::string username, std::string password, + bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), + bool verbose = false, + libcmis::CurlInitProtocolsFunction = nullptr); + + HttpSession( const HttpSession& copy ); + virtual ~HttpSession( ); + + HttpSession& operator=( const HttpSession& copy ); + + std::string& getUsername( ); + + std::string& getPassword( ); + + /** Don't throw the HTTP errors as CurlExceptions. + */ + void setNoHttpErrors( bool noHttpErrors ) { m_noHttpErrors = noHttpErrors; } + + + /** Set the OAuth2 data and get the access / refresh tokens. + */ + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + libcmis::HttpResponsePtr httpGetRequest( std::string url ); + libcmis::HttpResponsePtr httpPatchRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ); + libcmis::HttpResponsePtr httpPutRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ); + libcmis::HttpResponsePtr httpPostRequest( const std::string& url, + std::istream& is, + const std::string& contentType, + bool redirect = true ); + void httpDeleteRequest( std::string url ); + + long getHttpStatus( ); + + void setNoSSLCertificateCheck( bool noCheck ); + + virtual std::string getRefreshToken( ); + + protected: + HttpSession( ); + + /** Helper function actually handling the oauth2 process. + This function is provided for BaseSession to customize + the OAuth2 login parser. + */ + void oauth2Authenticate( ); + void setAuthMethod( unsigned long authMethod ) { m_authMethod = authMethod; } + virtual void httpRunRequest( std::string url, + std::vector< std::string > headers = std::vector< std::string > ( ), + bool redirect = true ); + + private: + void checkCredentials( ); + void checkOAuth2( std::string url ); + void oauth2Refresh( ); + void initProtocols( ); +}; + +#endif diff --git a/src/libcmis/json-utils.cxx b/src/libcmis/json-utils.cxx new file mode 100644 index 0000000..52b036a --- /dev/null +++ b/src/libcmis/json-utils.cxx @@ -0,0 +1,306 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "json-utils.hxx" + +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/json_parser.hpp> + +#include <libcmis/exception.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace libcmis; +using namespace boost; +using boost::property_tree::ptree; +using boost::property_tree::json_parser::read_json; +using boost::property_tree::json_parser::write_json; + +Json::Json( ) : + m_tJson( ptree( ) ), + m_type( json_object ) +{ +} + +Json::Json( const char *str ) : + m_tJson( ptree( ) ), + m_type( json_string ) +{ + m_tJson.put_value( str ); + m_type = parseType( ); +} + +Json::Json( ptree tJson ) : + m_tJson( tJson ), + m_type( json_object ) +{ + m_type = parseType( ); +} + +Json::Json( const PropertyPtr& property ): + m_tJson( ), + m_type( json_object ) +{ + string str = property->toString( ); + m_tJson.put("", str ); +} + +Json::Json( const PropertyPtrMap& properties ) : + m_tJson( ptree( ) ), + m_type( json_array ) +{ + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + string key = it->first; + string value = it->second->toString( ); + m_tJson.put( key, value ); + } +} + +Json::Json( const Json& copy ) : + m_tJson( copy.m_tJson ), + m_type( copy.m_type ) +{ +} + +Json::Json( const JsonObject& obj ) : + m_tJson( ptree( ) ), + m_type( json_array ) +{ + for ( JsonObject::const_iterator i = obj.begin() ; i != obj.end() ; ++i ) + add( i->first, i->second ) ; +} + +Json::Json( const JsonVector& arr ) : + m_tJson( ptree( ) ), + m_type( json_array ) +{ + for ( std::vector<Json>::const_iterator i = arr.begin(); i != arr.end(); ++i ) + add( *i ) ; +} + + +Json::~Json( ) +{ +} + +Json& Json::operator=( const Json& rhs ) +{ + if ( this != &rhs ) + { + m_tJson = rhs.m_tJson; + m_type = rhs.m_type; + } + return *this ; +} + +void Json::swap( Json& rhs ) +{ + std::swap( m_tJson, rhs.m_tJson ); + std::swap( m_type, rhs.m_type ); +} + +Json Json::operator[]( string key ) const +{ + ptree tJson; + try + { + tJson = m_tJson.get_child( key ); + } + catch ( boost::exception const& ) + { + return Json( "" ); + } + + Json childJson( tJson ); + + return childJson ; +} + +void Json::add( const std::string& key, const Json& json ) +{ + try + { + m_tJson.add_child( key, json.getTree( ) ); + } + catch ( boost::exception const& ) + { + throw libcmis::Exception( "Couldn't add Json object" ); + } +} + +Json::JsonVector Json::getList() +{ + JsonVector list; + for ( const auto &v: m_tJson.get_child("") ) + { + list.push_back( Json( v.second ) ); + } + return list ; +} + +void Json::add( const Json& json ) +{ + try + { + m_tJson.push_back( ptree::value_type( "", json.getTree( )) ); + } + catch ( boost::exception const& ) + { + throw libcmis::Exception( "Couldn't add Json object" ); + } +} + +Json Json::parse( const string& str ) +{ + ptree pTree; + std::stringstream ss( str ); + if ( ss.good( ) ) + { + try + { + property_tree::json_parser::read_json( ss, pTree ); + } + catch ( boost::exception const& ) + { + return Json( str.c_str( ) ); + } + } + return Json( pTree ); +} + +Json::JsonObject Json::getObjects( ) +{ + JsonObject objs; + for ( const auto &v: m_tJson.get_child("") ) + { + Json jsonValue( v.second ); + objs.insert( JsonObject::value_type(v.first, jsonValue ) ); + } + return objs ; +} + +Json::Type Json::parseType( ) +{ + Type type = json_string; + string str = toString( ); + if ( str.empty( ) ) + return type; + try + { + boost::posix_time::ptime time = libcmis::parseDateTime( str ); + if ( !time.is_not_a_date_time( ) ) + return json_datetime; + } + catch (...) + { + // Try other types + } + Type backupType = type; + type = json_bool; + try + { + parseBool( str ); + } + catch (...) + { + type = backupType; + } + if ( type != json_bool ) + { + if ( str.find('.') == string::npos ) + { + backupType = type; + type = json_int; + try + { + parseInteger( str ); + } + catch(...) + { + type = backupType; + } + } + else + { + backupType = type; + type = json_double; + try + { + parseDouble( str ); + } + catch(...) + { + type = backupType; + } + } + } + return type; +} + +Json::Type Json::getDataType( ) const +{ + return m_type; +} + +string Json::getStrType( ) const +{ + switch ( m_type ) + { + case json_null: return "json_null"; + case json_bool: return "json_bool"; + case json_int: return "json_int"; + case json_double: return "json_double"; + case json_string: return "json_string"; + case json_datetime: return "json_datetime"; + case json_object: return "json_object"; + case json_array: return "json_array"; + } + return "json_string"; +} + +string Json::toString( ) const +{ + string str; + try + { + stringstream ss; + write_json( ss, m_tJson ); + str = ss.str( ); + } + catch ( boost::exception const& ) + { + str = m_tJson.get_value<string>( ); + } + // empty json + if ( str == "{\n}\n" ) str = ""; + return str; +} + diff --git a/src/libcmis/json-utils.hxx b/src/libcmis/json-utils.hxx new file mode 100644 index 0000000..ef3ae55 --- /dev/null +++ b/src/libcmis/json-utils.hxx @@ -0,0 +1,87 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _JSON_UTILS_HXX_ +#define _JSON_UTILS_HXX_ + +#include <string> +#include <map> +#include <vector> + +#include <boost/property_tree/ptree.hpp> + +#include <libcmis/exception.hxx> +#include <libcmis/property.hxx> + +class Json +{ + public : + typedef std::map< std::string, Json > JsonObject ; + typedef std::vector< Json > JsonVector ; + enum Type { json_null, json_bool, json_double, json_int, json_object, + json_array, json_string, json_datetime } ; + + Json( ); + Json( const Json& copy ); + Json( const char *str ); + Json( const libcmis::PropertyPtr& property ); + Json( const libcmis::PropertyPtrMap& properties ); + Json( const JsonVector& arr ); + Json( const JsonObject& obj ); + + ~Json( ) ; + + Json operator[]( std::string key ) const; + + Json& operator=( const Json& rhs ) ; + + void swap( Json& other ) ; + + void add( const Json& json); + + void add( const std::string& key, const Json& json); + + static Json parse( const std::string& str ); + + std::string toString( ) const; + Type getDataType( ) const ; + std::string getStrType( ) const ; + + JsonObject getObjects(); + JsonVector getList(); + + boost::property_tree::ptree getTree( ) const{ return m_tJson; } + private : + Json( boost::property_tree::ptree tJson ) ; + boost::property_tree::ptree m_tJson ; + Type m_type; + Type parseType( ); +} ; + +#endif /* _JSON_UTILS_HXX_ */ + diff --git a/src/libcmis/oauth2-data.cxx b/src/libcmis/oauth2-data.cxx new file mode 100644 index 0000000..a56251f --- /dev/null +++ b/src/libcmis/oauth2-data.cxx @@ -0,0 +1,95 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/oauth2-data.hxx> + +using namespace std; + +namespace libcmis +{ + OAuth2Data::OAuth2Data( ) : + m_authUrl( ), + m_tokenUrl( ), + m_clientId( ), + m_clientSecret( ), + m_scope( ), + m_redirectUri( ) + { + } + + OAuth2Data::OAuth2Data( const string& authUrl, const string& tokenUrl, + const string& scope, const string& redirectUri, + const string& clientId, const string& clientSecret ): + m_authUrl( authUrl ), + m_tokenUrl( tokenUrl ), + m_clientId( clientId ), + m_clientSecret( clientSecret ), + m_scope( scope ), + m_redirectUri( redirectUri ) + { + } + + OAuth2Data::OAuth2Data( const OAuth2Data& copy ) : + m_authUrl( copy.m_authUrl ), + m_tokenUrl( copy.m_tokenUrl ), + m_clientId( copy.m_clientId ), + m_clientSecret( copy.m_clientSecret ), + m_scope( copy.m_scope ), + m_redirectUri( copy.m_redirectUri ) + { + } + + OAuth2Data::~OAuth2Data( ) + { + } + + OAuth2Data& OAuth2Data::operator=( const OAuth2Data& copy ) + { + if ( this != © ) + { + m_authUrl = copy.m_authUrl; + m_tokenUrl = copy.m_tokenUrl; + m_clientId = copy.m_clientId; + m_clientSecret = copy.m_clientSecret; + m_scope = copy.m_scope; + m_redirectUri = copy.m_redirectUri; + } + + return *this; + } + + bool OAuth2Data::isComplete() + { + return !m_authUrl.empty() && + !m_tokenUrl.empty() && + !m_clientId.empty() && + !m_clientSecret.empty() && + !m_scope.empty() && + !m_redirectUri.empty(); + } +} diff --git a/src/libcmis/oauth2-handler.cxx b/src/libcmis/oauth2-handler.cxx new file mode 100644 index 0000000..a340b80 --- /dev/null +++ b/src/libcmis/oauth2-handler.cxx @@ -0,0 +1,196 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <boost/algorithm/string.hpp> + +#include "oauth2-handler.hxx" + +#include <libcmis/session-factory.hxx> +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" +#include "oauth2-providers.hxx" + +using namespace std; + +OAuth2Handler::OAuth2Handler(HttpSession* session, libcmis::OAuth2DataPtr data) : + m_session( session ), + m_data( data ), + m_access( ), + m_refresh( ), + m_oauth2Parser( ) +{ + if ( !m_data ) + m_data.reset( new libcmis::OAuth2Data() ); + +} + +OAuth2Handler::OAuth2Handler( const OAuth2Handler& copy ) : + m_session( copy.m_session ), + m_data( copy.m_data ), + m_access( copy.m_access ), + m_refresh( copy.m_refresh ), + m_oauth2Parser( copy.m_oauth2Parser ) +{ +} + +OAuth2Handler::OAuth2Handler( ): + m_session( NULL ), + m_data( ), + m_access( ), + m_refresh( ), + m_oauth2Parser( ) +{ + m_data.reset( new libcmis::OAuth2Data() ); +} + +OAuth2Handler& OAuth2Handler::operator=( const OAuth2Handler& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_data = copy.m_data; + m_access = copy.m_access; + m_refresh = copy.m_refresh; + m_oauth2Parser = copy.m_oauth2Parser; + } + + return *this; +} + +OAuth2Handler::~OAuth2Handler( ) +{ + +} + +void OAuth2Handler::fetchTokens( string authCode ) +{ + string post = + "code=" + authCode + + "&client_id=" + m_data->getClientId() + + "&redirect_uri=" + m_data->getRedirectUri() + + "&grant_type=authorization_code" ; + if(boost::starts_with(m_data->getTokenUrl(), "https://oauth2.googleapis.com/")) + post += "&client_secret=" + m_data->getClientSecret(); + else + post += "&scope=" + libcmis::escape( m_data->getScope() ); + + istringstream is( post ); + + libcmis::HttpResponsePtr resp; + + try + { + resp = m_session->httpPostRequest ( m_data->getTokenUrl(), is, + "application/x-www-form-urlencoded" ); + } + catch ( const CurlException& e) + { + throw libcmis::Exception( + "Couldn't get tokens from the authorization code "); + } + + Json jresp = Json::parse( resp->getStream( )->str( ) ); + m_access = jresp[ "access_token" ].toString( ); + m_refresh = jresp[ "refresh_token" ].toString( ); +} + +void OAuth2Handler::refresh( ) +{ + m_access = string( ); + string post = + "refresh_token=" + m_refresh + + "&client_id=" + m_data->getClientId() + + "&grant_type=refresh_token" ; + if(boost::starts_with(m_data->getTokenUrl(), "https://oauth2.googleapis.com/")) + post += "&client_secret=" + m_data->getClientSecret(); + + istringstream is( post ); + libcmis::HttpResponsePtr resp; + try + { + resp = m_session->httpPostRequest( m_data->getTokenUrl( ), is, + "application/x-www-form-urlencoded" ); + } + catch (const CurlException& e ) + { + throw libcmis::Exception( "Couldn't refresh token "); + } + + Json jresp = Json::parse( resp->getStream( )->str( ) ); + m_access = jresp[ "access_token" ].toString(); +} + +string OAuth2Handler::getAuthURL( ) +{ + return m_data->getAuthUrl() + + "?scope=" + libcmis::escape( m_data->getScope( ) ) + + "&redirect_uri="+ m_data->getRedirectUri( ) + + "&response_type=code" + + "&client_id=" + m_data->getClientId( ); +} + +string OAuth2Handler::getAccessToken( ) +{ + return m_access; +} + +string OAuth2Handler::getRefreshToken( ) +{ + return m_refresh; +} + +void OAuth2Handler::setRefreshToken( string refreshToken ) +{ + m_refresh = refreshToken; +} + +string OAuth2Handler::getHttpHeader( ) +{ + string header; + if ( !m_access.empty() ) + header = "Authorization: Bearer " + m_access ; + return header; +} + +string OAuth2Handler::oauth2Authenticate( ) +{ + string code; + if ( m_oauth2Parser ) + { + code = m_oauth2Parser( m_session, getAuthURL( ), + m_session->getUsername( ), + m_session->getPassword( ) ); + } + return code; +} + +void OAuth2Handler::setOAuth2Parser( OAuth2Parser parser ) +{ + m_oauth2Parser = parser; +} diff --git a/src/libcmis/oauth2-handler.hxx b/src/libcmis/oauth2-handler.hxx new file mode 100644 index 0000000..bb9a394 --- /dev/null +++ b/src/libcmis/oauth2-handler.hxx @@ -0,0 +1,97 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _OAUTH2_HANDLER_HXX_ +#define _OAUTH2_HANDLER_HXX_ + +#include <string> +#include "http-session.hxx" +#include "oauth2-providers.hxx" + +namespace libcmis +{ + class OAuth2Data; +} + +class OAuth2Handler +{ + private: + HttpSession* m_session; + libcmis::OAuth2DataPtr m_data; + + std::string m_access; + std::string m_refresh; + + OAuth2Parser m_oauth2Parser; + + public: + + OAuth2Handler( HttpSession* session, libcmis::OAuth2DataPtr data ); + + OAuth2Handler( const OAuth2Handler& copy ); + ~OAuth2Handler( ); + + OAuth2Handler& operator=( const OAuth2Handler& copy ); + + std::string getAuthURL(); + + std::string getAccessToken( ) ; + std::string getRefreshToken( ) ; + void setRefreshToken( std::string refreshToken ) ; + + // adding HTTP auth header + std::string getHttpHeader( ) ; + + /** Exchange the previously obtained authentication code with the + access/refresh tokens. + + \param authCode + the authentication code normally obtained from authenticate + method. + */ + void fetchTokens( std::string authCode ); + void refresh( ); + + /** Get the authentication code given credentials. + + This method should be overridden to parse the authentication URL response, + authenticate using the form and get the token to avoid asking the user + to launch a web browser and do it himself. + + \return + the authentication code to transform into access/refresh tokens. + If no code is found, an empty string is returned. + */ + std::string oauth2Authenticate( ); + + void setOAuth2Parser( OAuth2Parser parser ); + + protected: + OAuth2Handler( ); +}; + +#endif /* _OAUTH2_HANDLER_HXX_ */ diff --git a/src/libcmis/oauth2-providers.cxx b/src/libcmis/oauth2-providers.cxx new file mode 100644 index 0000000..1c8d3cc --- /dev/null +++ b/src/libcmis/oauth2-providers.cxx @@ -0,0 +1,246 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "oauth2-providers.hxx" + +#include <memory> + +#include <boost/algorithm/string.hpp> + +#include <libxml/HTMLparser.h> +#include <libxml/xmlreader.h> + +#include <libcmis/session-factory.hxx> + +#include "http-session.hxx" + +#define CHALLENGE_PAGE_ACTION "/signin" +#define CHALLENGE_PAGE_ACTION_LEN sizeof( CHALLENGE_PAGE_ACTION ) - 1 +#define PIN_FORM_ACTION "/signin/challenge/ipp" +#define PIN_FORM_ACTION_LEN sizeof( PIN_FORM_ACTION ) - 1 +#define PIN_INPUT_NAME "Pin" + +using namespace std; + +string OAuth2Providers::OAuth2Dummy( HttpSession* /*session*/, const string& /*authUrl*/, + const string& /*username*/, const string& /*password*/ ) +{ + return string( ); +} + +string OAuth2Providers::OAuth2Alfresco( HttpSession* session, const string& authUrl, + const string& username, const string& password ) +{ + static const string CONTENT_TYPE( "application/x-www-form-urlencoded" ); + + // Log in + string res; + try + { + res = session->httpGetRequest( authUrl )->getStream( )->str( ); + } + catch ( const CurlException& ) + { + return string( ); + } + + string loginPost, loginLink; + + if ( !parseResponse( res.c_str( ), loginPost, loginLink ) ) + return string( ); + + loginPost += "username="; + loginPost += string( username ); + loginPost += "&password="; + loginPost += string( password ); + loginPost += "&action=Grant"; + + istringstream loginIs( loginPost ); + + libcmis::HttpResponsePtr resp; + + // Get the code + try + { + // Alfresco code is in the redirect link + resp = session->httpPostRequest( loginLink, loginIs, CONTENT_TYPE, false ); + } + catch ( const CurlException& ) + { + return string( ); + } + + string header = resp->getHeaders()["Location"]; + string code; + auto start = header.find("code="); + if ( start != string::npos ) + { + start += 5; + auto end = header.find("&"); + if ( end != string::npos ) + code = header.substr( start, end - start ); + else code = header.substr( start ); + } + + return code; +} + +OAuth2Parser OAuth2Providers::getOAuth2Parser( const std::string& url ) +{ + if ( boost::starts_with( url, "https://api.alfresco.com/" ) ) + // For Alfresco in the cloud, only match the hostname as there can be several + // binding URLs created with it. + return OAuth2Alfresco; + + return OAuth2Dummy; +} + +int OAuth2Providers::parseResponse ( const char* response, string& post, string& link ) +{ + xmlDoc *doc = htmlReadDoc ( BAD_CAST( response ), NULL, 0, + HTML_PARSE_NOWARNING | HTML_PARSE_RECOVER | HTML_PARSE_NOERROR ); + if ( doc == NULL ) return 0; + xmlTextReaderPtr reader = xmlReaderWalker( doc ); + if ( reader == NULL ) return 0; + + bool readInputField = false; + bool bIsRightForm = false; + bool bHasPinField = false; + + while ( true ) + { + // Go to the next node, quit if not found + if ( xmlTextReaderRead ( reader ) != 1) break; + xmlChar* nodeName = xmlTextReaderName ( reader ); + if ( nodeName == NULL ) continue; + // Find the redirect link + if ( xmlStrEqual( nodeName, BAD_CAST( "form" ) ) ) + { + // 2FA: Don't add fields form other forms not having pin field + if ( bIsRightForm && !bHasPinField ) + post = string( "" ); + if ( bIsRightForm && bHasPinField ) + break; + + xmlChar* action = xmlTextReaderGetAttribute( reader, + BAD_CAST( "action" )); + + // GDrive pin code page contains many forms. + // We have to parse only the form with pin field. + if ( action != NULL ) + { + bool bChallengePage = ( strncmp( (char*)action, + CHALLENGE_PAGE_ACTION, + CHALLENGE_PAGE_ACTION_LEN ) == 0 ); + bIsRightForm = ( strncmp( (char*)action, + PIN_FORM_ACTION, + PIN_FORM_ACTION_LEN ) == 0 ); + if ( ( xmlStrlen( action ) > 0 ) + && ( ( bChallengePage && bIsRightForm ) || !bChallengePage ) ) + { + link = string ( (char*) action); + readInputField = true; + } + else + readInputField = false; + xmlFree (action); + } + } + // Find input values + if ( readInputField && !xmlStrcmp( nodeName, BAD_CAST( "input" ) ) ) + { + xmlChar* name = xmlTextReaderGetAttribute( reader, + BAD_CAST( "name" )); + xmlChar* value = xmlTextReaderGetAttribute( reader, + BAD_CAST( "value" )); + if ( name != NULL && strcmp( (char*)name, PIN_INPUT_NAME ) == 0 ) + bHasPinField = true; + if ( ( name != NULL ) && ( value!= NULL ) ) + { + if ( ( xmlStrlen( name ) > 0) && ( xmlStrlen( value ) > 0) ) + { + post += libcmis::escape( ( char * ) name ); + post += string ( "=" ); + post += libcmis::escape( ( char * ) value ); + post += string ( "&" ); + } + } + xmlFree( name ); + xmlFree( value ); + } + xmlFree( nodeName ); + } + xmlFreeTextReader( reader ); + xmlFreeDoc( doc ); + if ( link.empty( ) || post.empty () ) + return 0; + return 1; +} + +string OAuth2Providers::parseCode( const char* response ) +{ + string authCode; + xmlDoc *doc = htmlReadDoc ( BAD_CAST( response ), NULL, 0, + HTML_PARSE_NOWARNING | HTML_PARSE_RECOVER | HTML_PARSE_NOERROR ); + if ( doc == NULL ) return authCode; + xmlTextReaderPtr reader = xmlReaderWalker( doc ); + if ( reader == NULL ) return authCode; + + while ( true ) + { + // Go to the next node, quit if not found + if ( xmlTextReaderRead ( reader ) != 1) break; + xmlChar* nodeName = xmlTextReaderName ( reader ); + if ( nodeName == NULL ) continue; + // Find the code + if ( xmlStrEqual( nodeName, BAD_CAST ( "input" ) ) ) + { + xmlChar* id = xmlTextReaderGetAttribute( reader, BAD_CAST( "id" )); + if ( id != NULL ) + { + if ( xmlStrEqual( id, BAD_CAST ( "code" ) ) ) + { + xmlChar* code = xmlTextReaderGetAttribute( + reader, BAD_CAST("value") ); + if ( code!= NULL ) + { + authCode = string ( (char*) code ); + xmlFree( code ); + } + } + xmlFree ( id ); + } + } + xmlFree( nodeName ); + } + xmlFreeTextReader( reader ); + xmlFreeDoc( doc ); + + return authCode; +} + diff --git a/src/libcmis/oauth2-providers.hxx b/src/libcmis/oauth2-providers.hxx new file mode 100644 index 0000000..eaeb1c4 --- /dev/null +++ b/src/libcmis/oauth2-providers.hxx @@ -0,0 +1,63 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _OAUTH2_PROVIDERS_HXX_ +#define _OAUTH2_PROVIDERS_HXX_ + +#include <string> + +class HttpSession; + +typedef std::string ( *OAuth2Parser ) ( HttpSession* session, const std::string& authUrl, + const std::string& username, const std::string& password ); + +class OAuth2Providers +{ + public : + static std::string OAuth2Dummy( HttpSession* session, const std::string& authUrl, + const std::string& username, const std::string& password ); + static std::string OAuth2Alfresco( HttpSession* session, const std::string& authUrl, + const std::string& username, const std::string& password ); + + static OAuth2Parser getOAuth2Parser( const std::string& baseUrl ); + + /* + * Parse the authorization code from the response page + * in the input tag, with id = code + */ + static std::string parseCode ( const char* response ); + + /* + * Parse input values and redirect link from the response page + */ + static int parseResponse ( const char* response, + std::string& post, + std::string& link ); +}; + +#endif /* _OAUTH2_PROVIDERS_HXX_ */ diff --git a/src/libcmis/object-type.cxx b/src/libcmis/object-type.cxx new file mode 100644 index 0000000..868ec56 --- /dev/null +++ b/src/libcmis/object-type.cxx @@ -0,0 +1,368 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. +*/ + +#include <libcmis/object-type.hxx> + +#include <libcmis/xml-utils.hxx> +#include <libcmis/property.hxx> + +using namespace std; + +namespace libcmis +{ + ObjectType::ObjectType( ) : + m_refreshTimestamp( 0 ), + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_description( ), + m_parentTypeId( ), + m_baseTypeId( ), + m_creatable( false ), + m_fileable( false ), + m_queryable( false ), + m_fulltextIndexed( false ), + m_includedInSupertypeQuery( false ), + m_controllablePolicy( false ), + m_controllableAcl( false ), + m_versionable( false ), + m_contentStreamAllowed( libcmis::ObjectType::Allowed ), + m_propertiesTypes( ) + { + } + + ObjectType::ObjectType( xmlNodePtr node ) : + m_refreshTimestamp( 0 ), + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_description( ), + m_parentTypeId( ), + m_baseTypeId( ), + m_creatable( false ), + m_fileable( false ), + m_queryable( false ), + m_fulltextIndexed( false ), + m_includedInSupertypeQuery( false ), + m_controllablePolicy( false ), + m_controllableAcl( false ), + m_versionable( false ), + m_contentStreamAllowed( libcmis::ObjectType::Allowed ), + m_propertiesTypes( ) + { + initializeFromNode( node ); + } + + ObjectType::ObjectType( const ObjectType& copy ) : + m_refreshTimestamp( copy.m_refreshTimestamp ), + m_id( copy.m_id ), + m_localName( copy.m_localName ), + m_localNamespace( copy.m_localNamespace ), + m_displayName( copy.m_displayName ), + m_queryName( copy.m_queryName ), + m_description( copy.m_description ), + m_parentTypeId( copy.m_parentTypeId ), + m_baseTypeId( copy.m_baseTypeId ), + m_creatable( copy.m_creatable ), + m_fileable( copy.m_fileable ), + m_queryable( copy.m_queryable ), + m_fulltextIndexed( copy.m_fulltextIndexed ), + m_includedInSupertypeQuery( copy.m_includedInSupertypeQuery ), + m_controllablePolicy( copy.m_controllablePolicy ), + m_controllableAcl( copy.m_controllableAcl ), + m_versionable( copy.m_versionable ), + m_contentStreamAllowed( copy.m_contentStreamAllowed ), + m_propertiesTypes( copy.m_propertiesTypes ) + { + } + + ObjectType& ObjectType::operator=( const ObjectType& copy ) + { + if ( this != © ) + { + m_refreshTimestamp = copy.m_refreshTimestamp; + m_id = copy.m_id; + m_localName = copy.m_localName; + m_localNamespace = copy.m_localNamespace; + m_displayName = copy.m_displayName; + m_queryName = copy.m_queryName; + m_description = copy.m_description; + m_parentTypeId = copy.m_parentTypeId; + m_baseTypeId = copy.m_baseTypeId; + m_creatable = copy.m_creatable; + m_fileable = copy.m_fileable; + m_queryable = copy.m_queryable; + m_fulltextIndexed = copy.m_fulltextIndexed; + m_includedInSupertypeQuery = copy.m_includedInSupertypeQuery; + m_controllablePolicy = copy.m_controllablePolicy; + m_controllableAcl = copy.m_controllableAcl; + m_versionable = copy.m_versionable; + m_contentStreamAllowed = copy.m_contentStreamAllowed; + m_propertiesTypes = copy.m_propertiesTypes; + } + return *this; + } + + time_t ObjectType::getRefreshTimestamp( ) const + { + return m_refreshTimestamp; + } + + string ObjectType::getId( ) const + { + return m_id; + } + + string ObjectType::getLocalName( ) const + { + return m_localName; + } + + string ObjectType::getLocalNamespace( ) const + { + return m_localNamespace; + } + + string ObjectType::getDisplayName( ) const + { + return m_displayName; + } + + string ObjectType::getQueryName( ) const + { + return m_queryName; + } + + string ObjectType::getDescription( ) const + { + return m_description; + } + + string ObjectType::getParentTypeId( ) const + { + return m_parentTypeId; + } + + string ObjectType::getBaseTypeId( ) const + { + return m_baseTypeId; + } + + bool ObjectType::isCreatable( ) const + { + return m_creatable; + } + + bool ObjectType::isFileable( ) const + { + return m_fileable; + } + + bool ObjectType::isQueryable( ) const + { + return m_queryable; + } + + bool ObjectType::isFulltextIndexed( ) const + { + return m_fulltextIndexed; + } + + bool ObjectType::isIncludedInSupertypeQuery( ) const + { + return m_includedInSupertypeQuery; + } + + bool ObjectType::isControllablePolicy( ) const + { + return m_controllablePolicy; + } + + bool ObjectType::isControllableACL( ) const + { + return m_controllableAcl; + } + + bool ObjectType::isVersionable( ) const + { + return m_versionable; + } + + ObjectType::ContentStreamAllowed ObjectType::getContentStreamAllowed( ) const + { + return m_contentStreamAllowed; + } + + map< string, PropertyTypePtr >& ObjectType::getPropertiesTypes( ) + { + return m_propertiesTypes; + } + + + void ObjectType::initializeFromNode( xmlNodePtr typeNode ) + { + if ( typeNode != NULL ) + { + for ( xmlNodePtr child = typeNode->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( const char * ) content, xmlStrlen( content ) ); + + if ( xmlStrEqual( child->name, BAD_CAST( "id" ) ) ) + m_id = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "localName" ) ) ) + m_localName = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "localNamespace" ) ) ) + m_localNamespace = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "displayName" ) ) ) + m_displayName = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "queryName" ) ) ) + m_queryName = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "description" ) ) ) + m_description = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "baseId" ) ) ) + m_baseTypeId = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "parentId" ) ) ) + m_parentTypeId = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "creatable" ) ) ) + m_creatable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "fileable" ) ) ) + m_fileable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "queryable" ) ) ) + m_queryable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "fulltextIndexed" ) ) ) + m_fulltextIndexed = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "includedInSupertypeQuery" ) ) ) + m_includedInSupertypeQuery = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "controllablePolicy" ) ) ) + m_controllablePolicy = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "controllableACL" ) ) ) + m_controllableAcl = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "versionable" ) ) ) + m_versionable = libcmis::parseBool( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "contentStreamAllowed" ) ) ) + { + libcmis::ObjectType::ContentStreamAllowed streamAllowed = libcmis::ObjectType::Allowed; + if ( value == "notallowed" ) + streamAllowed = libcmis::ObjectType::NotAllowed; + else if ( value == "required" ) + streamAllowed = libcmis::ObjectType::Required; + + m_contentStreamAllowed = streamAllowed; + } + else + { + libcmis::PropertyTypePtr type( new libcmis::PropertyType( child ) ); + m_propertiesTypes[ type->getId() ] = type; + } + + xmlFree( content ); + } + } + m_refreshTimestamp = time( NULL ); + } + } + + void ObjectType::refresh( ) + { + throw Exception( "ObjectType::refresh() shouldn't be called" ); + } + + ObjectTypePtr ObjectType::getParentType( ) + { + throw Exception( "ObjectType::getParentType() shouldn't be called" ); + } + + ObjectTypePtr ObjectType::getBaseType( ) + { + throw Exception( "ObjectType::getBaseType() shouldn't be called" ); + } + + vector< ObjectTypePtr > ObjectType::getChildren( ) + { + throw Exception( "ObjectType::getChildren() shouldn't be called" ); + } + + // LCOV_EXCL_START + string ObjectType::toString( ) + { + stringstream buf; + + buf << "Type Description:" << endl << endl; + buf << "Id: " << getId( ) << endl; + buf << "Display name: " << getDisplayName( ) << endl; + + buf << "Parent type: " << m_parentTypeId << endl; + buf << "Base type: " << m_baseTypeId << endl; + buf << "Children types [(id) Name]: " << endl; + try + { + vector< libcmis::ObjectTypePtr > children = getChildren( ); + for ( vector< libcmis::ObjectTypePtr >::iterator it = children.begin(); it != children.end(); ++it ) + { + libcmis::ObjectTypePtr type = *it; + buf << " (" << type->getId( ) << ")\t" << type->getDisplayName( ) << endl; + } + } + catch ( const libcmis::Exception& ) + { + // Ignore it + } + + buf << "Creatable: " << isCreatable( ) << endl; + buf << "Fileable: " << isFileable( ) << endl; + buf << "Queryable: " << isQueryable( ) << endl; + buf << "Full text indexed: " << isFulltextIndexed( ) << endl; + buf << "Included in supertype query: " << isIncludedInSupertypeQuery( ) << endl; + buf << "Controllable policy: " << isControllablePolicy( ) << endl; + buf << "Controllable ACL: " << isControllableACL( ) << endl; + buf << "Versionable: " << isVersionable( ) << endl; + + buf << "Property Definitions [RO/RW (id) Name]: " << endl; + map< string, libcmis::PropertyTypePtr > propsTypes = getPropertiesTypes( ); + for ( map< string, libcmis::PropertyTypePtr >::iterator it = propsTypes.begin( ); it != propsTypes.end( ); ++it ) + { + libcmis::PropertyTypePtr propType = it->second; + string updatable( "RO" ); + if ( propType->isUpdatable( ) ) + updatable = string( "RW" ); + + buf << " " << updatable << "\t (" << propType->getId( ) << ")\t" + << propType->getDisplayName( ) << endl; + } + + return buf.str(); + } + // LCOV_EXCL_STOP +} diff --git a/src/libcmis/object.cxx b/src/libcmis/object.cxx new file mode 100644 index 0000000..cd67f76 --- /dev/null +++ b/src/libcmis/object.cxx @@ -0,0 +1,398 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/object.hxx> + +#include <algorithm> + +#include <libcmis/session.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + Object::Object( Session* session ) : + m_session( session ), + m_typeDescription( ), + m_refreshTimestamp( 0 ), + m_typeId( ), + m_properties( ), + m_allowableActions( ), + m_renditions( ) + { + } + + Object::Object( Session* session, xmlNodePtr node ) : + m_session( session ), + m_typeDescription( ), + m_refreshTimestamp( 0 ), + m_typeId( ), + m_properties( ), + m_allowableActions( ), + m_renditions( ) + { + initializeFromNode( node ); + } + + Object::Object( const Object& copy ) : + m_session( copy.m_session ), + m_typeDescription( copy.m_typeDescription ), + m_refreshTimestamp( copy.m_refreshTimestamp ), + m_typeId( copy.m_typeId ), + m_properties( copy.m_properties ), + m_allowableActions( copy.m_allowableActions ), + m_renditions( copy.m_renditions ) + { + } + + Object& Object::operator=( const Object& copy ) + { + if ( this != © ) + { + m_session = copy.m_session; + m_typeDescription = copy.m_typeDescription; + m_refreshTimestamp = copy.m_refreshTimestamp; + m_typeId = copy.m_typeId; + m_properties = copy.m_properties; + m_allowableActions = copy.m_allowableActions; + m_renditions = copy.m_renditions; + } + + return *this; + } + + void Object::initializeFromNode( xmlNodePtr node ) + { + // Even if node is NULL we'll have an empty doc, so no need + // to worry about it. + xmlDocPtr doc = wrapInDoc( node ); + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + + libcmis::registerNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + // Get the allowableActions + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( "//cmis:allowableActions" ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlNodePtr actionsNode = xpathObj->nodesetval->nodeTab[0]; + m_allowableActions.reset( new libcmis::AllowableActions( actionsNode ) ); + } + xmlXPathFreeObject( xpathObj ); + + // TODO Get rid of this request: + // * Too time consuming + // * Makes secondary aspect properties annoying to create + // * Prevents from getting Alfresco additional properties + // First get the type id as it will give us the property definitions + string typeIdReq( "/*/cmis:properties/cmis:propertyId[@propertyDefinitionId='cmis:objectTypeId']/cmis:value/text()" ); + m_typeId = libcmis::getXPathValue( xpathCtx, typeIdReq ); + + string propertiesReq( "/*/cmis:properties/*" ); + xpathObj = xmlXPathEvalExpression( BAD_CAST( propertiesReq.c_str() ), xpathCtx ); + if ( NULL != xpathObj && NULL != xpathObj->nodesetval ) + { + int size = xpathObj->nodesetval->nodeNr; + for ( int i = 0; i < size; i++ ) + { + xmlNodePtr propertyNode = xpathObj->nodesetval->nodeTab[i]; + libcmis::PropertyPtr property = libcmis::parseProperty( propertyNode, getTypeDescription( ) ); + if ( property != NULL ) + m_properties[ property->getPropertyType( )->getId() ] = property; + } + } + xmlXPathFreeObject( xpathObj ); + } + + xmlXPathFreeContext( xpathCtx ); + xmlFreeDoc( doc ); + + m_refreshTimestamp = time( NULL ); + } + + string Object::getStringProperty( const string& propertyName ) + { + string name; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + name = it->second->getStrings( ).front( ); + return name; + } + + string Object::getId( ) + { + return getStringProperty( "cmis:objectId" ); + } + + string Object::getName( ) + { + return getStringProperty( "cmis:name" ); + } + + string Object::getBaseType( ) + { + return getStringProperty( "cmis:baseTypeId" ); + } + + string Object::getType( ) + { + string value = getStringProperty( "cmis:objectTypeId" ); + if ( value.empty( ) ) + value = m_typeId; + return value; + } + + string Object::getCreatedBy( ) + { + return getStringProperty( "cmis:createdBy" ); + } + + string Object::getLastModifiedBy( ) + { + return getStringProperty( "cmis:lastModifiedBy" ); + } + + string Object::getChangeToken( ) + { + return getStringProperty( "cmis:changeToken" ); + } + + vector< string > Object::getPaths( ) + { + return vector< string > ( ); + } + + boost::posix_time::ptime Object::getCreationDate( ) + { + boost::posix_time::ptime value; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:creationDate" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getDateTimes( ).empty( ) ) + value = it->second->getDateTimes( ).front( ); + return value; + } + + boost::posix_time::ptime Object::getLastModificationDate( ) + { + boost::posix_time::ptime value; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:lastModificationDate" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getDateTimes( ).empty( ) ) + value = it->second->getDateTimes( ).front( ); + return value; + } + + bool Object::isImmutable( ) + { + bool value = false; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:isImmutable" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getBools( ).empty( ) ) + value = it->second->getBools( ).front( ); + return value; + } + + vector< string > Object::getSecondaryTypes( ) + { + vector< string > types; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:secondaryObjectTypeIds" ) ); + if ( it != getProperties( ).end( ) && it->second != NULL ) + types = it->second->getStrings( ); + + return types; + } + + ObjectPtr Object::addSecondaryType( string id, PropertyPtrMap properties ) + { + // First make sure the cmis:secondaryObjectTypeIds property can be defined + map< string, PropertyTypePtr >& propertyTypes = getTypeDescription( )-> + getPropertiesTypes(); + + map< string, PropertyTypePtr >::iterator it = propertyTypes.find( "cmis:secondaryObjectTypeIds" ); + if ( it == propertyTypes.end() ) + throw ( Exception( "Secondary Types not supported", "constraint" ) ); + + // Copy all the new properties without checking they are + // defined in the secondary type definition: that would + // require one more HTTP request and the server will complain + // anyway if it's not good. + PropertyPtrMap newProperties( properties ); + + // Prepare the new cmis:secondaryObjectTypeIds property + vector< string > secTypes = getSecondaryTypes( ); + if ( find( secTypes.begin(), secTypes.end(), id ) == secTypes.end( ) ) + { + secTypes.push_back( id ); + PropertyPtr newSecTypes( new Property( it->second, secTypes ) ); + newProperties["cmis:secondaryObjectTypeIds"] = newSecTypes; + } + return updateProperties( newProperties ); + } + + ObjectPtr Object::removeSecondaryType( string id ) + { + // First make sure the cmis:secondaryObjectTypeIds property can be defined + map< string, PropertyTypePtr >& propertyTypes = getTypeDescription( )-> + getPropertiesTypes(); + + map< string, PropertyTypePtr >::iterator it = propertyTypes.find( "cmis:secondaryObjectTypeIds" ); + if ( it == propertyTypes.end() ) + throw ( Exception( "Secondary Types not supported", "constraint" ) ); + + // Prepare the new cmis:secondaryObjectTypeIds property + PropertyPtrMap newProperties; + vector< string > secTypes = getSecondaryTypes( ); + vector< string > newSecTypes; + for ( vector< string >::iterator idIt = secTypes.begin( ); + idIt != secTypes.end( ); ++idIt ) + { + if ( *idIt != id ) + newSecTypes.push_back( *idIt ); + } + + // No need to update the property if it didn't change + if ( newSecTypes.size( ) != secTypes.size( ) ) + { + PropertyPtr property ( new Property( it->second, newSecTypes ) ); + newProperties["cmis:secondaryObjectTypeIds"] = property; + } + + return updateProperties( newProperties ); + } + + PropertyPtrMap& Object::getProperties( ) + { + return m_properties; + } + + libcmis::ObjectTypePtr Object::getTypeDescription( ) + { + if ( !m_typeDescription.get( ) && m_session != NULL ) + m_typeDescription = m_session->getType( getType( ) ); + + return m_typeDescription; + } + + vector< RenditionPtr> Object::getRenditions( string /*filter*/ ) + { + return m_renditions; + } + + string Object::getThumbnailUrl( ) + { + string url; + vector< RenditionPtr > renditions = getRenditions( ); + for ( vector< RenditionPtr >::iterator it = renditions.begin( ); + it != renditions.end( ); ++it) + + { + if ( (*it)->getKind( ) == "cmis:thumbnail" ) return (*it)->getUrl( ); + } + + return url; + } + + // LCOV_EXCL_START + string Object::toString( ) + { + stringstream buf; + + buf << "Id: " << getId() << endl; + buf << "Name: " << getName() << endl; + buf << "Type: " << getType() << endl; + buf << "Base type: " << getBaseType() << endl; + buf << "Created on " << boost::posix_time::to_simple_string( getCreationDate() ) + << " by " << getCreatedBy() << endl; + buf << "Last modified on " << boost::posix_time::to_simple_string( getLastModificationDate() ) + << " by " << getLastModifiedBy() << endl; + buf << "Change token: " << getChangeToken() << endl; + + // Write Allowable Actions + if ( getAllowableActions( ) ) + buf << endl << getAllowableActions( )->toString( ) << endl; + + // Write remaining properties + static const char* skippedProps[] = { + "cmis:name", "cmis:baseTypeId", "cmis:objectTypeId", "cmis:createdBy", + "cmis:creationDate", "cmis:lastModifiedBy", "cmis:lastModificationDate", + "cmis::changeToken" + }; + int skippedCount = sizeof( skippedProps ) / sizeof( char* ); + + for ( PropertyPtrMap::iterator it = getProperties( ).begin(); + it != getProperties( ).end( ); ++it ) + { + string propId = it->first; + bool toSkip = false; + for ( int i = 0; i < skippedCount && !toSkip; ++i ) + { + toSkip = propId == skippedProps[i]; + } + + if ( !toSkip ) + { + libcmis::PropertyPtr prop = it->second; + if ( prop != NULL && prop->getPropertyType( ) != NULL ) + { + buf << prop->getPropertyType( )->getDisplayName( ) << "( " << prop->getPropertyType()->getId( ) << " ): " << endl; + vector< string > strValues = prop->getStrings( ); + for ( vector< string >::iterator valueIt = strValues.begin( ); + valueIt != strValues.end( ); ++valueIt ) + { + buf << "\t" << *valueIt << endl; + } + } + } + } + + vector< libcmis::RenditionPtr > renditions = getRenditions( ); + if ( !renditions.empty() ) + { + buf << "Renditions: " << endl; + for ( vector< libcmis::RenditionPtr >::iterator it = renditions.begin(); + it != renditions.end(); ++it ) + { + buf << ( *it )->toString( ) << endl; + } + } + + return buf.str(); + } + // LCOV_EXCL_STOP + + void Object::toXml( xmlTextWriterPtr writer ) + { + // Output the properties + xmlTextWriterStartElement( writer, BAD_CAST( "cmis:properties" ) ); + for ( PropertyPtrMap::iterator it = getProperties( ).begin( ); + it != getProperties( ).end( ); ++it ) + { + it->second->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + } +} diff --git a/src/libcmis/onedrive-allowable-actions.hxx b/src/libcmis/onedrive-allowable-actions.hxx new file mode 100644 index 0000000..fbdba9c --- /dev/null +++ b/src/libcmis/onedrive-allowable-actions.hxx @@ -0,0 +1,102 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Varga Mihai <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_ALLOWABLE_ACTIONS_HXX_ +#define _ONEDRIVE_ALLOWABLE_ACTIONS_HXX_ + +#include <libcmis/allowable-actions.hxx> + +class OneDriveAllowableActions: public libcmis::AllowableActions +{ + public: + OneDriveAllowableActions( bool isFolder ) : AllowableActions( ) + { + m_states.clear( ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::UpdateProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectRelationships, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectParents, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::MoveObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateRelationship, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyPolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAppliedPolicies, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemovePolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetACL, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyACL, false ) ); + + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderTree, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderParent, isFolder) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetDescendants, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CancelCheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckIn, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::SetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAllVersions, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::AddObjectToFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemoveObjectFromFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetRenditions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetChildren, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateDocument, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateFolder, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteTree, isFolder ) ); + } +}; + +#endif diff --git a/src/libcmis/onedrive-document.cxx b/src/libcmis/onedrive-document.cxx new file mode 100644 index 0000000..863a92f --- /dev/null +++ b/src/libcmis/onedrive-document.cxx @@ -0,0 +1,177 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-document.hxx" + +#include <libcmis/rendition.hxx> + +#include "onedrive-folder.hxx" +#include "onedrive-session.hxx" +#include "onedrive-utils.hxx" +#include "json-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveDocument::OneDriveDocument( OneDriveSession* session ) : + libcmis::Object( session), + libcmis::Document( session ), + OneDriveObject( session ) +{ +} + +OneDriveDocument::OneDriveDocument( OneDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session), + libcmis::Document( session ), + OneDriveObject( session, json, id, name ) +{ +} + +OneDriveDocument::~OneDriveDocument( ) +{ +} + +vector< libcmis::FolderPtr > OneDriveDocument::getParents( ) +{ + vector< libcmis::FolderPtr > parents; + + string parentId = getStringProperty( "cmis:parentId" ); + + libcmis::ObjectPtr obj = getSession( )->getObject( parentId ); + libcmis::FolderPtr parent = boost::dynamic_pointer_cast< libcmis::Folder >( obj ); + parents.push_back( parent ); + return parents; +} + +boost::shared_ptr< istream > OneDriveDocument::getContentStream( string /*streamId*/ ) +{ + boost::shared_ptr< istream > stream; + string streamUrl = getStringProperty( "source" ); + if ( streamUrl.empty( ) ) + throw libcmis::Exception( "could not find stream url" ); + + try + { + stream = getSession( )->httpGetRequest( streamUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return stream; +} + +void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os, + string /*contentType*/, + string fileName, + bool bReplaceExisting ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + string metaUrl = getUrl( ); + + // Update file name meta information + if ( bReplaceExisting && !fileName.empty( ) && fileName != getContentFilename( ) ) + { + Json metaJson; + Json fileJson( fileName.c_str( ) ); + metaJson.add("name", fileJson ); + + std::istringstream is( metaJson.toString( ) ); + vector<string> headers; + headers.push_back( "Content-Type: application/json" ); + try + { + getSession()->httpPatchRequest( metaUrl, is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + } + + fileName = libcmis::escape( getStringProperty( "cmis:name" ) ); + string putUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + + getStringProperty( "cmis:parentId" ) + ":/" + + fileName + ":/content"; + + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector <string> headers; + try + { + getSession()->httpPutRequest( putUrl, *is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + throw libcmis::Exception( "Document content wasn't set for" + "some reason" ); + refresh( ); +} + +libcmis::DocumentPtr OneDriveDocument::checkOut( ) +{ + // OneDrive doesn't have CheckOut, so just return the same document here + // TODO: no longer true - onedrive now has checkout/checkin + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkout = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkout; +} + +void OneDriveDocument::cancelCheckout( ) +{ + // Don't do anything since we don't have CheckOut +} + +libcmis::DocumentPtr OneDriveDocument::checkIn( bool /*isMajor*/, + std::string /*comment*/, + const PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ) +{ + // OneDrive doesn't have CheckIn, so just upload the properties, + // the content stream and fetch the new document resource. + updateProperties( properties ); + setContentStream( stream, contentType, fileName ); + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkin = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkin; +} + +vector< libcmis::DocumentPtr > OneDriveDocument::getAllVersions( ) +{ + return vector< libcmis::DocumentPtr > ( ); +} diff --git a/src/libcmis/onedrive-document.hxx b/src/libcmis/onedrive-document.hxx new file mode 100644 index 0000000..d70cede --- /dev/null +++ b/src/libcmis/onedrive-document.hxx @@ -0,0 +1,75 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_DOCUMENT_HXX_ +#define _ONEDRIVE_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/rendition.hxx> + +#include "onedrive-object.hxx" +#include "json-utils.hxx" + +class OneDriveDocument : public libcmis::Document, public OneDriveObject +{ + public: + OneDriveDocument( OneDriveSession* session ); + + OneDriveDocument( OneDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + ~OneDriveDocument( ); + + std::string getType( ) { return std::string( "cmis:document" );} + std::string getBaseType( ) { return std::string( "cmis:document" );} + + virtual std::vector< libcmis::FolderPtr > getParents( ); + + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName, + bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + + virtual libcmis::DocumentPtr checkIn( bool isMajor, + std::string comment, + const std::map< std::string,libcmis::PropertyPtr >& + properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); +}; + +#endif diff --git a/src/libcmis/onedrive-folder.cxx b/src/libcmis/onedrive-folder.cxx new file mode 100644 index 0000000..c1980c8 --- /dev/null +++ b/src/libcmis/onedrive-folder.cxx @@ -0,0 +1,167 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-folder.hxx" + +#include "onedrive-document.hxx" +#include "onedrive-session.hxx" +#include "onedrive-property.hxx" +#include "onedrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveFolder::OneDriveFolder( OneDriveSession* session ): + libcmis::Object( session ), + libcmis::Folder( session ), + OneDriveObject( session ) +{ +} + +OneDriveFolder::OneDriveFolder( OneDriveSession* session, Json json ): + libcmis::Object( session ), + libcmis::Folder( session ), + OneDriveObject( session, json ) +{ +} + +OneDriveFolder::~OneDriveFolder( ) +{ +} + +vector< libcmis::ObjectPtr > OneDriveFolder::getChildren( ) +{ + vector< libcmis::ObjectPtr > children; + // TODO: limited to 200 items by default - to get more one would have to + // follow @odata.nextLink or change pagination size + string query = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children"; + + string res; + try + { + res = getSession( )->httpGetRequest( query )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["value"].getList( ); + + // Create children objects from Json objects + for(unsigned int i = 0; i < objs.size(); i++) + { + children.push_back( getSession( )->getObjectFromJson( objs[i] ) ); + } + + return children; +} + +libcmis::FolderPtr OneDriveFolder::createFolder( + const PropertyPtrMap& properties ) +{ + Json propsJson = OneDriveUtils::toOneDriveJson( properties ); + string uploadUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children"; + + std::istringstream is( propsJson.toString( ) ); + string response; + try + { + response = getSession()->httpPostRequest( uploadUrl, is, "application/json" ) + ->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( response ); + libcmis::FolderPtr folderPtr( new OneDriveFolder( getSession( ), jsonRes ) ); + + refresh( ); + return folderPtr; +} + +libcmis::DocumentPtr OneDriveFolder::createDocument( + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, + string /*contentType*/, string fileName ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + if (fileName.empty( ) ) + { + for ( PropertyPtrMap::const_iterator it = properties.begin( ) ; + it != properties.end( ) ; ++it ) + { + if ( it->first == "cmis:name" ) + { + fileName = it->second->toString( ); + } + } + } + + // TODO: limited to 4MB, larger uploads need dedicated UploadSession + fileName = libcmis::escape( fileName ); + string newDocUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + + getId( ) + ":/" + fileName + ":/content"; + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector< string > headers; + string res; + // this will only create the file and return it's id, name and source url + try + { + res = getSession( )->httpPutRequest( newDocUrl, *is, headers ) + ->getStream( )->str( ); + } + catch (const CurlException& e) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + DocumentPtr document( new OneDriveDocument( getSession( ), jsonRes ) ); + + // Upload the properties + ObjectPtr object = document->updateProperties( properties ); + document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + + refresh( ); + return document; +} + +vector< string > OneDriveFolder::removeTree( + bool /*allVersions*/, + libcmis::UnfileObjects::Type /*unfile*/, + bool /*continueOnError*/ ) +{ + remove( ); + // Nothing to return here + return vector< string >( ); +} diff --git a/src/libcmis/onedrive-folder.hxx b/src/libcmis/onedrive-folder.hxx new file mode 100644 index 0000000..fc47ab3 --- /dev/null +++ b/src/libcmis/onedrive-folder.hxx @@ -0,0 +1,64 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_FOLDER_HXX_ +#define _ONEDRIVE_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "onedrive-object.hxx" +#include "json-utils.hxx" + +class OneDriveFolder : public libcmis::Folder, public OneDriveObject +{ + public: + OneDriveFolder( OneDriveSession* session ); + OneDriveFolder( OneDriveSession* session, Json json ); + ~OneDriveFolder( ); + + std::string getType( ) { return std::string( "cmis:folder" );} + std::string getBaseType( ) { return std::string( "cmis:folder" );} + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( + const libcmis::PropertyPtrMap& properties ); + + virtual libcmis::DocumentPtr createDocument( + const libcmis::PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName ); + + virtual std::vector< std::string > removeTree( + bool allVersion = true, + libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/onedrive-object-type.cxx b/src/libcmis/onedrive-object-type.cxx new file mode 100644 index 0000000..a88aef8 --- /dev/null +++ b/src/libcmis/onedrive-object-type.cxx @@ -0,0 +1,118 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-object-type.hxx" + +OneDriveObjectType::OneDriveObjectType( const std::string& id ): ObjectType( ) +{ + m_id = id; + m_localName = "OneDrive Object Type"; + m_localNamespace = "OneDrive Object Type"; + m_displayName = "OneDrive Object Type"; + m_queryName = id; + m_description = "OneDrive Object Type"; + m_parentTypeId = ""; + m_baseTypeId = id; + m_creatable = true; + m_versionable = false; + m_fileable = true; + m_fulltextIndexed = ( id == "cmis:document" ); + m_queryable = true; + if ( id == "cmis:document" ) + m_contentStreamAllowed = libcmis::ObjectType::Allowed; + else + m_contentStreamAllowed = libcmis::ObjectType::NotAllowed; + + libcmis::PropertyTypePtr idType(new libcmis::PropertyType( ) ); + idType->setId( "cmis:objectTypeId" ); + idType->setType( libcmis::PropertyType::String ); + m_propertiesTypes[ idType->getId( ) ] = idType; + + // create PropertyTypes which are updatable. + + // name + libcmis::PropertyTypePtr nameType( new libcmis::PropertyType( ) ); + nameType->setId( "cmis:name" ); + nameType->setType( libcmis::PropertyType::String ); + nameType->setUpdatable( true ); + m_propertiesTypes[ nameType->getId( ) ] = nameType; + + // description + libcmis::PropertyTypePtr descriptionType( new libcmis::PropertyType( ) ); + descriptionType->setId( "cmis:description" ); + descriptionType->setType( libcmis::PropertyType::String ); + descriptionType->setUpdatable( true ); + m_propertiesTypes[ descriptionType->getId( ) ] = descriptionType; + + // modifiedDate + libcmis::PropertyTypePtr modifiedDateType( new libcmis::PropertyType( ) ); + modifiedDateType->setId( "cmis:lastModificationDate" ); + modifiedDateType->setType( libcmis::PropertyType::DateTime ); + modifiedDateType->setUpdatable( false ); + m_propertiesTypes[ modifiedDateType->getId( ) ] = modifiedDateType; + + // creationDate + libcmis::PropertyTypePtr creationDateType( new libcmis::PropertyType( ) ); + creationDateType->setId( "cmis:creationDate" ); + creationDateType->setType( libcmis::PropertyType::DateTime ); + creationDateType->setUpdatable( false ); + m_propertiesTypes[ creationDateType->getId( ) ] = creationDateType; + + + if ( id == "cmis:document" ) + { + // size + libcmis::PropertyTypePtr contentStreamLength( new libcmis::PropertyType( ) ); + contentStreamLength->setId( "cmis:contentStreamLength" ); + contentStreamLength->setType( libcmis::PropertyType::Integer ); + contentStreamLength->setUpdatable( false ); + m_propertiesTypes[ contentStreamLength->getId( ) ] = contentStreamLength; + + // streamFileName + libcmis::PropertyTypePtr streamFileNameType( new libcmis::PropertyType( ) ); + streamFileNameType->setId( "cmis:contentStreamFileName" ); + streamFileNameType->setType( libcmis::PropertyType::String ); + streamFileNameType->setUpdatable( true ); + m_propertiesTypes[ streamFileNameType->getId( ) ] = streamFileNameType; + } +} + +libcmis::ObjectTypePtr OneDriveObjectType::getParentType( ) +{ + libcmis::ObjectTypePtr parentTypePtr; + if ( m_parentTypeId != "" ) { + parentTypePtr.reset( new OneDriveObjectType( m_parentTypeId ) ); + } + return parentTypePtr; +} + +libcmis::ObjectTypePtr OneDriveObjectType::getBaseType( ) +{ + libcmis::ObjectTypePtr baseTypePtr( new OneDriveObjectType( m_baseTypeId ) ); + return baseTypePtr; +} diff --git a/src/libcmis/onedrive-object-type.hxx b/src/libcmis/onedrive-object-type.hxx new file mode 100644 index 0000000..34b65f6 --- /dev/null +++ b/src/libcmis/onedrive-object-type.hxx @@ -0,0 +1,46 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_OBJECT_TYPE_HXX_ +#define _ONEDRIVE_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "json-utils.hxx" + +class OneDriveObjectType: public libcmis::ObjectType +{ + public: + OneDriveObjectType( const std::string& id ); + + virtual libcmis::ObjectTypePtr getParentType( ); + + virtual libcmis::ObjectTypePtr getBaseType( ); +}; + +#endif diff --git a/src/libcmis/onedrive-object.cxx b/src/libcmis/onedrive-object.cxx new file mode 100644 index 0000000..8deb591 --- /dev/null +++ b/src/libcmis/onedrive-object.cxx @@ -0,0 +1,198 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-object.hxx" + +#include "onedrive-allowable-actions.hxx" +#include "onedrive-property.hxx" +#include "onedrive-repository.hxx" +#include "onedrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveObject::OneDriveObject( OneDriveSession* session ) : + libcmis::Object( session ) +{ +} + +OneDriveObject::OneDriveObject( OneDriveSession* session, Json json, string id, string name ) : + libcmis::Object( session ) +{ + initializeFromJson( json, id, name ); +} + +OneDriveObject::OneDriveObject( const OneDriveObject& copy ) : + libcmis::Object( copy ) +{ +} + +OneDriveObject& OneDriveObject::operator=( const OneDriveObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + return *this; +} + +void OneDriveObject::initializeFromJson ( Json json, string /*id*/, string /*name*/ ) +{ + Json::JsonObject objs = json.getObjects( ); + Json::JsonObject::iterator it; + PropertyPtr property; + bool isFolder = json["folder"].toString( ) != ""; + for ( it = objs.begin( ); it != objs.end( ); ++it) + { + property.reset( new OneDriveProperty( it->first, it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + if ( it->first == "name" && !isFolder ) + { + property.reset( new OneDriveProperty( "cmis:contentStreamFileName", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } else if ( it->first == "parentReference" ) { + if (it->second["id"].toString() != "") { + property.reset( new OneDriveProperty( "cmis:parentId", it->second["id"] ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + } + } + + m_refreshTimestamp = time( NULL ); + m_allowableActions.reset( new OneDriveAllowableActions( isFolder ) ); +} + +OneDriveSession* OneDriveObject::getSession( ) +{ + return dynamic_cast< OneDriveSession* > ( m_session ); +} + +void OneDriveObject::refreshImpl( Json json ) +{ + m_typeDescription.reset( ); + m_properties.clear( ); + initializeFromJson( json ); +} + +void OneDriveObject::refresh( ) +{ + string res; + try + { + res = getSession()->httpGetRequest( getUrl( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json json = Json::parse( res ); + refreshImpl( json ); +} + +void OneDriveObject::remove( bool /*allVersions*/ ) +{ + try + { + getSession( )->httpDeleteRequest( getUrl( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +string OneDriveObject::getUrl( ) +{ + return getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ); +} + +string OneDriveObject::getUploadUrl( ) +{ + return getUrl( ) + "/files"; +} + +vector< string> OneDriveObject::getMultiStringProperty( const string& propertyName ) +{ + vector< string > values; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + values = it->second->getStrings( ); + return values; +} + +libcmis::ObjectPtr OneDriveObject::updateProperties( + const PropertyPtrMap& properties ) +{ + // Make Json object from properties + Json json = OneDriveUtils::toOneDriveJson( properties ); + + istringstream is( json.toString( )); + + libcmis::HttpResponsePtr response; + try + { + vector< string > headers; + headers.push_back( "Content-Type: application/json" ); + response = getSession( )->httpPatchRequest( getUrl( ), is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + libcmis::ObjectPtr updated = getSession()->getObjectFromJson( jsonRes ); + + if ( updated->getId( ) == getId( ) ) + refreshImpl( jsonRes ); + + return updated; +} + +void OneDriveObject::move( FolderPtr /*source*/, FolderPtr destination ) +{ + Json destJson; + Json destId( destination->getId( ).c_str( ) ); + destJson.add( "destination", destId ); + + istringstream is( destJson.toString( ) ); + libcmis::HttpResponsePtr response; + try + { + string url = getUrl( ) + "?method=MOVE"; + response = getSession( )->httpPostRequest( url, is, "application/json" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + + refreshImpl( jsonRes ); +} diff --git a/src/libcmis/onedrive-object.hxx b/src/libcmis/onedrive-object.hxx new file mode 100644 index 0000000..8ad46b3 --- /dev/null +++ b/src/libcmis/onedrive-object.hxx @@ -0,0 +1,76 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_OBJECT_HXX_ +#define _ONEDRIVE_OBJECT_HXX_ + +#include <libcmis/object.hxx> + +#include "onedrive-session.hxx" +#include "json-utils.hxx" + +// Class representing an object for OneDrive protocol. +class OneDriveObject : public virtual libcmis::Object +{ + public: + OneDriveObject( OneDriveSession* session ); + + // Create a OneDrive document from Json properties. + OneDriveObject( OneDriveSession* session, Json json, + std::string id = std::string( ), + std::string name = std::string( ) ); + OneDriveObject( const OneDriveObject& copy ); + virtual ~OneDriveObject( ) { } + + OneDriveObject& operator=( const OneDriveObject& copy ); + + void initializeFromJson( Json json, std::string id = std::string( ), + std::string name = std::string( ) ); + + void refreshImpl( Json json ); + virtual void refresh( ); + virtual void remove( bool allVersions = true ); + + std::string getUrl( ); + std::string getUploadUrl( ); + std::vector< std::string > getMultiStringProperty( + const std::string& propertyName ); + + virtual boost::shared_ptr< Object > updateProperties( + const libcmis::PropertyPtrMap& properties ); + + virtual std::vector< libcmis::RenditionPtr> getRenditions( std::string /*filter = std::string( )*/ ) + {return std::vector< libcmis::RenditionPtr>( );} + + virtual void move( boost::shared_ptr< libcmis::Folder > source, + boost::shared_ptr< libcmis::Folder > destination ); + + protected: + OneDriveSession* getSession( ); + +}; +#endif diff --git a/src/libcmis/onedrive-property.cxx b/src/libcmis/onedrive-property.cxx new file mode 100644 index 0000000..a9aa72a --- /dev/null +++ b/src/libcmis/onedrive-property.cxx @@ -0,0 +1,78 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-property.hxx" + +#include <libcmis/property-type.hxx> + +#include "onedrive-utils.hxx" + +using namespace std; +using namespace libcmis; + +OneDriveProperty::OneDriveProperty( ) +{ +} + +OneDriveProperty::~OneDriveProperty( ) +{ +} + +OneDriveProperty::OneDriveProperty( const string& key, Json json ): + Property( ) +{ + PropertyTypePtr propertyType( new PropertyType( ) ); + string convertedKey = OneDriveUtils::toCmisKey( key ); + propertyType->setId( convertedKey ); + propertyType->setLocalName( convertedKey ); + propertyType->setLocalNamespace( convertedKey ); + propertyType->setQueryName( convertedKey ); + propertyType->setDisplayName( key ); + propertyType->setTypeFromJsonType( json.getStrType( ) ); + propertyType->setUpdatable( OneDriveUtils::checkUpdatable( key ) ); + propertyType->setMultiValued( OneDriveUtils::checkMultiValued( key ) ); + + setPropertyType( propertyType ); + + vector< string > values = OneDriveUtils::parseOneDriveProperty( key, json ); + setValues( values ); +} + +OneDriveProperty::OneDriveProperty( const OneDriveProperty& copy ) : + libcmis::Property( copy ) +{ +} + +OneDriveProperty& OneDriveProperty::operator=( const OneDriveProperty& copy ) +{ + if ( this != © ) + { + libcmis::Property::operator=( copy ); + } + return *this; +} diff --git a/src/libcmis/onedrive-property.hxx b/src/libcmis/onedrive-property.hxx new file mode 100644 index 0000000..9d45316 --- /dev/null +++ b/src/libcmis/onedrive-property.hxx @@ -0,0 +1,52 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _ONEDRIVE_PROPERTY_HXX_ +#define _ONEDRIVE_PROPERTY_HXX_ + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +// reference: http://msdn.microsoft.com/en-us/library/hh243648.aspx +class OneDriveProperty : public libcmis::Property +{ + public : + // Create a OneDrive Property from a Json property with its key + OneDriveProperty( const std::string& key, Json json); + ~OneDriveProperty( ); + OneDriveProperty( const OneDriveProperty& copy); + OneDriveProperty& operator=( const OneDriveProperty& copy ); + + // Check if the property is updatable + bool checkUpdatable( const std::string& key ); + private : + // Avoid calling default constructor + OneDriveProperty( ); +}; +#endif diff --git a/src/libcmis/onedrive-repository.cxx b/src/libcmis/onedrive-repository.cxx new file mode 100644 index 0000000..b01f5c2 --- /dev/null +++ b/src/libcmis/onedrive-repository.cxx @@ -0,0 +1,54 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#include "onedrive-repository.hxx" + +OneDriveRepository::OneDriveRepository( ) : + Repository( ) +{ + m_id = "OneDrive"; + m_name = "One Drive"; + m_description = "One Drive repository"; + m_productName = "One Drive"; + m_productVersion = "v5"; + m_rootId = "/me/drive/root"; + + m_capabilities[ ACL ] = "discover"; + m_capabilities[ AllVersionsSearchable ] = "true"; + m_capabilities[ Changes ] = "all"; + m_capabilities[ GetDescendants ] = "true"; + m_capabilities[ GetFolderTree ] = "true"; + m_capabilities[ OrderBy ] = "custom"; + m_capabilities[ Multifiling ] = "true"; + m_capabilities[ PWCSearchable ] = "true"; + m_capabilities[ PWCUpdatable ] = "true"; + m_capabilities[ Query ] = "bothcombined"; + m_capabilities[ Renditions ] = "read"; + m_capabilities[ Unfiling ] = "false"; + m_capabilities[ VersionSpecificFiling ] = "false"; + m_capabilities[ Join ] = "none"; +} diff --git a/src/libcmis/onedrive-repository.hxx b/src/libcmis/onedrive-repository.hxx new file mode 100644 index 0000000..e1a8dc4 --- /dev/null +++ b/src/libcmis/onedrive-repository.hxx @@ -0,0 +1,40 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_REPOSITORY_HXX_ +#define _ONEDRIVE_REPOSITORY_HXX_ + +#include <libcmis/repository.hxx> + +class OneDriveRepository: public libcmis::Repository +{ + public: + OneDriveRepository( ); +}; + +#endif + diff --git a/src/libcmis/onedrive-session.cxx b/src/libcmis/onedrive-session.cxx new file mode 100644 index 0000000..375cd2e --- /dev/null +++ b/src/libcmis/onedrive-session.cxx @@ -0,0 +1,207 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-session.hxx" + +#include "oauth2-handler.hxx" +#include "onedrive-object-type.hxx" +#include "onedrive-document.hxx" +#include "onedrive-folder.hxx" +#include "onedrive-object.hxx" +#include "onedrive-repository.hxx" + +using namespace std; + +OneDriveSession::OneDriveSession ( string baseUrl, + string username, + string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose ) : + BaseSession( baseUrl, string(), username, password, false, + libcmis::OAuth2DataPtr(), verbose ) + +{ + // Add the dummy repository + m_repositories.push_back( getRepository( ) ); + + if ( oauth2 && oauth2->isComplete( ) ){ + setOAuth2Data( oauth2 ); + } +} + +OneDriveSession::OneDriveSession() : + BaseSession() +{ +} + +OneDriveSession::~OneDriveSession() +{ +} + +void OneDriveSession::setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ) +{ + m_oauth2Handler = new OAuth2Handler( this, oauth2 ); + m_oauth2Handler->setOAuth2Parser( OAuth2Providers::getOAuth2Parser( getBindingUrl( ) ) ); + + oauth2Authenticate( ); +} + +void OneDriveSession::oauth2Authenticate() +{ + // treat the supplied password as refresh token + if (!m_password.empty()) + { + try + { + m_inOAuth2Authentication = true; + + m_oauth2Handler->setRefreshToken(m_password); + // Try to get new access tokens using the stored refreshtoken + m_oauth2Handler->refresh(); + m_inOAuth2Authentication = false; + } + catch (const CurlException &e) + { + m_inOAuth2Authentication = false; + // refresh token expired or invalid, trigger initial auth (that in turn will hit the fallback with copy'n'paste method) + BaseSession::oauth2Authenticate(); + } + } + else + { + BaseSession::oauth2Authenticate(); + } +} + +string OneDriveSession::getRefreshToken() { + return HttpSession::getRefreshToken(); +} + +libcmis::RepositoryPtr OneDriveSession::getRepository( ) +{ + // Return a dummy repository since OneDrive doesn't have that notion + libcmis::RepositoryPtr repo( new OneDriveRepository( ) ); + return repo; +} + +libcmis::ObjectPtr OneDriveSession::getObject( string objectId ) +{ + // Run the http request to get the properties definition + string res; + string objectLink = m_bindingUrl + "/me/drive/items/" + objectId; + if (objectId == getRootId()) + objectLink = m_bindingUrl + objectId; + try + { + res = httpGetRequest( objectLink )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + return getObjectFromJson( jsonRes ); +} + +libcmis::ObjectPtr OneDriveSession::getObjectFromJson( Json& jsonRes ) +{ + libcmis::ObjectPtr object; + if ( jsonRes["folder"].toString() != "" ) + { + object.reset( new OneDriveFolder( this, jsonRes ) ); + } + else if ( jsonRes["file"].toString() != "" ) + { + object.reset( new OneDriveDocument( this, jsonRes ) ); + } + else + { + object.reset( new OneDriveObject( this, jsonRes ) ); + } + return object; +} + +libcmis::ObjectPtr OneDriveSession::getObjectByPath( string path ) +{ + string res; + string objectQuery = m_bindingUrl + "/me/drive/root:" + libcmis::escape( path ); + try + { + res = httpGetRequest( objectQuery )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw libcmis::Exception( "No file could be found for path " + path + ": " + e.what() ); + } + Json jsonRes = Json::parse( res ); + return getObjectFromJson( jsonRes ); +} + +bool OneDriveSession::isAPathMatch( Json objectJson, string path ) +{ + string parentId = objectJson["parent_id"].toString( ); + string objectName = objectJson["name"].toString( ); + size_t pos = path.rfind("/"); + string pathName = path.substr( pos + 1, path.size( ) ); + string truncatedPath = path.substr( 0, pos ); + + if ( truncatedPath.empty( ) && parentId == "null" && objectName == pathName ) + { + // match + return true; + } + if ( truncatedPath.empty( ) || parentId == "null" || objectName != pathName ) + { + return false; + } + + string res; + string parentUrl = m_bindingUrl + "/" + parentId; + try + { + res = httpGetRequest( parentUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + return isAPathMatch( jsonRes, truncatedPath ); +} + +libcmis::ObjectTypePtr OneDriveSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new OneDriveObjectType( id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > OneDriveSession::getBaseTypes( ) +{ + vector< libcmis::ObjectTypePtr > types; + return types; +} diff --git a/src/libcmis/onedrive-session.hxx b/src/libcmis/onedrive-session.hxx new file mode 100644 index 0000000..3c30c04 --- /dev/null +++ b/src/libcmis/onedrive-session.hxx @@ -0,0 +1,75 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_SESSION_HXX_ +#define _ONEDRIVE_SESSION_HXX_ + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" +#include "json-utils.hxx" + +class OneDriveSession : public BaseSession +{ + public: + OneDriveSession( std::string baseUrl, + std::string username, + std::string password, + libcmis::OAuth2DataPtr oauth2, + bool verbose = false ); + + ~OneDriveSession ( ); + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string ) { return true; } + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + libcmis::ObjectPtr getObjectFromJson( Json& jsonRes ); + + bool isAPathMatch( Json objectJson, std::string path ); + + virtual std::string getRefreshToken(); + + private: + OneDriveSession( ); + OneDriveSession( const OneDriveSession& copy ) = delete; + OneDriveSession& operator=( const OneDriveSession& copy ) = delete; + + virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 ); + + void oauth2Authenticate( ); +}; + +#endif /* _ONEDRIVE_SESSION_HXX_ */ diff --git a/src/libcmis/onedrive-utils.cxx b/src/libcmis/onedrive-utils.cxx new file mode 100644 index 0000000..17ed324 --- /dev/null +++ b/src/libcmis/onedrive-utils.cxx @@ -0,0 +1,131 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "onedrive-utils.hxx" + +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +string OneDriveUtils::toCmisKey( const string& key ) +{ + string convertedKey; + if ( key == "id") + convertedKey = "cmis:objectId"; + else if ( key == "from" ) + convertedKey = "cmis:createdBy"; + else if ( key == "description" ) + convertedKey = "cmis:description"; + else if ( key == "createdDateTime" ) + convertedKey = "cmis:creationDate"; + else if ( key == "lastModifiedDateTime" ) + convertedKey = "cmis:lastModificationDate"; + else if ( key == "name" ) + convertedKey = "cmis:name"; + else if ( key == "size" ) + convertedKey = "cmis:contentStreamLength"; + else if ( key == "@microsoft.graph.downloadUrl" ) + convertedKey = "source"; + else convertedKey = key; + return convertedKey; +} + +string OneDriveUtils::toOneDriveKey( const string& key ) +{ + string convertedKey; + if ( key == "cmis:objectId") + convertedKey = "id"; + else if ( key == "cmis:createdBy" ) + convertedKey = "from"; + else if ( key == "cmis:creationDate" ) + convertedKey = "created_time"; + else if ( key == "cmis:description" ) + convertedKey = "description"; + else if ( key == "cmis:lastModificationDate" ) + convertedKey = "updated_time"; + else if ( key == "cmis:name" ) + convertedKey = "name"; + else if ( key == "cmis:contentStreamLength" ) + convertedKey = "file_size"; + else convertedKey = key; + return convertedKey; +} + +bool OneDriveUtils::checkUpdatable( const std::string& key) +{ + bool updatable = ( key == "name" || + key == "description" ); + return updatable; +} + +bool OneDriveUtils::checkMultiValued( const string& key ) +{ + bool bMultiValued = ( key == "from" || + key == "shared_with" ); + return bMultiValued; +} + +vector< string > OneDriveUtils::parseOneDriveProperty( string key, Json json ) +{ + vector< string > values; + if ( key == "from" ) + { + string ownerName = json["name"].toString( ); + values.push_back( ownerName); + } + else if ( key == "shared_with" ) + { + string sharedWith = json["access"].toString( ); + values.push_back( sharedWith ); + } + else values.push_back( json.toString( ) ); + return values; +} + +Json OneDriveUtils::toOneDriveJson( const PropertyPtrMap& properties ) +{ + Json propsJson; + + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + string key = toOneDriveKey( it->first ); + Json value( it->second ); + + // Convert the key back to the onedrive key + if ( checkUpdatable( key ) ) + { + propsJson.add( key, value ); + } + } + + return propsJson; +} diff --git a/src/libcmis/onedrive-utils.hxx b/src/libcmis/onedrive-utils.hxx new file mode 100644 index 0000000..eb9fa6e --- /dev/null +++ b/src/libcmis/onedrive-utils.hxx @@ -0,0 +1,60 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _ONEDRIVE_UTILS_HXX_ +#define _ONEDRIVE_UTILS_HXX_ + +#include <string> + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +class OneDriveUtils +{ + public : + + // Convert a OneDrive Property key to a CMIS key + static std::string toCmisKey( const std::string& key); + + // Convert a CMIS key to OneDrive key + static std::string toOneDriveKey( const std::string& key ); + + // Check if a property is updatable + static bool checkUpdatable( const std::string& key); + + // Check if a property has multiple values + static bool checkMultiValued( const std::string& key); + + // Parse a OneDrive property value to CMIS values + static std::vector< std::string > parseOneDriveProperty( std::string key, Json jsonValue ); + + // Convert CMIS properties to OneDrive properties + static Json toOneDriveJson( const libcmis::PropertyPtrMap& properties ); +}; + +#endif diff --git a/src/libcmis/property-type.cxx b/src/libcmis/property-type.cxx new file mode 100644 index 0000000..42af61d --- /dev/null +++ b/src/libcmis/property-type.cxx @@ -0,0 +1,254 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/property-type.hxx> + +#include <libcmis/object-type.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + PropertyType::PropertyType( ) : + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_type( String ), + m_xmlType( "String" ), + m_multiValued( false ), + m_updatable( false ), + m_inherited( false ), + m_required( false ), + m_queryable( false ), + m_orderable( false ), + m_openChoice( false ), + m_temporary( false ) + { + } + + PropertyType::PropertyType( xmlNodePtr node ) : + m_id( ), + m_localName( ), + m_localNamespace( ), + m_displayName( ), + m_queryName( ), + m_type( String ), + m_xmlType( "String" ), + m_multiValued( false ), + m_updatable( false ), + m_inherited( false ), + m_required( false ), + m_queryable( false ), + m_orderable( false ), + m_openChoice( false ), + m_temporary( false ) + { + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + string value( ( const char * ) content ); + xmlFree( content ); + + if ( xmlStrEqual( child->name, BAD_CAST( "id" ) ) ) + setId( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "localName" ) ) ) + setLocalName( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "localNamespace" ) ) ) + setLocalNamespace( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "displayName" ) ) ) + setDisplayName( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "queryName" ) ) ) + setQueryName( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "propertyType" ) ) ) + setTypeFromXml( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "cardinality" ) ) ) + setMultiValued( value == "multi" ); + else if ( xmlStrEqual( child->name, BAD_CAST( "updatability" ) ) ) + setUpdatable( value == "readwrite" ); + else if ( xmlStrEqual( child->name, BAD_CAST( "inherited" ) ) ) + setInherited( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "required" ) ) ) + setRequired( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "queryable" ) ) ) + setQueryable( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "orderable" ) ) ) + setOrderable( libcmis::parseBool( value ) ); + else if ( xmlStrEqual( child->name, BAD_CAST( "openChoice" ) ) ) + setOpenChoice( libcmis::parseBool( value ) ); + } + } + + PropertyType::PropertyType( const PropertyType& copy ) : + m_id ( copy.m_id ), + m_localName ( copy.m_localName ), + m_localNamespace ( copy.m_localNamespace ), + m_displayName ( copy.m_displayName ), + m_queryName ( copy.m_queryName ), + m_type ( copy.m_type ), + m_xmlType( copy.m_xmlType ), + m_multiValued ( copy.m_multiValued ), + m_updatable ( copy.m_updatable ), + m_inherited ( copy.m_inherited ), + m_required ( copy.m_required ), + m_queryable ( copy.m_queryable ), + m_orderable ( copy.m_orderable ), + m_openChoice ( copy.m_openChoice ), + m_temporary( copy.m_temporary ) + { + } + + PropertyType::PropertyType( string type, + string id, + string localName, + string displayName, + string queryName ) : + m_id ( id ), + m_localName ( localName ), + m_localNamespace ( ), + m_displayName ( displayName ), + m_queryName ( queryName ), + m_type ( ), + m_xmlType( type ), + m_multiValued( false ), + m_updatable( false ), + m_inherited( false ), + m_required( false ), + m_queryable( false ), + m_orderable( false ), + m_openChoice( false ), + m_temporary( true ) + { + setTypeFromXml( m_xmlType ); + } + + PropertyType& PropertyType::operator=( const PropertyType& copy ) + { + if ( this != © ) + { + m_id = copy.m_id; + m_localName = copy.m_localName; + m_localNamespace = copy.m_localNamespace; + m_displayName = copy.m_displayName; + m_queryName = copy.m_queryName; + m_type = copy.m_type; + m_xmlType = copy.m_xmlType; + m_multiValued = copy.m_multiValued; + m_updatable = copy.m_updatable; + m_inherited = copy.m_inherited; + m_required = copy.m_required; + m_queryable = copy.m_queryable; + m_orderable = copy.m_orderable; + m_openChoice = copy.m_openChoice; + m_temporary = copy.m_temporary; + } + + return *this; + } + + void PropertyType::setTypeFromJsonType( string jsonType ) + { + if ( jsonType == "json_bool" ) + m_type = Bool; + else if ( jsonType == "json_double" ) + m_type = Decimal; + else if ( jsonType == "json_int" ) + m_type = Integer; + else if ( jsonType == "json_datetime" ) + m_type = DateTime; + else m_type = String; + } + + void PropertyType::setTypeFromXml( string typeStr ) + { + // Default to string + m_xmlType = string( "String" ); + m_type = String; + + if ( typeStr == "datetime" ) + { + m_xmlType = string( "DateTime" ); + m_type = DateTime; + } + else if ( typeStr == "integer" ) + { + m_xmlType = string( "Integer" ); + m_type = Integer; + } + else if ( typeStr == "decimal" ) + { + m_xmlType = string( "Decimal" ); + m_type = Decimal; + } + else if ( typeStr == "boolean" ) + { + m_xmlType = string( "Boolean" ); + m_type = Bool; + } + // Special kinds of String + else if ( typeStr == "html" ) + m_xmlType = string( "Html" ); + else if ( typeStr == "id" ) + m_xmlType = string( "Id" ); + else if ( typeStr == "uri" ) + m_xmlType = string( "Uri" ); + } + + void PropertyType::update( vector< ObjectTypePtr > typesDefs ) + { + for ( vector< ObjectTypePtr >::iterator it = typesDefs.begin(); + it != typesDefs.end( ) && m_temporary; ++it ) + { + map< string, PropertyTypePtr >& propertyTypes = + ( *it )->getPropertiesTypes( ); + map< string, PropertyTypePtr >::iterator propIt = + propertyTypes.find( getId( ) ); + if ( propIt != propertyTypes.end() ) + { + PropertyTypePtr complete = propIt->second; + + m_localName = complete->m_localName; + m_localNamespace = complete->m_localNamespace; + m_displayName = complete->m_displayName; + m_queryName = complete->m_queryName; + m_type = complete->m_type; + m_xmlType = complete->m_xmlType; + m_multiValued = complete->m_multiValued; + m_updatable = complete->m_updatable; + m_inherited = complete->m_inherited; + m_required = complete->m_required; + m_queryable = complete->m_queryable; + m_orderable = complete->m_orderable; + m_openChoice = complete->m_openChoice; + m_temporary = false; + } + } + } +} diff --git a/src/libcmis/property.cxx b/src/libcmis/property.cxx new file mode 100644 index 0000000..41c181b --- /dev/null +++ b/src/libcmis/property.cxx @@ -0,0 +1,232 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/property.hxx> + +#include <boost/algorithm/string.hpp> + +#include <libcmis/object-type.hxx> +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + Property::Property( ): + m_propertyType( ), + m_strValues( ), + m_boolValues( ), + m_longValues( ), + m_doubleValues( ), + m_dateTimeValues( ) + { + } + + Property::Property( PropertyTypePtr propertyType, std::vector< std::string > strValues ) : + m_propertyType( propertyType ), + m_strValues( ), + m_boolValues( ), + m_longValues( ), + m_doubleValues( ), + m_dateTimeValues( ) + { + setValues( strValues ); + } + + void Property::setValues( vector< string > strValues ) + { + m_strValues = strValues; + m_boolValues.clear( ); + m_longValues.clear( ); + m_doubleValues.clear( ); + m_dateTimeValues.clear( ); + + for ( vector< string >::iterator it = strValues.begin(); it != strValues.end( ); ++it ) + { + try + { + // If no PropertyType was provided at construction time, use String + PropertyType::Type type = PropertyType::String; + if ( getPropertyType( ) != NULL ) + type = getPropertyType( )->getType( ); + + switch ( type ) + { + case PropertyType::Integer: + m_longValues.push_back( parseInteger( *it ) ); + break; + case PropertyType::Decimal: + m_doubleValues.push_back( parseDouble( *it ) ); + break; + case PropertyType::Bool: + m_boolValues.push_back( parseBool( *it ) ); + break; + case PropertyType::DateTime: + { + boost::posix_time::ptime time = parseDateTime( *it ); + if ( !time.is_not_a_date_time( ) ) + m_dateTimeValues.push_back( time ); + } + break; + default: + case PropertyType::String: + // Nothing to convert for strings + break; + } + } + catch( const Exception& ) + { + // Just ignore the unparsable values + } + } + } + + void Property::setPropertyType( PropertyTypePtr propertyType) + { + m_propertyType = propertyType; + } + void Property::toXml( xmlTextWriterPtr writer ) + { + // Don't write the property if we have no type for it. + if ( getPropertyType( ) != NULL ) + { + string xmlType = string( "cmis:property" ) + getPropertyType()->getXmlType( ); + xmlTextWriterStartElement( writer, BAD_CAST( xmlType.c_str( ) ) ); + + // Write the attributes + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "propertyDefinitionId" ), + "%s", BAD_CAST( getPropertyType()->getId( ).c_str( ) ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "localName" ), + "%s", BAD_CAST( getPropertyType()->getLocalName( ).c_str( ) ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "displayName" ), + "%s", BAD_CAST( getPropertyType()->getDisplayName( ).c_str( ) ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "queryName" ), + "%s", BAD_CAST( getPropertyType()->getQueryName( ).c_str( ) ) ); + + // Write the values + for ( vector< string >::iterator it = m_strValues.begin( ); it != m_strValues.end( ); ++it ) + { + xmlTextWriterWriteElement( writer, BAD_CAST( "cmis:value" ), BAD_CAST( it->c_str( ) ) ); + } + + xmlTextWriterEndElement( writer ); + } + } + + string Property::toString( ) + { + string res; + if ( getPropertyType( ) != NULL ) + { + for ( vector< string >::iterator it = m_strValues.begin( ); + it != m_strValues.end( ); ++it ) + { + res.append( *it ); + } + } + return res; + } + + PropertyPtr parseProperty( xmlNodePtr node, ObjectTypePtr objectType ) + { + PropertyPtr property; + + if ( node != NULL ) + { + // Get the property definition Id + string propDefinitionId; + try + { + propDefinitionId = getXmlNodeAttributeValue( node, "propertyDefinitionId" ); + } + catch ( const Exception& ) + { + } + + // Try to get the property type definition + PropertyTypePtr propType; + if ( !propDefinitionId.empty() && objectType ) + { + map< string, PropertyTypePtr >::iterator it = objectType->getPropertiesTypes( ).find( propDefinitionId ); + if ( it != objectType->getPropertiesTypes().end( ) ) + propType = it->second; + } + + // Try to construct a temporary type definition + if ( !propDefinitionId.empty( ) && !propType ) + { + if ( node->name != NULL ) + { + string localName = getXmlNodeAttributeValue( node, + "localName", "" ); + string displayName = getXmlNodeAttributeValue( node, + "displayName", "" ); + string queryName = getXmlNodeAttributeValue( node, + "queryName", "" ); + + string xmlType( ( char * )node->name ); + string propStr( "property" ); + size_t pos = xmlType.find( propStr ); + if ( pos == 0 ) { + xmlType = xmlType.substr( propStr.length( ) ); + boost::to_lower( xmlType ); + } + + propType.reset( new PropertyType( xmlType, propDefinitionId, + localName, displayName, + queryName ) ); + } + } + + if ( propType ) + { + try + { + // Find the value nodes + vector< string > values; + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "value" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + values.push_back( string( ( char * ) content ) ); + xmlFree( content ); + } + } + property.reset( new Property( propType, values ) ); + } + catch ( const Exception& ) + { + // Ignore that non-property node + } + } + } + + return property; + } +} diff --git a/src/libcmis/rendition.cxx b/src/libcmis/rendition.cxx new file mode 100644 index 0000000..c68c053 --- /dev/null +++ b/src/libcmis/rendition.cxx @@ -0,0 +1,195 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2013 Cao Cuong Ngo <cao.cuong.ngo@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/rendition.hxx> + +#include <sstream> + +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis{ + +Rendition::Rendition( ): + m_streamId( ), + m_mimeType( ), + m_kind( ), + m_href( ), + m_title( ), + m_length( -1 ), + m_width ( -1 ), + m_height( -1 ), + m_renditionDocumentId( ) +{ +} + +Rendition::Rendition( string streamId, string mimeType, + string kind, string href, string title, long length, + long width, long height, string renditionDocumentId ): + m_streamId( streamId ), + m_mimeType( mimeType ), + m_kind( kind ), + m_href( href ), + m_title( title ), + m_length( length ), + m_width ( width ), + m_height( height ), + m_renditionDocumentId( renditionDocumentId ) +{ +} + +Rendition::Rendition( xmlNodePtr node ): + m_streamId( ), + m_mimeType( ), + m_kind( ), + m_href( ), + m_title( ), + m_length( -1 ), + m_width ( -1 ), + m_height( -1 ), + m_renditionDocumentId( ) +{ + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char * ) content ); + xmlFree( content ); + + if ( xmlStrEqual( child->name, BAD_CAST( "streamId" ) ) ) + m_streamId = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "mimetype" ) ) ) + m_mimeType = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "length" ) ) ) + m_length = libcmis::parseInteger( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "kind" ) ) ) + m_kind = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "title" ) ) ) + m_title = value; + else if ( xmlStrEqual( child->name, BAD_CAST( "height" ) ) ) + m_height = libcmis::parseInteger( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "width" ) ) ) + m_width = libcmis::parseInteger( value ); + else if ( xmlStrEqual( child->name, BAD_CAST( "renditionDocumentId" ) ) ) + m_renditionDocumentId = value; + } +} + +Rendition::~Rendition( ) +{ +} + +bool Rendition::isThumbnail( ) +{ + return m_kind == "cmis:thumbnail"; +} + + +const string& Rendition::getStreamId( ) const +{ + return m_streamId; +} + +const string& Rendition::getMimeType( ) const +{ + return m_mimeType; +} + +const string& Rendition::getKind( ) const +{ + return m_kind; +} + +const string& Rendition::getUrl( ) const +{ + return m_href; +} + +const string& Rendition::getTitle( ) const +{ + return m_title; +} + +long Rendition::getLength( ) const +{ + return m_length; +} + +long Rendition::getWidth( ) const +{ + return m_width; +} + +long Rendition::getHeight( ) const +{ + return m_height; +} + +const string& Rendition::getRenditionDocumentId( ) +{ + return m_renditionDocumentId; +} + + +// LCOV_EXCL_START +string Rendition::toString( ) +{ + stringstream buf; + + if ( !getStreamId( ).empty( ) ) + buf << " ID: " << getStreamId( ) << endl; + + if ( !getKind().empty() ) + buf << " Kind: " << getKind( ) << endl; + + if ( !getMimeType( ).empty() ) + buf << " MimeType: " << getMimeType( ) << endl; + + if ( !getUrl().empty( ) ) + buf << " URL: " << getUrl( ) << endl; + + if ( !getTitle().empty( ) ) + buf << " Title: " << getTitle( ) << endl; + + if ( getLength( ) >= 0 ) + buf << " Length: " << getLength( ) << endl; + + if ( getWidth( ) >= 0 ) + buf << " Width: " << getWidth( ) << endl; + + if ( getHeight( ) >= 0 ) + buf << " Height: " << getHeight( ) << endl; + + if ( !getRenditionDocumentId().empty( ) ) + buf << " Rendition Document ID: " << getRenditionDocumentId( ) << endl; + + return buf.str( ); +} +// LCOV_EXCL_STOP + +} diff --git a/src/libcmis/repository.cxx b/src/libcmis/repository.cxx new file mode 100644 index 0000000..0a7cbe6 --- /dev/null +++ b/src/libcmis/repository.cxx @@ -0,0 +1,294 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Cédric Bosdonnat <cbosdo@users.sourceforge.net> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/repository.hxx> + +#include <sstream> + +#include <libcmis/xml-utils.hxx> + +using namespace std; + +namespace libcmis +{ + Repository::Repository( ) : + m_id( ), + m_name( ), + m_description( ), + m_vendorName( ), + m_productName( ), + m_productVersion( ), + m_rootId( ), + m_cmisVersionSupported( ), + m_thinClientUri( ), + m_principalAnonymous( ), + m_principalAnyone( ), + m_capabilities( ) + { + } + + Repository::Repository( xmlNodePtr node ) : + m_id( ), + m_name( ), + m_description( ), + m_vendorName( ), + m_productName( ), + m_productVersion( ), + m_rootId( ), + m_cmisVersionSupported( ), + m_thinClientUri( ), + m_principalAnonymous( ), + m_principalAnyone( ), + m_capabilities( ) + { + initializeFromNode( node ); + } + + string Repository::getId( ) const + { + return m_id; + } + + string Repository::getName( ) const + { + return m_name; + } + + string Repository::getDescription( ) const + { + return m_description; + } + + string Repository::getVendorName( ) const + { + return m_vendorName; + } + + string Repository::getProductName( ) const + { + return m_productName; + } + + string Repository::getProductVersion( ) const + { + return m_productVersion; + } + + string Repository::getRootId( ) const + { + return m_rootId; + } + + string Repository::getCmisVersionSupported( ) const + { + return m_cmisVersionSupported; + } + + boost::shared_ptr< string > Repository::getThinClientUri( ) const + { + return m_thinClientUri; + } + + boost::shared_ptr< string > Repository::getPrincipalAnonymous( ) const + { + return m_principalAnonymous; + } + + boost::shared_ptr< string > Repository::getPrincipalAnyone( ) const + { + return m_principalAnyone; + } + + + void Repository::initializeFromNode( xmlNodePtr node ) + { + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + string localName( ( char* ) child->name ); + + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char* )content ); + xmlFree( content ); + + if ( localName == "repositoryId" ) + m_id = value; + else if ( localName == "repositoryName" ) + m_name = value; + else if ( localName == "repositoryDescription" ) + m_description = value; + else if ( localName == "vendorName" ) + m_vendorName = value; + else if ( localName == "productName" ) + m_productName = value; + else if ( localName == "productVersion" ) + m_productVersion = value; + else if ( localName == "rootFolderId" ) + m_rootId = value; + else if ( localName == "cmisVersionSupported" ) + m_cmisVersionSupported = value; + else if ( localName == "thinClientURI" ) + m_thinClientUri.reset( new string( value ) ); + else if ( localName == "principalAnonymous" ) + m_principalAnonymous.reset( new string( value ) ); + else if ( localName == "principalAnyone" ) + m_principalAnyone.reset( new string( value ) ); + else if ( localName == "capabilities" ) + { + m_capabilities = parseCapabilities( child ); + } + } + } + + string Repository::getCapability( Capability capability ) const + { + string result; + + map< Capability, string >::const_iterator it = m_capabilities.find( capability ); + if ( it != m_capabilities.end() ) + result = it->second; + + return result; + } + + bool Repository::getCapabilityAsBool( Capability capability ) const + { + string value = getCapability( capability ); + bool result = false; + try + { + result = libcmis::parseBool( value ); + } + catch ( const Exception& ) + { + } + return result; + } + + // LCOV_EXCL_START + string Repository::toString( ) const + { + stringstream buf; + + buf << "Id: " << getId( ) << endl; + buf << "Name: " << getName( ) << endl; + buf << "Description: " << getDescription( ) << endl; + buf << "Vendor: " << getVendorName( ) << endl; + buf << "Product: " << getProductName( ) << " - version " << getProductVersion( ) << endl; + buf << "Root Id: " << getRootId( ) << endl; + buf << "Supported CMIS Version: " << getCmisVersionSupported( ) << endl; + if ( getThinClientUri( ) ) + buf << "Thin Client URI: " << *getThinClientUri( ) << endl; + if ( getPrincipalAnonymous( ) ) + buf << "Anonymous user: " << *getPrincipalAnonymous( ) << endl; + if ( getPrincipalAnyone( ) ) + buf << "Anyone user: " << *getPrincipalAnyone( ) << endl; + buf << endl; + buf << "Capabilities:" << endl; + + static string capabilitiesNames[] = + { + "ACL", + "AllVersionsSearchable", + "Changes", + "ContentStreamUpdatability", + "GetDescendants", + "GetFolderTree", + "OrderBy", + "Multifiling", + "PWCSearchable", + "PWCUpdatable", + "Query", + "Renditions", + "Unfiling", + "VersionSpecificFiling", + "Join" + }; + + for ( int i = ACL; i < Join; ++i ) + { + buf << "\t" << capabilitiesNames[i] << ": " << getCapability( ( Capability )i ) << endl; + } + + return buf.str(); + } + // LCOV_EXCL_STOP + + map< Repository::Capability, string > Repository::parseCapabilities( xmlNodePtr node ) + { + map< Capability, string > capabilities; + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + string localName( ( char* ) child->name ); + + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char* )content ); + xmlFree( content ); + + Capability capability = ACL; + bool ignore = false; + if ( localName == "capabilityACL" ) + capability = ACL; + else if ( localName == "capabilityAllVersionsSearchable" ) + capability = AllVersionsSearchable; + else if ( localName == "capabilityChanges" ) + capability = Changes; + else if ( localName == "capabilityContentStreamUpdatability" ) + capability = ContentStreamUpdatability; + else if ( localName == "capabilityGetDescendants" ) + capability = GetDescendants; + else if ( localName == "capabilityGetFolderTree" ) + capability = GetFolderTree; + else if ( localName == "capabilityOrderBy" ) + capability = OrderBy; + else if ( localName == "capabilityMultifiling" ) + capability = Multifiling; + else if ( localName == "capabilityPWCSearchable" ) + capability = PWCSearchable; + else if ( localName == "capabilityPWCUpdatable" ) + capability = PWCUpdatable; + else if ( localName == "capabilityQuery" ) + capability = Query; + else if ( localName == "capabilityRenditions" ) + capability = Renditions; + else if ( localName == "capabilityUnfiling" ) + capability = Unfiling; + else if ( localName == "capabilityVersionSpecificFiling" ) + capability = VersionSpecificFiling; + else if ( localName == "capabilityJoin" ) + capability = Join; + else + ignore = true; + + if ( !ignore ) + capabilities[capability] = value; + } + + return capabilities; + } +} diff --git a/src/libcmis/session-factory.cxx b/src/libcmis/session-factory.cxx new file mode 100644 index 0000000..47dc1c8 --- /dev/null +++ b/src/libcmis/session-factory.cxx @@ -0,0 +1,164 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/session-factory.hxx> + +#include "atom-session.hxx" +#include "gdrive-session.hxx" +#include "onedrive-session.hxx" +#include "sharepoint-session.hxx" +#include "ws-session.hxx" + +using namespace std; + +namespace libcmis +{ + CurlInitProtocolsFunction g_CurlInitProtocolsFunction = 0; + AuthProviderPtr SessionFactory::s_authProvider; + OAuth2AuthCodeProvider SessionFactory::s_oauth2AuthCodeProvider; + + string SessionFactory::s_proxy; + string SessionFactory::s_noProxy; + string SessionFactory::s_proxyUser; + string SessionFactory::s_proxyPass; + + CertValidationHandlerPtr SessionFactory::s_certValidationHandler; + + void SessionFactory::setCurlInitProtocolsFunction(CurlInitProtocolsFunction const initProtocols) + { + g_CurlInitProtocolsFunction = initProtocols; + } + + void SessionFactory::setProxySettings( string proxy, string noProxy, + string proxyUser, string proxyPass ) + { + SessionFactory::s_proxy = proxy; + SessionFactory::s_noProxy = noProxy; + SessionFactory::s_proxyUser = proxyUser; + SessionFactory::s_proxyPass = proxyPass; + } + + Session* SessionFactory::createSession( string bindingUrl, string username, + string password, string repository, bool noSslCheck, + libcmis::OAuth2DataPtr oauth2, bool verbose ) + { + Session* session = NULL; + + if ( !bindingUrl.empty( ) ) + { + // Try the special cases based on the binding URL + if ( bindingUrl == "https://www.googleapis.com/drive/v3" ) + { + session = new GDriveSession( bindingUrl, username, password, + oauth2, verbose ); + } + else if ( bindingUrl == "https://graph.microsoft.com/v1.0" ) + { + session = new OneDriveSession( bindingUrl, username, password, + oauth2, verbose); + } + else + { + libcmis::HttpResponsePtr response; + boost::shared_ptr< HttpSession> httpSession( + new HttpSession( username, password, + noSslCheck, oauth2, verbose, + g_CurlInitProtocolsFunction) ); + + try + { + response = httpSession->httpGetRequest( bindingUrl ); + } + catch ( const CurlException& ) + { + // Could be SharePoint - needs NTLM authentication + session = new SharePointSession( bindingUrl, username, + password, verbose ); + } + + // Try the CMIS cases: we need to autodetect the binding type + if ( session == NULL ) + { + try + { + session = new AtomPubSession( bindingUrl, repository, + *httpSession, response ); + } + catch ( const Exception& ) + { + } + } + + if ( session == NULL ) + { + // We couldn't get an AtomSession, we may have an URL for the WebService binding + try + { + session = new WSSession( bindingUrl, repository, + *httpSession, response ); + } + catch ( const Exception& ) + { + } + } + + if ( session == NULL ) + { + // Maybe the first request didn't throw an exception and the authentication + // succeeded so we need to double check for SharePoint + try + { + session = new SharePointSession( bindingUrl, + *httpSession, response ); + } + catch ( const Exception& ) + { + } + } + } + } + + return session; + } + + vector< RepositoryPtr > SessionFactory::getRepositories( string bindingUrl, + string username, string password, bool verbose ) + { + vector< RepositoryPtr > repos; + + Session* session = createSession( bindingUrl, username, password, + string(), false, OAuth2DataPtr(), verbose ); + if ( session != NULL ) + { + repos = session->getRepositories( ); + delete session; + } + + return repos; + } +} diff --git a/src/libcmis/sharepoint-allowable-actions.hxx b/src/libcmis/sharepoint-allowable-actions.hxx new file mode 100644 index 0000000..abc48fc --- /dev/null +++ b/src/libcmis/sharepoint-allowable-actions.hxx @@ -0,0 +1,102 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Varga Mihai <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_ALLOWABLE_ACTIONS_HXX_ +#define _SHAREPOINT_ALLOWABLE_ACTIONS_HXX_ + +#include <libcmis/allowable-actions.hxx> + +class SharePointAllowableActions: public libcmis::AllowableActions +{ + public: + SharePointAllowableActions( bool isFolder ) : AllowableActions( ) + { + m_states.clear( ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::UpdateProperties, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetProperties, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectRelationships, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetObjectParents, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::MoveObject, true ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateRelationship, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyPolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAppliedPolicies, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemovePolicy, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetACL, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::ApplyACL, false ) ); + + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderTree, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetFolderParent, isFolder) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetDescendants, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CancelCheckOut, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CheckIn, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::SetContentStream, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetAllVersions, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::AddObjectToFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::RemoveObjectFromFolder, !isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetRenditions, false ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::GetChildren, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateDocument, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::CreateFolder, isFolder ) ); + m_states.insert( std::pair< libcmis::ObjectAction::Type, bool> ( + libcmis::ObjectAction::DeleteTree, isFolder ) ); + } +}; + +#endif diff --git a/src/libcmis/sharepoint-document.cxx b/src/libcmis/sharepoint-document.cxx new file mode 100644 index 0000000..dec5527 --- /dev/null +++ b/src/libcmis/sharepoint-document.cxx @@ -0,0 +1,213 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-document.hxx" + +#include "sharepoint-session.hxx" +#include "sharepoint-utils.hxx" +#include "json-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointDocument::SharePointDocument( SharePointSession* session ) : + libcmis::Object( session), + libcmis::Document( session ), + SharePointObject( session ) +{ +} + +SharePointDocument::SharePointDocument( SharePointSession* session, Json json, string parentId, string name ) : + libcmis::Object( session), + libcmis::Document( session ), + SharePointObject( session, json, parentId, name ) +{ +} + +SharePointDocument::~SharePointDocument( ) +{ +} + +vector< libcmis::FolderPtr > SharePointDocument::getParents( ) +{ + vector< libcmis::FolderPtr > parents; + + string parentId = getStringProperty( "cmis:parentId" ); + + libcmis::ObjectPtr obj = getSession( )->getObject( parentId ); + libcmis::FolderPtr parent = boost::dynamic_pointer_cast< libcmis::Folder >( obj ); + parents.push_back( parent ); + return parents; +} + +boost::shared_ptr< istream > SharePointDocument::getContentStream( string /*streamId*/ ) +{ + boost::shared_ptr< istream > stream; + // file uri + /$value + string streamUrl = getId( ) + "/%24value"; + try + { + stream = getSession( )->httpGetRequest( streamUrl )->getStream( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return stream; +} + +void SharePointDocument::setContentStream( boost::shared_ptr< ostream > os, + string contentType, + string /*fileName*/, + bool /*overwrite*/ ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + // file uri + /$value + string putUrl = getId( ) + "/%24value"; + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + vector <string> headers; + headers.push_back( string( "Content-Type: " ) + contentType ); + try + { + getSession()->httpPutRequest( putUrl, *is, headers ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + long httpStatus = getSession( )->getHttpStatus( ); + if ( httpStatus < 200 || httpStatus >= 300 ) + { + throw libcmis::Exception( "Document content wasn't set for" + "some reason" ); + } + refresh( ); +} + +libcmis::DocumentPtr SharePointDocument::checkOut( ) +{ + istringstream is( "" ); + string url = getId( ) + "/checkout"; + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkout = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkout; +} + +void SharePointDocument::cancelCheckout( ) +{ + istringstream is( "" ); + string url = getId( ) + "/undocheckout"; + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +libcmis::DocumentPtr SharePointDocument::checkIn( bool isMajor, + std::string comment, + const PropertyPtrMap& /*properties*/, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ) +{ + setContentStream( stream, contentType, fileName ); + comment = libcmis::escape( comment ); + string url = getId( ) + "/checkin(comment='" + comment + "'"; + if ( isMajor ) + { + url += ",checkintype=1)"; + } + else + { + url += ",checkintype=0)"; + } + istringstream is( "" ); + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr checkin = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + return checkin; +} + +vector< libcmis::DocumentPtr > SharePointDocument::getAllVersions( ) +{ + string res; + string url = getStringProperty( "Versions" ); + vector< libcmis::DocumentPtr > allVersions; + try + { + res = getSession( )->httpGetRequest( url )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + // adding the latest version + libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) ); + libcmis::DocumentPtr doc = + boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + allVersions.push_back( doc ); + + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["d"]["results"].getList( ); + for ( unsigned int i = 0; i < objs.size( ); i++) + { + string versionNumber = objs[i]["ID"].toString( ); + string versionId = getId( ) + "/Versions(" + versionNumber + ")"; + obj = getSession( )->getObject( versionId ); + doc = boost::dynamic_pointer_cast< libcmis::Document > ( obj ); + allVersions.push_back( doc ); + } + + return allVersions; +} diff --git a/src/libcmis/sharepoint-document.hxx b/src/libcmis/sharepoint-document.hxx new file mode 100644 index 0000000..8423eb0 --- /dev/null +++ b/src/libcmis/sharepoint-document.hxx @@ -0,0 +1,72 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_DOCUMENT_HXX_ +#define _SHAREPOINT_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "sharepoint-object.hxx" +#include "json-utils.hxx" + +class SharePointDocument : public libcmis::Document, public SharePointObject +{ + public: + SharePointDocument( SharePointSession* session ); + + SharePointDocument( SharePointSession* session, Json json, + std::string parentId = std::string( ), + std::string name = std::string( ) ); + ~SharePointDocument( ); + + std::string getType( ) { return std::string( "cmis:document" );} + std::string getBaseType( ) { return std::string( "cmis:document" );} + + virtual std::vector< libcmis::FolderPtr > getParents( ); + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName, + bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + virtual libcmis::DocumentPtr checkIn( bool isMajor, + std::string comment, + const std::map< std::string,libcmis::PropertyPtr >& + properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); +}; + +#endif diff --git a/src/libcmis/sharepoint-folder.cxx b/src/libcmis/sharepoint-folder.cxx new file mode 100644 index 0000000..e4da4a3 --- /dev/null +++ b/src/libcmis/sharepoint-folder.cxx @@ -0,0 +1,205 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-folder.hxx" + +#include "sharepoint-document.hxx" +#include "sharepoint-session.hxx" +#include "sharepoint-property.hxx" +#include "sharepoint-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointFolder::SharePointFolder( SharePointSession* session ): + libcmis::Object( session ), + libcmis::Folder( session ), + SharePointObject( session ) +{ +} + +SharePointFolder::SharePointFolder( SharePointSession* session, Json json, string parentId ): + libcmis::Object( session ), + libcmis::Folder( session ), + SharePointObject( session, json, parentId ) +{ +} + +SharePointFolder::~SharePointFolder( ) +{ +} + +string SharePointFolder::getParentId( ) +{ + string parentId = getStringProperty( "cmis:parentId" ); + if ( parentId.empty( ) ) + { + string parentUrl = getStringProperty( "ParentFolder" ); + string res; + try + { + res = getSession( )->httpGetRequest( parentUrl )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + parentId = jsonRes["d"]["__metadata"]["uri"].toString( ); + PropertyPtr property; + property.reset( new SharePointProperty( "cmis:parentId", + Json( parentId.c_str( ) ) ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + return parentId; +} + +vector< libcmis::ObjectPtr > SharePointFolder::getChildren( ) +{ + vector< libcmis::ObjectPtr > children; + string filesUrl = getStringProperty( "Files" ); + string foldersUrl = getStringProperty( "Folders" ); + + Json::JsonVector objs = getChildrenImpl( filesUrl ); + Json::JsonVector folders = getChildrenImpl( foldersUrl ); + objs.insert( objs.begin( ), folders.begin( ), folders.end( ) ); + + for ( unsigned int i = 0; i < objs.size( ); i++) + { + children.push_back( getSession( )->getObjectFromJson( objs[i], getId( ) ) ); + } + return children; +} + +Json::JsonVector SharePointFolder::getChildrenImpl( string url ) +{ + string res; + try + { + res = getSession( )->httpGetRequest( url )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + Json::JsonVector objs = jsonRes["d"]["results"].getList( ); + return objs; +} + +libcmis::FolderPtr SharePointFolder::createFolder( const PropertyPtrMap& properties ) +{ + string folderName; + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + if ( it->first == "cmis:name" ) + { + folderName = it->second->toString( ); + } + } + // bindingUrl/folders/add('/path/to/folder') + string relativeUrl; + if ( getStringProperty( "ServerRelativeUrl" ) == "/" ) + { + relativeUrl = "/" + folderName; + } + else + { + relativeUrl = getStringProperty( "ServerRelativeUrl" ) + "/" + folderName; + } + relativeUrl = libcmis::escape( relativeUrl ); + string folderUrl = getSession( )->getBindingUrl( ); + folderUrl += "/folders/add('" + relativeUrl + "')"; + + istringstream is( "" ); + string res; + try + { + res = getSession( )->httpPostRequest( folderUrl, is, "" )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + + libcmis::FolderPtr newFolder( new SharePointFolder( getSession( ), jsonRes, getId( ) ) ); + return newFolder; +} + +libcmis::DocumentPtr SharePointFolder::createDocument( const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, + string contentType, + string fileName ) +{ + if ( !os.get( ) ) + throw libcmis::Exception( "Missing stream" ); + + if ( fileName.empty( ) ) + { + for ( PropertyPtrMap::const_iterator it = properties.begin() ; + it != properties.end() ; ++it ) + { + if ( it->first == "cmis:name" || + it->first == "cmis:contentStreamFileName" ) + { + fileName = it->second->toString( ); + } + } + } + fileName = libcmis::escape( fileName ); + string url = getId( ) + "/files/add(overwrite=true,"; + url += "url='" + fileName + "')"; + + // Upload stream + boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) ); + string res; + try + { + res = getSession( )->httpPostRequest( url, *is, contentType )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + Json jsonRes = Json::parse( res ); + DocumentPtr document( new SharePointDocument( getSession( ), jsonRes, getId( ) ) ); + return document; +} + +vector< string > SharePointFolder::removeTree( bool /*allVersions*/, + libcmis::UnfileObjects::Type /*unfile*/, + bool /*continueOnError*/ ) +{ + remove( ); + // Nothing to return here + return vector< string >( ); +} diff --git a/src/libcmis/sharepoint-folder.hxx b/src/libcmis/sharepoint-folder.hxx new file mode 100644 index 0000000..223ed60 --- /dev/null +++ b/src/libcmis/sharepoint-folder.hxx @@ -0,0 +1,67 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_FOLDER_HXX_ +#define _SHAREPOINT_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "sharepoint-object.hxx" +#include "json-utils.hxx" + +class SharePointFolder : public libcmis::Folder, public SharePointObject +{ + public: + SharePointFolder( SharePointSession* session ); + SharePointFolder( SharePointSession* session, + Json json, + std::string parentId = std::string( ) ); + ~SharePointFolder( ); + + std::string getType( ) { return std::string( "cmis:folder" );} + std::string getBaseType( ) { return std::string( "cmis:folder" );} + virtual std::string getParentId( ); + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + Json::JsonVector getChildrenImpl( std::string url ); + + virtual libcmis::FolderPtr createFolder( const libcmis::PropertyPtrMap& properties ); + + virtual libcmis::DocumentPtr createDocument( const libcmis::PropertyPtrMap& properties, + boost::shared_ptr< std::ostream > os, + std::string contentType, + std::string fileName ); + + virtual std::vector< std::string > removeTree( bool allVersion = true, + libcmis::UnfileObjects::Type + unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/sharepoint-object-type.cxx b/src/libcmis/sharepoint-object-type.cxx new file mode 100644 index 0000000..d6f405e --- /dev/null +++ b/src/libcmis/sharepoint-object-type.cxx @@ -0,0 +1,105 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-object-type.hxx" + +SharePointObjectType::SharePointObjectType( const std::string& id ): ObjectType( ) +{ + m_id = id; + m_localName = "SharePoint Object Type"; + m_localNamespace = "SharePoint Object Type"; + m_displayName = "SharePoint Object Type"; + m_queryName = "SharePoint Object Type"; + m_description = "SharePoint Object Type"; + m_parentTypeId = id; + m_baseTypeId = id; + m_creatable = true; + m_versionable = true; + m_fulltextIndexed = true; + + libcmis::PropertyTypePtr idType(new libcmis::PropertyType( ) ); + idType->setId( "cmis:objectTypeId" ); + idType->setType( libcmis::PropertyType::String ); + m_propertiesTypes[ idType->getId( ) ] = idType; + + // create PropertyTypes which are updatable. + + // name + libcmis::PropertyTypePtr nameType( new libcmis::PropertyType( ) ); + nameType->setId( "cmis:name" ); + nameType->setType( libcmis::PropertyType::String ); + nameType->setUpdatable( true ); + m_propertiesTypes[ nameType->getId( ) ] = nameType; + + // streamFileName + libcmis::PropertyTypePtr streamFileNameType( new libcmis::PropertyType( ) ); + streamFileNameType->setId( "cmis:contentStreamFileName" ); + streamFileNameType->setType( libcmis::PropertyType::String ); + streamFileNameType->setUpdatable( true ); + m_propertiesTypes[ streamFileNameType->getId( ) ] = streamFileNameType; + + // modifiedDate + libcmis::PropertyTypePtr modifiedDateType( new libcmis::PropertyType( ) ); + modifiedDateType->setId( "cmis:lastModificationDate" ); + modifiedDateType->setType( libcmis::PropertyType::DateTime ); + modifiedDateType->setUpdatable( false ); + m_propertiesTypes[ modifiedDateType->getId( ) ] = modifiedDateType; + + // creationDate + libcmis::PropertyTypePtr creationDateType( new libcmis::PropertyType( ) ); + creationDateType->setId( "cmis:creationDate" ); + creationDateType->setType( libcmis::PropertyType::DateTime ); + creationDateType->setUpdatable( false ); + m_propertiesTypes[ creationDateType->getId( ) ] = creationDateType; + + // size + libcmis::PropertyTypePtr contentStreamLength( new libcmis::PropertyType( ) ); + contentStreamLength->setId( "cmis:contentStreamLength" ); + contentStreamLength->setType( libcmis::PropertyType::Integer ); + contentStreamLength->setUpdatable( false ); + m_propertiesTypes[ contentStreamLength->getId( ) ] = contentStreamLength; + + // checkinComment + libcmis::PropertyTypePtr checkinComment( new libcmis::PropertyType( ) ); + checkinComment->setId( "cmis:checkinComment" ); + checkinComment->setType( libcmis::PropertyType::String ); + checkinComment->setUpdatable( false ); + m_propertiesTypes[ checkinComment->getId( ) ] = checkinComment; +} + +libcmis::ObjectTypePtr SharePointObjectType::getParentType( ) +{ + libcmis::ObjectTypePtr parentTypePtr( new SharePointObjectType( m_parentTypeId ) ); + return parentTypePtr; +} + +libcmis::ObjectTypePtr SharePointObjectType::getBaseType( ) +{ + libcmis::ObjectTypePtr baseTypePtr( new SharePointObjectType( m_baseTypeId ) ); + return baseTypePtr; +} diff --git a/src/libcmis/sharepoint-object-type.hxx b/src/libcmis/sharepoint-object-type.hxx new file mode 100644 index 0000000..8ea8e3d --- /dev/null +++ b/src/libcmis/sharepoint-object-type.hxx @@ -0,0 +1,46 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_OBJECT_TYPE_HXX_ +#define _SHAREPOINT_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "json-utils.hxx" + +class SharePointObjectType: public libcmis::ObjectType +{ + public: + SharePointObjectType( const std::string& id ); + + virtual libcmis::ObjectTypePtr getParentType( ); + + virtual libcmis::ObjectTypePtr getBaseType( ); +}; + +#endif diff --git a/src/libcmis/sharepoint-object.cxx b/src/libcmis/sharepoint-object.cxx new file mode 100644 index 0000000..1523205 --- /dev/null +++ b/src/libcmis/sharepoint-object.cxx @@ -0,0 +1,200 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-object.hxx" + +#include "sharepoint-allowable-actions.hxx" +#include "sharepoint-property.hxx" +#include "sharepoint-repository.hxx" +#include "sharepoint-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointObject::SharePointObject( SharePointSession* session ) : + libcmis::Object( session ) +{ +} + +SharePointObject::SharePointObject( SharePointSession* session, Json json, string parentId, string name ) : + libcmis::Object( session ) +{ + initializeFromJson( json, parentId, name ); +} + +SharePointObject::SharePointObject( const SharePointObject& copy ) : + libcmis::Object( copy ) +{ +} + +SharePointObject& SharePointObject::operator=( const SharePointObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + return *this; +} + +void SharePointObject::initializeFromJson ( Json json, string parentId, string /*name*/ ) +{ + if ( !json["d"].toString( ).empty( ) ) { + // Basic GET requests receive the data inside a "d" object, + // but child listing doesn't, so this unifies the representation + json = json["d"]; + } + Json::JsonObject objs = json.getObjects( ); + Json::JsonObject::iterator it; + PropertyPtr property; + bool isFolder = json["__metadata"]["type"].toString( ) == "SP.Folder"; + for ( it = objs.begin( ); it != objs.end( ); ++it) + { + property.reset( new SharePointProperty( it->first, it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + if ( it->first == "Name" && !isFolder ) + { + property.reset( new SharePointProperty( "cmis:contentStreamFileName", it->second ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + } + + if ( !parentId.empty( ) ) + { + // ParentId is not provided in the response + property.reset( new SharePointProperty( "cmis:parentId", Json( parentId.c_str( ) ) ) ); + m_properties[ property->getPropertyType( )->getId()] = property; + } + + if ( !isFolder ) + { + string authorUrl = getStringProperty( "Author" ); + if ( authorUrl.empty( ) ) + { + // it's a file version + authorUrl = getStringProperty( "CreatedBy" ); + } + Json authorJson = getSession( )->getJsonFromUrl( authorUrl ); + property.reset( new SharePointProperty( "cmis:createdBy", + authorJson["d"]["Title"] ) ); + m_properties[ property->getPropertyType( )->getId( ) ] = property; + } + else + { + // we need to get the creation and lastUpdate time which aren't + // provided in the response + Json propJson = getSession( )->getJsonFromUrl( getStringProperty( "Properties" ) ); + property.reset( new SharePointProperty( "cmis:creationDate", + propJson["d"]["vti_x005f_timecreated"] ) ); + m_properties[ property->getPropertyType( )->getId( ) ] = property; + + property.reset( new SharePointProperty( "cmis:lastModificationDate", + propJson["d"]["vti_x005f_timelastmodified"] ) ); + m_properties[ property->getPropertyType( )->getId( ) ] = property; + } + + m_refreshTimestamp = time( NULL ); + m_allowableActions.reset( new SharePointAllowableActions( isFolder ) ); +} + +SharePointSession* SharePointObject::getSession( ) +{ + return dynamic_cast< SharePointSession* > ( m_session ); +} + +void SharePointObject::refreshImpl( Json json ) +{ + m_typeDescription.reset( ); + m_properties.clear( ); + initializeFromJson( json ); +} + +void SharePointObject::refresh( ) +{ + string res; + try + { + res = getSession( )->httpGetRequest( getId( ) )->getStream( )->str( ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json json = Json::parse( res ); + refreshImpl( json ); +} + +void SharePointObject::remove( bool /*allVersions*/ ) +{ + try + { + getSession( )->httpDeleteRequest( getId( ) ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } +} + +vector< string> SharePointObject::getMultiStringProperty( const string& propertyName ) +{ + vector< string > values; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( propertyName ) ); + if ( it != getProperties( ).end( ) && it->second != NULL && !it->second->getStrings( ).empty( ) ) + values = it->second->getStrings( ); + return values; +} + +libcmis::ObjectPtr SharePointObject::updateProperties( + const PropertyPtrMap& /*properties*/ ) +{ + // there are no updateable properties so just return the same object + libcmis::ObjectPtr updated = getSession( )->getObject( getId( ) ); + return updated; +} + +void SharePointObject::move( FolderPtr /*source*/, FolderPtr destination ) +{ + if ( !getStringProperty( "cmis:checkinComment" ).empty( ) ) + { + // only documents can be moved and only documents have this property + string url = getId( ) + "/moveto(newurl='"; + url += libcmis::escape( destination->getStringProperty( "ServerRelativeUrl" ) ); + url += "/" + getStringProperty( "cmis:name" ) + "'"; + // overwrite flag + url += ",flags=1)"; + istringstream is( "" ); + try + { + getSession( )->httpPostRequest( url, is, "" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + refresh( ); + } +} diff --git a/src/libcmis/sharepoint-object.hxx b/src/libcmis/sharepoint-object.hxx new file mode 100644 index 0000000..ace581b --- /dev/null +++ b/src/libcmis/sharepoint-object.hxx @@ -0,0 +1,74 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_OBJECT_HXX_ +#define _SHAREPOINT_OBJECT_HXX_ + +#include <libcmis/object.hxx> + +#include "sharepoint-session.hxx" +#include "json-utils.hxx" + +// Class representing an object for SharePoint protocol. +class SharePointObject : public virtual libcmis::Object +{ + public: + SharePointObject( SharePointSession* session ); + + // Create a SharePoint document from Json properties. + SharePointObject( SharePointSession* session, Json json, + std::string parentId = std::string( ), + std::string name = std::string( ) ); + SharePointObject( const SharePointObject& copy ); + virtual ~SharePointObject( ) { } + + SharePointObject& operator=( const SharePointObject& copy ); + + void initializeFromJson( Json json, std::string parentId = std::string( ), + std::string name = std::string( ) ); + + void refreshImpl( Json json ); + virtual void refresh( ); + virtual void remove( bool allVersions = true ); + + std::vector< std::string > getMultiStringProperty( + const std::string& propertyName ); + + virtual boost::shared_ptr< Object > updateProperties( + const libcmis::PropertyPtrMap& properties ); + + virtual std::vector< libcmis::RenditionPtr> getRenditions( std::string /*filter = std::string( )*/ ) + {return std::vector< libcmis::RenditionPtr>( );} + + virtual void move( boost::shared_ptr< libcmis::Folder > source, + boost::shared_ptr< libcmis::Folder > destination ); + + protected: + SharePointSession* getSession( ); + +}; +#endif diff --git a/src/libcmis/sharepoint-property.cxx b/src/libcmis/sharepoint-property.cxx new file mode 100644 index 0000000..dfca7fe --- /dev/null +++ b/src/libcmis/sharepoint-property.cxx @@ -0,0 +1,79 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-property.hxx" + +#include <libcmis/property-type.hxx> + +#include "sharepoint-utils.hxx" + +using namespace std; +using namespace libcmis; + +SharePointProperty::SharePointProperty( ) +{ +} + +SharePointProperty::~SharePointProperty( ) +{ +} + +SharePointProperty::SharePointProperty( const string& key, Json json ): + Property( ) +{ + PropertyTypePtr propertyType( new PropertyType( ) ); + string convertedKey = SharePointUtils::toCmisKey( key ); + propertyType->setId( convertedKey ); + propertyType->setLocalName( convertedKey ); + propertyType->setLocalNamespace( convertedKey ); + propertyType->setQueryName( convertedKey ); + propertyType->setDisplayName( key ); + propertyType->setTypeFromJsonType( json.getStrType( ) ); + propertyType->setUpdatable( false ); + propertyType->setMultiValued( false ); + propertyType->setType( SharePointUtils::getPropertyType( convertedKey ) ); + + setPropertyType( propertyType ); + + vector< string > values = SharePointUtils::parseSharePointProperty( key, json ); + setValues( values ); +} + +SharePointProperty::SharePointProperty( const SharePointProperty& copy ) : + libcmis::Property( copy ) +{ +} + +SharePointProperty& SharePointProperty::operator=( const SharePointProperty& copy ) +{ + if ( this != © ) + { + libcmis::Property::operator=( copy ); + } + return *this; +} diff --git a/src/libcmis/sharepoint-property.hxx b/src/libcmis/sharepoint-property.hxx new file mode 100644 index 0000000..2649be4 --- /dev/null +++ b/src/libcmis/sharepoint-property.hxx @@ -0,0 +1,50 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef _SHAREPOINT_PROPERTY_HXX_ +#define _SHAREPOINT_PROPERTY_HXX_ + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +// reference: http://msdn.microsoft.com/en-us/library/hh243648.aspx +class SharePointProperty : public libcmis::Property +{ + public : + // Create a SharePoint Property from a Json property with its key + SharePointProperty( const std::string& key, Json json); + ~SharePointProperty( ); + SharePointProperty( const SharePointProperty& copy); + SharePointProperty& operator=( const SharePointProperty& copy ); + + private : + // Avoid calling default constructor + SharePointProperty( ); +}; +#endif diff --git a/src/libcmis/sharepoint-repository.cxx b/src/libcmis/sharepoint-repository.cxx new file mode 100644 index 0000000..f992689 --- /dev/null +++ b/src/libcmis/sharepoint-repository.cxx @@ -0,0 +1,65 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#include "sharepoint-repository.hxx" + +SharePointRepository::SharePointRepository( std::string baseUrl ) : + Repository( ) +{ + m_id = "SharePoint"; + m_name = "SharePoint"; + m_description = "SharePoint repository"; + m_productName = "SharePoint"; + m_productVersion = "2010/2013"; + // getFolderByServerRelativeUrl() API expects path to be + // *server-relative*, i.e. they must include site path. + // Given the baseUrl like "https://sp2013/sites/mysite/_api/Web" + // for a site "mysite" on sharepoint server "sp2013", + // the site root is '/sites/mysite/', not '/'. + // Trying to get folder '/' results in "Value does not fall + // within expected range" error. + // Preferrable here is to extract the root path from baseUrl, + // stripping server and api parts. But it can be unreliable + // if api part (_api/Web) is different for some server. + // On the other side, just querying empty path '' gives the root folder. + m_rootId = baseUrl + "/getFolderByServerRelativeUrl('')"; + + m_capabilities[ ACL ] = "discover"; + m_capabilities[ AllVersionsSearchable ] = "true"; + m_capabilities[ Changes ] = "all"; + m_capabilities[ GetDescendants ] = "true"; + m_capabilities[ GetFolderTree ] = "true"; + m_capabilities[ OrderBy ] = "custom"; + m_capabilities[ Multifiling ] = "true"; + m_capabilities[ PWCSearchable ] = "true"; + m_capabilities[ PWCUpdatable ] = "true"; + m_capabilities[ Query ] = "bothcombined"; + m_capabilities[ Renditions ] = "read"; + m_capabilities[ Unfiling ] = "false"; + m_capabilities[ VersionSpecificFiling ] = "false"; + m_capabilities[ Join ] = "none"; +} diff --git a/src/libcmis/sharepoint-repository.hxx b/src/libcmis/sharepoint-repository.hxx new file mode 100644 index 0000000..abf60c8 --- /dev/null +++ b/src/libcmis/sharepoint-repository.hxx @@ -0,0 +1,39 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_REPOSITORY_HXX_ +#define _SHAREPOINT_REPOSITORY_HXX_ + +#include <libcmis/repository.hxx> + +class SharePointRepository: public libcmis::Repository +{ + public: + SharePointRepository( std::string baseUrl ); +}; + +#endif diff --git a/src/libcmis/sharepoint-session.cxx b/src/libcmis/sharepoint-session.cxx new file mode 100644 index 0000000..d33bae8 --- /dev/null +++ b/src/libcmis/sharepoint-session.cxx @@ -0,0 +1,424 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-session.hxx" + +#include <libcmis/session-factory.hxx> + +#include "sharepoint-document.hxx" +#include "sharepoint-folder.hxx" +#include "sharepoint-object.hxx" +#include "sharepoint-object-type.hxx" +#include "sharepoint-repository.hxx" +#include "sharepoint-utils.hxx" + +using namespace std; + +SharePointSession::SharePointSession ( string baseUrl, + string username, + string password, + bool verbose ) : + BaseSession( baseUrl, string(), username, password, false, + libcmis::OAuth2DataPtr(), verbose ), + m_digestCode( string( ) ) + +{ + setAuthMethod( CURLAUTH_NTLM ); + libcmis::HttpResponsePtr response; + try + { + response = httpGetRequest( baseUrl + "/currentuser" ); + } + catch ( const CurlException& e ) + { + // It's not SharePoint or wrong username/passwd provided + throw e.getCmisException( ); + } + + // Add the dummy repository + m_repositories.push_back( getRepository( ) ); + fetchDigestCode( ); +} + +SharePointSession::SharePointSession( string baseUrl, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ) : + BaseSession( baseUrl, string(), httpSession ), + m_digestCode( string( ) ) +{ + if ( !SharePointUtils::isSharePoint( response->getStream( )->str( ) ) ) + { + throw libcmis::Exception( "Not a SharePoint service" ); + } + // Add the dummy repository + m_repositories.push_back( getRepository( ) ); + fetchDigestCode( ); +} + +SharePointSession::SharePointSession() : + BaseSession(), m_digestCode( string( ) ) +{ +} + +SharePointSession::~SharePointSession() +{ +} + +bool SharePointSession::setRepository( string ) +{ + return true; +} + +libcmis::RepositoryPtr SharePointSession::getRepository( ) +{ + // Return a dummy repository since SharePoint doesn't have that notion + libcmis::RepositoryPtr repo( new SharePointRepository( getBindingUrl( ) ) ); + return repo; +} + +libcmis::ObjectPtr SharePointSession::getObject( string objectId ) +{ + // objectId is uri for the file + string res; + try + { + res = httpGetRequest( objectId )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + Json jsonRes = Json::parse( res ); + return getObjectFromJson( jsonRes ); +} + +libcmis::ObjectPtr SharePointSession::getObjectFromJson( Json& jsonRes, string parentId ) +{ + libcmis::ObjectPtr object; + if ( !jsonRes["d"].toString( ).empty( ) ) { + jsonRes = jsonRes["d"]; + } + string kind = jsonRes["__metadata"]["type"].toString( ); + // only SharePointObject available for now + if ( kind == "SP.Folder" ) + { + object.reset( new SharePointFolder( this, jsonRes, parentId ) ); + } + else if ( kind == "SP.File" || kind == "SP.FileVersion" ) + { + object.reset( new SharePointDocument( this, jsonRes, parentId ) ); + } + else + { + object.reset( new SharePointObject( this, jsonRes, parentId ) ); + } + return object; +} + +libcmis::ObjectPtr SharePointSession::getObjectByPath( string path ) +{ + libcmis::ObjectPtr object; + path = libcmis::escape( path ); + // we don't know the object type so we try with Folder first + try + { + string folderUrl = getBindingUrl( ) + "/getFolderByServerRelativeUrl"; + folderUrl += "('" + path + "')"; + object = getObject( folderUrl ); + } + catch ( const libcmis::Exception &e ) + { + // it's not a Folder, maybe it's a File + string fileUrl = getBindingUrl( ) + "/getFileByServerRelativeUrl"; + fileUrl += "('" + path + "')"; + object = getObject( fileUrl ); + } + return object; +} + +libcmis::ObjectTypePtr SharePointSession::getType( string id ) +{ + libcmis::ObjectTypePtr type( new SharePointObjectType( id ) ); + return type; +} + +vector< libcmis::ObjectTypePtr > SharePointSession::getBaseTypes( ) +{ + vector< libcmis::ObjectTypePtr > types; + return types; +} + +Json SharePointSession::getJsonFromUrl( string url ) +{ + string response; + try + { + response = httpGetRequest( url )->getStream()->str(); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + return Json::parse( response ); +} + +/* Overwriting HttpSession::httpRunRequest to add the "accept:application/json" header */ +void SharePointSession::httpRunRequest( string url, vector< string > headers, bool redirect ) +{ + // Redirect + curl_easy_setopt( m_curlHandle, CURLOPT_FOLLOWLOCATION, redirect); + + // Activate the cookie engine + curl_easy_setopt( m_curlHandle, CURLOPT_COOKIEFILE, "" ); + + // Grab something from the web + curl_easy_setopt( m_curlHandle, CURLOPT_URL, url.c_str() ); + + // Set the headers + struct curl_slist *headers_slist = NULL; + for ( vector< string >::iterator it = headers.begin( ); it != headers.end( ); ++it ) + headers_slist = curl_slist_append( headers_slist, it->c_str( ) ); + + headers_slist = curl_slist_append( headers_slist, "accept:application/json; odata=verbose" ); + headers_slist = curl_slist_append( headers_slist, ( "x-requestdigest:" + m_digestCode ).c_str( ) ); + + if ( !getUsername().empty() && !getPassword().empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPAUTH, m_authMethod ); +#if LIBCURL_VERSION_VALUE >= 0x071301 + curl_easy_setopt( m_curlHandle, CURLOPT_USERNAME, getUsername().c_str() ); + curl_easy_setopt( m_curlHandle, CURLOPT_PASSWORD, getPassword().c_str() ); +#else + string userpwd = getUsername() + ":" + getPassword(); + curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, userpwd.c_str( ) ); +#endif + } + + curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, headers_slist ); + + // Set the proxy configuration if any + if ( !libcmis::SessionFactory::getProxy( ).empty() ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, libcmis::SessionFactory::getProxy( ).c_str() ); +#if LIBCURL_VERSION_VALUE >= 0x071304 + curl_easy_setopt( m_curlHandle, CURLOPT_NOPROXY, libcmis::SessionFactory::getNoProxy( ).c_str() ); +#endif + const string& proxyUser = libcmis::SessionFactory::getProxyUser( ); + const string& proxyPass = libcmis::SessionFactory::getProxyPass( ); + if ( !proxyUser.empty( ) && !proxyPass.empty( ) ) + { + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERNAME, proxyUser.c_str( ) ); + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYPASSWORD, proxyPass.c_str( ) ); +#else + string userpwd = proxyUser + ":" + proxyPass; + curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, userpwd.c_str( ) ); +#endif + } + } + + // Get some feedback when something wrong happens + char errBuff[CURL_ERROR_SIZE]; + errBuff[0] = 0; + curl_easy_setopt( m_curlHandle, CURLOPT_ERRORBUFFER, errBuff ); + + // We want to get the response even if there is an Http error + if ( !m_noHttpErrors ) + curl_easy_setopt( m_curlHandle, CURLOPT_FAILONERROR, 1 ); + + if ( m_verbose ) + curl_easy_setopt( m_curlHandle, CURLOPT_VERBOSE, 1 ); + + // We want to get the certificate infos in error cases +#if LIBCURL_VERSION_VALUE >= 0X071301 + curl_easy_setopt( m_curlHandle, CURLOPT_CERTINFO, 1 ); +#endif + + if ( m_noSSLCheck ) + { +#if LIBCURL_VERSION_VALUE >= 0x070801 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); +#endif +#if LIBCURL_VERSION_VALUE >= 0x070402 + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); +#endif + } + + // Perform the query + CURLcode errCode = curl_easy_perform( m_curlHandle ); + + // Free the headers list + curl_slist_free_all( headers_slist ); + + // Process the response + bool isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + if ( CURLE_OK != errCode && !( m_noHttpErrors && isHttpError ) ) + { + long httpError = 0; + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + + bool errorFixed = false; +#if LIBCURL_VERSION_VALUE >= 0X071301 + // If we had a bad certificate, then try to get more details + if ( CURLE_SSL_CACERT == errCode ) + { + vector< string > certificates; + + // We somehow need to rerun the request to get the certificate + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0); + errCode = curl_easy_perform( m_curlHandle ); + + union { + struct curl_slist *to_info; + struct curl_certinfo *to_certinfo; + } ptr; + + ptr.to_info = NULL; + + CURLcode res = curl_easy_getinfo(m_curlHandle, CURLINFO_CERTINFO, &ptr.to_info); + + if ( !res && ptr.to_info ) + { + // We need the first certificate in the chain only + if ( ptr.to_certinfo->num_of_certs > 0 ) + { + struct curl_slist *slist; + + string certStart( "Cert:" ); + for ( slist = ptr.to_certinfo->certinfo[0]; slist; slist = slist->next ) + { + string data( slist->data ); + size_t startPos = data.find( certStart ); + if ( startPos == 0 ) + { + startPos = certStart.length(); + data = data.substr( startPos ); + certificates.push_back( data ); + } + } + } + } + + if ( !certificates.empty() ) + { + libcmis::CertValidationHandlerPtr validationHandler = + libcmis::SessionFactory::getCertificateValidationHandler( ); + bool ignoreCert = validationHandler && validationHandler->validateCertificate( certificates ); + if ( ignoreCert ) + { + m_noSSLCheck = true; + + isHttpError = errCode == CURLE_HTTP_RETURNED_ERROR; + errorFixed = ( CURLE_OK == errCode || ( m_noHttpErrors && isHttpError ) ); + if ( !errorFixed ) + curl_easy_getinfo( m_curlHandle, CURLINFO_RESPONSE_CODE, &httpError ); + } + else + { + throw CurlException( "Invalid SSL certificate" ); + } + } + } +#endif + + if ( !errorFixed ) + throw CurlException( string( errBuff ), errCode, url, httpError ); + } +} + +libcmis::HttpResponsePtr SharePointSession::httpPutRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ) +{ + libcmis::HttpResponsePtr response; + try + { + response = HttpSession::httpPutRequest( url, is, headers ); + } + catch ( const CurlException& e ) + { + fetchDigestCodeCurl( ); + response = HttpSession::httpPutRequest( url, is, headers ); + } + return response; +} + +libcmis::HttpResponsePtr SharePointSession::httpPostRequest( const std::string& url, + std::istream& is, + const std::string& contentType, + bool redirect ) +{ + libcmis::HttpResponsePtr response; + try + { + response = HttpSession::httpPostRequest( url, is, contentType, redirect ); + } + catch ( const CurlException& e ) + { + fetchDigestCodeCurl( ); + response = HttpSession::httpPostRequest( url, is, contentType, redirect ); + } + return response; +} + +void SharePointSession::httpDeleteRequest( std::string url ) +{ + try + { + HttpSession::httpDeleteRequest( url ); + } + catch ( const CurlException& e ) + { + fetchDigestCodeCurl( ); + HttpSession::httpDeleteRequest( url ); + } +} + +void SharePointSession::fetchDigestCode( ) +try +{ + fetchDigestCodeCurl( ); +} +catch ( const CurlException& e ) +{ + throw e.getCmisException( ); +} + +void SharePointSession::fetchDigestCodeCurl( ) +{ + istringstream is( "empty" ); + libcmis::HttpResponsePtr response; + // url = http://host/_api/contextinfo, first we remove the '/web' part + string url = m_bindingUrl.substr( 0, m_bindingUrl.size( ) - 4 ) + "/contextinfo"; + response = HttpSession::httpPostRequest( url, is, "" ); + string res = response->getStream( )->str( ); + Json jsonRes = Json::parse( res ); + m_digestCode = jsonRes["d"]["GetContextWebInformation"]["FormDigestValue"].toString( ); +} diff --git a/src/libcmis/sharepoint-session.hxx b/src/libcmis/sharepoint-session.hxx new file mode 100644 index 0000000..2d47cb9 --- /dev/null +++ b/src/libcmis/sharepoint-session.hxx @@ -0,0 +1,91 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_SESSION_HXX_ +#define _SHAREPOINT_SESSION_HXX_ + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" +#include "json-utils.hxx" + +class SharePointSession : public BaseSession +{ + public: + SharePointSession( std::string baseUrl, + std::string username, + std::string password, + bool verbose = false ); + + SharePointSession( std::string baseUrl, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ); + + ~SharePointSession ( ); + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + libcmis::ObjectPtr getObjectFromJson( Json& jsonRes, + std::string parentId = std::string( ) ); + + Json getJsonFromUrl( std::string url ); + + void fetchDigestCode( ); + + void httpRunRequest( std::string url, + std::vector< std::string > headers, + bool redirect ); + + libcmis::HttpResponsePtr httpPutRequest( std::string url, + std::istream& is, + std::vector< std::string > headers ); + libcmis::HttpResponsePtr httpPostRequest( const std::string& url, + std::istream& is, + const std::string& contentType, + bool redirect = true ); + void httpDeleteRequest( std::string url ); + + + private: + SharePointSession( ); + SharePointSession( const SharePointSession& copy ) = delete; + SharePointSession& operator=( const SharePointSession& copy ) = delete; + void fetchDigestCodeCurl( ); + std::string m_digestCode; +}; + +#endif /* _SHAREPONT_SESSION_HXX_ */ diff --git a/src/libcmis/sharepoint-utils.cxx b/src/libcmis/sharepoint-utils.cxx new file mode 100644 index 0000000..35a9f87 --- /dev/null +++ b/src/libcmis/sharepoint-utils.cxx @@ -0,0 +1,133 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "sharepoint-utils.hxx" + +#include <boost/shared_ptr.hpp> + +#include <libcmis/xml-utils.hxx> + +#include "json-utils.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +string SharePointUtils::toCmisKey( const string& key ) +{ + string convertedKey; + if ( key == "__metadata") + convertedKey = "cmis:objectId"; + else if ( key == "CheckInComment" ) + convertedKey = "cmis:checkinComment"; + else if ( key == "TimeCreated" ) + convertedKey = "cmis:creationDate"; + else if ( key == "TimeLastModified" || + key == "Created" ) + convertedKey = "cmis:lastModificationDate"; + else if ( key == "Name" ) + convertedKey = "cmis:name"; + else if ( key == "CheckOutType" ) + convertedKey = "cmis:isVersionSeriesCheckedOut"; + else if ( key == "UIVersionLabel" || + key == "VersionLabel" ) + convertedKey = "cmis:versionLabel"; + else if ( key == "Length" || + key == "Size" ) + convertedKey = "cmis:contentStreamLength"; + else convertedKey = key; + return convertedKey; +} + +libcmis::PropertyType::Type SharePointUtils::getPropertyType( const string& key ) +{ + libcmis::PropertyType::Type propertyType; + if ( key == "cmis:creationDate" || + key == "cmis:lastModificationDate" ) + { + propertyType = libcmis::PropertyType::DateTime; + } + else if ( key == "cmis:contentStreamLength" ) + { + propertyType = libcmis::PropertyType::Integer; + } + else if ( key == "cmis:isVersionSeriesCheckedOut" ) + { + propertyType = libcmis::PropertyType::Bool; + } + else + { + propertyType = libcmis::PropertyType::String; + } + return propertyType; +} + +vector< string > SharePointUtils::parseSharePointProperty( string key, Json json ) +{ + vector< string > values; + if ( key == "__metadata" ) + { + string id = json["uri"].toString( ); + values.push_back( id ); + } + if ( key == "Author" || + key == "CheckedOutByUser" || + key == "CreatedBy" || + key == "Files" || + key == "Folders" || + key == "ListItemAllFields" || + key == "LockedByUser" || + key == "ModifiedBy" || + key == "ParentFolder" || + key == "Properties" || + key == "Versions" ) + { + string propertyUri = json["__deferred"]["uri"].toString( ); + values.push_back( propertyUri ); + } + if ( key == "CheckOutType" ) + { + // Online = 0, Offline = 1, None = 2 + if ( json.toString( ) == "2" ) + { + values.push_back( "false" ); + } + else + { + values.push_back( "true" ); + } + } + else values.push_back( json.toString( ) ); + return values; +} + +bool SharePointUtils::isSharePoint( string response ) +{ + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( response.c_str( ), response.size( ), "noname.xml", NULL, 0 ), xmlFreeDoc ); + const boost::shared_ptr< xmlXPathContext > xpath( xmlXPathNewContext( doc.get() ), xmlXPathFreeContext ); + return "SP.Web" == libcmis::getXPathValue( xpath.get(), "//@term" ); +} diff --git a/src/libcmis/sharepoint-utils.hxx b/src/libcmis/sharepoint-utils.hxx new file mode 100644 index 0000000..80a5e51 --- /dev/null +++ b/src/libcmis/sharepoint-utils.hxx @@ -0,0 +1,54 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2014 Mihai Varga <mihai.mv13@gmail.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _SHAREPOINT_UTILS_HXX_ +#define _SHAREPOINT_UTILS_HXX_ + +#include <string> + +#include <libcmis/property.hxx> + +#include "json-utils.hxx" + +class SharePointUtils +{ + public : + + // Convert a SharePoint Property key to a CMIS key + static std::string toCmisKey( const std::string& key); + + // Returns the property type (String/Bool/Integer etc ) + static libcmis::PropertyType::Type getPropertyType( const std::string& key ); + + // Parse a SharePoint property value to CMIS values + static std::vector< std::string > parseSharePointProperty( std::string key, Json jsonValue ); + + // Checks if a response came from a SharePoint service + static bool isSharePoint( std::string response ); +}; + +#endif diff --git a/src/libcmis/ws-document.cxx b/src/libcmis/ws-document.cxx new file mode 100644 index 0000000..ba2f2bc --- /dev/null +++ b/src/libcmis/ws-document.cxx @@ -0,0 +1,135 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-document.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +WSDocument::WSDocument( const WSObject& object ) : + libcmis::Object( object ), + libcmis::Document( const_cast< WSObject& >( object ).getSession( ) ), + WSObject( object ) +{ +} + +WSDocument::~WSDocument( ) +{ +} + +vector< libcmis::FolderPtr > WSDocument::getParents( ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getNavigationService( ).getObjectParents( repoId, getId( ) ); +} + +boost::shared_ptr< istream > WSDocument::getContentStream( std::string /* streamId */ ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).getContentStream( repoId, getId( ) ); +} + +void WSDocument::setContentStream( boost::shared_ptr< ostream > os, string contentType, + string fileName, bool overwrite ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getObjectService( ).setContentStream( repoId, getId( ), + overwrite, getChangeToken( ), os, contentType, fileName ); + + refresh( ); +} + +libcmis::DocumentPtr WSDocument::checkOut( ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getVersioningService( ).checkOut( repoId, getId( ) ); +} + +void WSDocument::cancelCheckout( ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getVersioningService( ).cancelCheckOut( repoId, getId( ) ); +} + +libcmis::DocumentPtr WSDocument::checkIn( bool isMajor, string comment, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > stream, + string contentType, string fileName ) +{ + string repoId = getSession( )->getRepositoryId( ); + libcmis::DocumentPtr newVersion; + + // Try the normal request first, but if we have a server error, we may want to resend it + // without the stream as SharePoint wants no stream in the request, but gets the one from + // the PWC see the following discussion: + // http://social.technet.microsoft.com/Forums/eu/sharepoint2010programming/thread/b30e4d82-5b7e-4ceb-b9ad-c6f0d4c59d11 + bool tryNoStream = false; + try + { + newVersion = getSession( )->getVersioningService( ).checkIn( repoId, getId( ), + isMajor, properties, stream, contentType, fileName, comment ); + } + catch ( const libcmis::Exception& e ) + { + string spError( "Object reference not set to an instance of an object" ); + if ( string( e.what( ) ).find( spError ) != string::npos ) + tryNoStream = true; + else + throw; + } + + if ( tryNoStream ) + { + // Set the content stream first + setContentStream( stream, contentType, fileName ); + + // Then check-in + boost::shared_ptr< ostream > nostream; + newVersion = getSession( )->getVersioningService( ).checkIn( repoId, getId( ), + isMajor, properties, nostream, string( ), string( ), comment ); + } + + if ( newVersion->getId( ) == getId( ) ) + refresh( ); + + return newVersion; +} + +vector< libcmis::DocumentPtr > WSDocument::getAllVersions( ) +{ + vector< libcmis::DocumentPtr > versions; + string repoId = getSession( )->getRepositoryId( ); + string versionSeries; + PropertyPtrMap::const_iterator it = getProperties( ).find( string( "cmis:versionSeriesId" ) ); + if ( it != getProperties( ).end( ) && !it->second->getStrings( ).empty( ) ) + { + versionSeries = it->second->getStrings( ).front( ); + versions = getSession( )->getVersioningService( ).getAllVersions( repoId, versionSeries ); + } + return versions; +} diff --git a/src/libcmis/ws-document.hxx b/src/libcmis/ws-document.hxx new file mode 100644 index 0000000..c569496 --- /dev/null +++ b/src/libcmis/ws-document.hxx @@ -0,0 +1,60 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_DOCUMENT_HXX_ +#define _WS_DOCUMENT_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "ws-object.hxx" + +class WSDocument : public libcmis::Document, public WSObject +{ + public: + WSDocument( const WSObject& object ); + virtual ~WSDocument( ); + + virtual std::vector< libcmis::FolderPtr > getParents( ); + + virtual boost::shared_ptr< std::istream > getContentStream( std::string streamId = std::string( ) ); + + virtual void setContentStream( boost::shared_ptr< std::ostream > os, std::string contentType, + std::string fileName, bool overwrite = true ); + + virtual libcmis::DocumentPtr checkOut( ); + virtual void cancelCheckout( ); + + virtual libcmis::DocumentPtr checkIn( bool isMajor, std::string comment, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string fileName ); + + virtual std::vector< libcmis::DocumentPtr > getAllVersions( ); +}; + +#endif diff --git a/src/libcmis/ws-folder.cxx b/src/libcmis/ws-folder.cxx new file mode 100644 index 0000000..4e82ac2 --- /dev/null +++ b/src/libcmis/ws-folder.cxx @@ -0,0 +1,68 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-folder.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +WSFolder::WSFolder( const WSObject& object ) : + libcmis::Object( object ), + libcmis::Folder( const_cast< WSObject& >( object ).getSession( ) ), + WSObject( object ) +{ +} + +WSFolder::~WSFolder( ) +{ +} + +vector< libcmis::ObjectPtr > WSFolder::getChildren( ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getNavigationService( ).getChildren( repoId, getId( ) ); +} + +libcmis::FolderPtr WSFolder::createFolder( const PropertyPtrMap& properties ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).createFolder( repoId, properties, getId( ) ); +} + +libcmis::DocumentPtr WSFolder::createDocument( const PropertyPtrMap& properties, + boost::shared_ptr< ostream > os, string contentType, string fileName ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).createDocument( repoId, properties, getId( ), os, contentType, fileName ); +} + +vector< string > WSFolder::removeTree( bool allVersion, libcmis::UnfileObjects::Type unfile, bool continueOnError ) +{ + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).deleteTree( repoId, getId( ), allVersion, unfile, continueOnError ); +} diff --git a/src/libcmis/ws-folder.hxx b/src/libcmis/ws-folder.hxx new file mode 100644 index 0000000..2653967 --- /dev/null +++ b/src/libcmis/ws-folder.hxx @@ -0,0 +1,54 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_FOLDER_HXX_ +#define _WS_FOLDER_HXX_ + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> + +#include "ws-object.hxx" +#include "ws-session.hxx" + +class WSFolder : public libcmis::Folder, public WSObject +{ + public: + WSFolder( const WSObject& object ); + virtual ~WSFolder( ); + + // virtual pure methods from Folder + virtual std::vector< libcmis::ObjectPtr > getChildren( ); + + virtual libcmis::FolderPtr createFolder( const std::map< std::string, libcmis::PropertyPtr >& properties ); + virtual libcmis::DocumentPtr createDocument( const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > os, std::string contentType, std::string fileName ); + + virtual std::vector< std::string > removeTree( bool allVersion = true, libcmis::UnfileObjects::Type unfile = libcmis::UnfileObjects::Delete, + bool continueOnError = false ); +}; + +#endif diff --git a/src/libcmis/ws-navigationservice.cxx b/src/libcmis/ws-navigationservice.cxx new file mode 100644 index 0000000..f3d18ec --- /dev/null +++ b/src/libcmis/ws-navigationservice.cxx @@ -0,0 +1,101 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-navigationservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; + +NavigationService::NavigationService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +NavigationService::NavigationService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "NavigationService" ) ) +{ +} + +NavigationService::NavigationService( const NavigationService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +NavigationService::~NavigationService( ) +{ +} + +NavigationService& NavigationService::operator=( const NavigationService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +vector< libcmis::FolderPtr > NavigationService::getObjectParents( std::string repoId, std::string objectId ) +{ + vector< libcmis::FolderPtr > parents; + + GetObjectParentsRequest request( repoId, objectId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetObjectParentsResponse* response = dynamic_cast< GetObjectParentsResponse* >( resp ); + if ( response != NULL ) + parents = response->getParents( ); + } + + return parents; +} + +vector< libcmis::ObjectPtr > NavigationService::getChildren( string repoId, string folderId ) +{ + vector< libcmis::ObjectPtr > children; + + GetChildrenRequest request( repoId, folderId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetChildrenResponse* response = dynamic_cast< GetChildrenResponse* >( resp ); + if ( response != NULL ) + children = response->getChildren( ); + } + + return children; +} diff --git a/src/libcmis/ws-navigationservice.hxx b/src/libcmis/ws-navigationservice.hxx new file mode 100644 index 0000000..c245bbc --- /dev/null +++ b/src/libcmis/ws-navigationservice.hxx @@ -0,0 +1,60 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_NAVIGATIONSERVICE_HXX_ +#define _WS_NAVIGATIONSERVICE_HXX_ + +#include <string> +#include <vector> + +#include <libcmis/folder.hxx> + +class WSSession; + +class NavigationService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + NavigationService( WSSession* session ); + NavigationService( const NavigationService& copy ); + ~NavigationService( ); + + NavigationService& operator=( const NavigationService& copy ); + + std::vector< libcmis::FolderPtr > getObjectParents( std::string repoId, std::string objectId ); + std::vector< libcmis::ObjectPtr > getChildren( std::string repoId, std::string folderId ); + + private: + + NavigationService( ); +}; + +#endif diff --git a/src/libcmis/ws-object-type.cxx b/src/libcmis/ws-object-type.cxx new file mode 100644 index 0000000..9275da9 --- /dev/null +++ b/src/libcmis/ws-object-type.cxx @@ -0,0 +1,90 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-object-type.hxx" + +using namespace std; + +WSObjectType::WSObjectType( WSSession* session, xmlNodePtr node ) : + libcmis::ObjectType( node ), + m_session( session ) +{ +} + +WSObjectType::WSObjectType( ) : + libcmis::ObjectType( ), + m_session( NULL ) +{ +} + +WSObjectType::WSObjectType( const WSObjectType& copy ) : + libcmis::ObjectType( copy ), + m_session( copy.m_session ) +{ +} + +WSObjectType::~WSObjectType( ) +{ +} + +WSObjectType& WSObjectType::operator=( const WSObjectType& copy ) +{ + if ( this != © ) + { + libcmis::ObjectType::operator=( copy ); + m_session = copy.m_session; + } + + return *this; +} + +void WSObjectType::refresh( ) +{ + libcmis::ObjectTypePtr type = m_session->getType( m_id ); + WSObjectType* const other = dynamic_cast< WSObjectType* >( type.get( ) ); + if ( other != NULL ) + *this = *other; +} + +libcmis::ObjectTypePtr WSObjectType::getParentType( ) +{ + return m_session->getType( m_parentTypeId ); +} + +libcmis::ObjectTypePtr WSObjectType::getBaseType( ) +{ + return m_session->getType( m_baseTypeId ); +} + +vector< libcmis::ObjectTypePtr > WSObjectType::getChildren( ) +{ + vector< libcmis::ObjectTypePtr > children; + children = m_session->getRepositoryService( ).getTypeChildren( + m_session->getRepositoryId( ), m_id ); + return children; +} diff --git a/src/libcmis/ws-object-type.hxx b/src/libcmis/ws-object-type.hxx new file mode 100644 index 0000000..1fcf18d --- /dev/null +++ b/src/libcmis/ws-object-type.hxx @@ -0,0 +1,57 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_OBJECT_TYPE_HXX_ +#define _WS_OBJECT_TYPE_HXX_ + +#include <libcmis/object-type.hxx> + +#include "ws-session.hxx" + +class WSObjectType : public libcmis::ObjectType +{ + private: + WSSession* m_session; + + public: + WSObjectType( WSSession* session, xmlNodePtr node ); + WSObjectType( const WSObjectType& copy ); + virtual ~WSObjectType( ); + + WSObjectType& operator=( const WSObjectType& copy ); + + virtual void refresh( ); + + virtual libcmis::ObjectTypePtr getParentType( ); + virtual libcmis::ObjectTypePtr getBaseType( ); + virtual std::vector< libcmis::ObjectTypePtr > getChildren( ); + + private: + WSObjectType( ); +}; + +#endif diff --git a/src/libcmis/ws-object.cxx b/src/libcmis/ws-object.cxx new file mode 100644 index 0000000..65a238d --- /dev/null +++ b/src/libcmis/ws-object.cxx @@ -0,0 +1,130 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-object.hxx" + +#include "ws-document.hxx" +#include "ws-folder.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +WSObject::WSObject( WSSession* session ) : + libcmis::Object( session ) +{ +} + + +WSObject::WSObject( WSSession* session, xmlNodePtr node ) : + libcmis::Object( session, node ) +{ +} + +WSObject::WSObject( const WSObject& copy ) : + libcmis::Object( copy ) +{ +} + +WSObject::~WSObject( ) +{ +} + +WSObject& WSObject::operator=( const WSObject& copy ) +{ + if ( this != © ) + { + libcmis::Object::operator=( copy ); + } + + return *this; +} + +vector< libcmis::RenditionPtr > WSObject::getRenditions( string filter ) +{ + // Check that the server supports that optional feature. There is no need to check it + // when getting the object as we may get them by shear luck + libcmis::RepositoryPtr repo = getSession( )->getRepository( ); + bool isCapable = repo && repo->getCapability( libcmis::Repository::Renditions ) == "read"; + + if ( m_renditions.empty() && isCapable ) + { + string repoId = getSession( )->getRepositoryId( ); + m_renditions = getSession( )->getObjectService( ).getRenditions( repoId, this->getId( ), filter ); + } + return m_renditions; +} + +libcmis::ObjectPtr WSObject::updateProperties( + const PropertyPtrMap& properties ) +{ + // No need to send HTTP request if there is nothing to update + if ( properties.empty( ) ) + { + libcmis::ObjectPtr object; + if ( getBaseType( ) == "cmis:document" ) + { + const WSDocument& thisDoc = dynamic_cast< const WSDocument& >( *this ); + object.reset( new WSDocument( thisDoc ) ); + } + else if ( getBaseType( ) == "cmis:folder" ) + { + const WSFolder& thisFolder = dynamic_cast< const WSFolder& >( *this ); + object.reset( new WSFolder( thisFolder ) ); + } + return object; + } + string repoId = getSession( )->getRepositoryId( ); + return getSession( )->getObjectService( ).updateProperties( repoId, this->getId( ), properties, this->getChangeToken( ) ); +} + +void WSObject::refresh( ) +{ + libcmis::ObjectPtr object = m_session->getObject( getId( ) ); + WSObject* const other = dynamic_cast< WSObject* >( object.get( ) ); + if ( other != NULL ) + *this = *other; +} + +void WSObject::remove( bool allVersions ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getObjectService( ).deleteObject( repoId, this->getId( ), allVersions ); +} + +void WSObject::move( libcmis::FolderPtr source, libcmis::FolderPtr destination ) +{ + string repoId = getSession( )->getRepositoryId( ); + getSession( )->getObjectService( ).move( repoId, getId( ), destination->getId( ), source->getId( ) ); + + refresh( ); +} + +WSSession* WSObject::getSession( ) +{ + return dynamic_cast< WSSession* >( m_session ); +} diff --git a/src/libcmis/ws-object.hxx b/src/libcmis/ws-object.hxx new file mode 100644 index 0000000..f657371 --- /dev/null +++ b/src/libcmis/ws-object.hxx @@ -0,0 +1,61 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_OBJECT_HXX_ +#define _WS_OBJECT_HXX_ + +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> + +#include "ws-session.hxx" + +class WSObject : public virtual libcmis::Object +{ + protected: + WSObject( WSSession* session ); + + public: + WSObject( WSSession* session, xmlNodePtr node ); + WSObject( const WSObject& copy ); + virtual ~WSObject( ); + + WSObject& operator=( const WSObject& copy ); + + virtual std::vector< libcmis::RenditionPtr > getRenditions( std::string filter ); + virtual libcmis::ObjectPtr updateProperties( + const std::map< std::string, libcmis::PropertyPtr >& properties ); + + virtual void refresh( ); + + virtual void remove( bool allVersions = true ); + + virtual void move( libcmis::FolderPtr source, libcmis::FolderPtr destination ); + + WSSession* getSession( ); +}; + +#endif diff --git a/src/libcmis/ws-objectservice.cxx b/src/libcmis/ws-objectservice.cxx new file mode 100644 index 0000000..871349c --- /dev/null +++ b/src/libcmis/ws-objectservice.cxx @@ -0,0 +1,242 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-objectservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +ObjectService::ObjectService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +ObjectService::ObjectService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "ObjectService" ) ) +{ +} + +ObjectService::ObjectService( const ObjectService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +ObjectService::~ObjectService( ) +{ +} + +ObjectService& ObjectService::operator=( const ObjectService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +libcmis::ObjectPtr ObjectService::getObject( string repoId, string id ) +{ + libcmis::ObjectPtr object; + + GetObjectRequest request( repoId, id ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetObjectResponse* response = dynamic_cast< GetObjectResponse* >( resp ); + if ( response != NULL ) + object = response->getObject( ); + } + + return object; +} + +libcmis::ObjectPtr ObjectService::getObjectByPath( string repoId, string path ) +{ + libcmis::ObjectPtr object; + + GetObjectByPathRequest request( repoId, path ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetObjectResponse* response = dynamic_cast< GetObjectResponse* >( resp ); + if ( response != NULL ) + object = response->getObject( ); + } + + return object; +} + +vector< libcmis::RenditionPtr > ObjectService::getRenditions( + string repoId, string objectId, string filter ) +{ + vector< libcmis::RenditionPtr > renditions; + + GetRenditionsRequest request( repoId, objectId, filter ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetRenditionsResponse* response = dynamic_cast< GetRenditionsResponse* >( resp ); + if ( response != NULL ) + { + renditions = response->getRenditions( ); + } + } + + return renditions; +} + +libcmis::ObjectPtr ObjectService::updateProperties( + string repoId, string objectId, + const PropertyPtrMap& properties, + string changeToken ) +{ + libcmis::ObjectPtr object; + + UpdatePropertiesRequest request( repoId, objectId, properties, changeToken ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + UpdatePropertiesResponse* response = dynamic_cast< UpdatePropertiesResponse* >( resp ); + if ( response != NULL ) + { + string id = response->getObjectId( ); + object = getObject( repoId, id ); + } + } + + return object; +} + +void ObjectService::deleteObject( string repoId, string id, bool allVersions ) +{ + DeleteObjectRequest request( repoId, id, allVersions ); + m_session->soapRequest( m_url, request ); +} + +vector< string > ObjectService::deleteTree( std::string repoId, std::string folderId, bool allVersions, + libcmis::UnfileObjects::Type unfile, bool continueOnFailure ) +{ + vector< string > failedIds; + + DeleteTreeRequest request( repoId, folderId, allVersions, unfile, continueOnFailure ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + DeleteTreeResponse* response = dynamic_cast< DeleteTreeResponse* >( resp ); + if ( response != NULL ) + failedIds = response->getFailedIds( ); + } + + return failedIds; +} + +void ObjectService::move( string repoId, string objectId, string destId, string srcId ) +{ + MoveObjectRequest request( repoId, objectId, destId, srcId ); + m_session->soapRequest( m_url, request ); +} + +boost::shared_ptr< istream > ObjectService::getContentStream( string repoId, string objectId ) +{ + boost::shared_ptr< istream > stream; + + GetContentStreamRequest request( repoId, objectId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetContentStreamResponse* response = dynamic_cast< GetContentStreamResponse* >( resp ); + if ( response != NULL ) + stream = response->getStream( ); + } + + return stream; +} + +void ObjectService::setContentStream( std::string repoId, std::string objectId, bool overwrite, std::string changeToken, + boost::shared_ptr< std::ostream > stream, std::string contentType, std::string fileName ) +{ + SetContentStreamRequest request( repoId, objectId, overwrite, changeToken, stream, contentType, fileName ); + m_session->soapRequest( m_url, request ); +} + +libcmis::FolderPtr ObjectService::createFolder( string repoId, const PropertyPtrMap& properties, + string folderId ) +{ + libcmis::FolderPtr folder; + + CreateFolderRequest request( repoId, properties, folderId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CreateFolderResponse* response = dynamic_cast< CreateFolderResponse* >( resp ); + if ( response != NULL ) + { + string id = response->getObjectId( ); + folder = m_session->getFolder( id ); + } + } + + return folder; +} + +libcmis::DocumentPtr ObjectService::createDocument( string repoId, const PropertyPtrMap& properties, + string folderId, boost::shared_ptr< ostream > stream, string contentType, string fileName ) +{ + libcmis::DocumentPtr document; + + CreateDocumentRequest request( repoId, properties, folderId, stream, contentType, fileName ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CreateFolderResponse* response = dynamic_cast< CreateFolderResponse* >( resp ); + if ( response != NULL ) + { + string id = response->getObjectId( ); + libcmis::ObjectPtr object = m_session->getObject( id ); + document = boost::dynamic_pointer_cast< libcmis::Document >( object ); + } + } + + return document; +} diff --git a/src/libcmis/ws-objectservice.hxx b/src/libcmis/ws-objectservice.hxx new file mode 100644 index 0000000..877deb7 --- /dev/null +++ b/src/libcmis/ws-objectservice.hxx @@ -0,0 +1,95 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_OBJECTSERVICE_HXX_ +#define _WS_OBJECTSERVICE_HXX_ + +#include <istream> +#include <string> +#include <vector> + +#include <libcmis/document.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> + +#include "base-session.hxx" +#include "ws-soap.hxx" + +class WSSession; + +class ObjectService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + ObjectService( WSSession* session ); + ObjectService( const ObjectService& copy ); + ~ObjectService( ); + + ObjectService& operator=( const ObjectService& copy ); + + libcmis::ObjectPtr getObject( std::string repoId, std::string id ); + + libcmis::ObjectPtr getObjectByPath( std::string repoId, std::string path ); + + std::vector< libcmis::RenditionPtr > getRenditions( + std::string repoId, std::string objectId, std::string filter ); + + libcmis::ObjectPtr updateProperties( + std::string repoId, + std::string objectId, + const std::map< std::string, libcmis::PropertyPtr > & properties, + std::string changeToken ); + + void deleteObject( std::string repoId, std::string id, bool allVersions ); + + std::vector< std::string > deleteTree( std::string repoId, std::string folderId, bool allVersions, + libcmis::UnfileObjects::Type unfile, bool continueOnFailure ); + + void move( std::string repoId, std::string objectId, std::string destId, std::string srcId ); + + boost::shared_ptr< std::istream > getContentStream( std::string repoId, std::string objectId ); + + void setContentStream( std::string repoId, std::string objectId, bool overwrite, std::string changeToken, + boost::shared_ptr< std::ostream > stream, std::string contentType, std::string fileName ); + + libcmis::FolderPtr createFolder( std::string repoId, const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId ); + + libcmis::DocumentPtr createDocument( std::string repoId, const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId, boost::shared_ptr< std::ostream > stream, std::string contentType, + std::string fileName ); + + private: + + ObjectService( ); +}; + +#endif diff --git a/src/libcmis/ws-relatedmultipart.cxx b/src/libcmis/ws-relatedmultipart.cxx new file mode 100644 index 0000000..4def5dd --- /dev/null +++ b/src/libcmis/ws-relatedmultipart.cxx @@ -0,0 +1,353 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-relatedmultipart.hxx" + +#include <algorithm> +#include <sstream> +#include <boost/algorithm/string.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> +#include <curl/curl.h> + +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace boost::uuids; + +RelatedPart::RelatedPart( string& name, string& type, string& content ) : + m_name( name ), + m_contentType( type ), + m_content( content ) +{ +} + +// LCOV_EXCL_START +string RelatedPart::toString( string cid ) +{ + string buf; + + buf += "Content-Id: <" + cid + ">\r\n"; + buf += "Content-Type: " + getContentType( ) + "\r\n"; + buf += "Content-Transfer-Encoding: binary\r\n\r\n"; + buf += getContent( ); + + return buf; +} +// LCOV_EXCL_STOP + +RelatedMultipart::RelatedMultipart( ) : + m_startId( ), + m_startInfo( ), + m_parts( ), + m_boundary( ) +{ + stringstream tmpStream("--------uuid:"); + tmpStream << random_generator()(); + m_boundary = tmpStream.str(); +} + +RelatedMultipart::RelatedMultipart( const string& body, const string& contentType ) : + m_startId( ), + m_startInfo( ), + m_parts( ), + m_boundary( ) +{ + // Parse the content-type + size_t lastPos = 0; + size_t pos = contentType.find_first_of( ";\"" ); + while ( pos != string::npos ) + { + bool escaped = contentType[pos] == '"'; + if ( escaped ) + { + // Look for the closing quote and then look for the ; after it + pos = contentType.find( "\"", pos + 1 ); + pos = contentType.find( ";", pos + 1 ); + } + + string param = contentType.substr( lastPos, pos - lastPos ); + size_t eqPos = param.find( "=" ); + if ( eqPos != string::npos ) + { + string name = param.substr( 0, eqPos ); + string value = param.substr( eqPos + 1 ); + if ( value[0] == '"' && value[value.length() - 1] == '"' ) + value = value.substr( 1, value.length( ) - 2 ); + + name = libcmis::trim( name ); + + if ( name == "start" ) + { + m_startId = value; + // Remove the '<' '>' around the id if any + if ( m_startId[0] == '<' && m_startId[m_startId.size()-1] == '>' ) + m_startId = m_startId.substr( 1, m_startId.size() - 2 ); + } + else if ( name == "boundary" ) + m_boundary = value; + else if ( name == "start-info" ) + m_startInfo = value; + } + + if ( pos != string::npos ) + { + lastPos = pos + 1; + pos = contentType.find_first_of( ";\"", lastPos ); + } + } + + // Parse the multipart + string bodyFixed( body ); + if ( boost::starts_with( bodyFixed, "--" + m_boundary + "\r\n" ) ) + bodyFixed = "\r\n" + bodyFixed; + + if ( bodyFixed[bodyFixed.length() - 1 ] != '\n' ) + bodyFixed += '\n'; + + string lineEnd( "\n" ); + string boundaryString( "--" + m_boundary ); + string endBoundaryString( "--" + m_boundary + "--" ); + pos = bodyFixed.find( lineEnd ); + lastPos = 0; + bool inPart = false; + bool inHeaders = false; + string cid; + string name; + string type; + string partBody; + + while ( pos != string::npos ) + { + string line = bodyFixed.substr( lastPos, pos - lastPos ); + if ( boost::starts_with( line, boundaryString ) ) + { + // Found a part start + inPart = true; + + if ( !cid.empty() && !type.empty( ) ) + { + // Remove potential \r at the end of the body part + if ( partBody[partBody.length() - 1] == '\r' ) + partBody = partBody.substr( 0, partBody.length() - 1 ); + + RelatedPartPtr relatedPart( new RelatedPart( name, type, partBody ) ); + m_parts[cid] = relatedPart; + + } + cid.clear( ); + type.clear( ); + name.clear( ); + partBody.clear( ); + inHeaders = true; + } + else if ( inPart ) + { + if ( inHeaders ) + { + // Remove potential \r at the end + if ( !line.empty() && line[line.length() - 1] == '\r' ) + line = line.substr( 0, line.length() - 1 ); + + if ( line.empty( ) ) + inHeaders = false; + else + { + size_t colonPos = line.find( ":" ); + string headerName = line.substr( 0, colonPos ); + string headerValue = line.substr( colonPos + 1 ); + if ( boost::to_lower_copy( headerName ) == "content-id" ) + { + cid = libcmis::trim( headerValue ); + // Remove the '<' '>' around the id if any + if ( cid[0] == '<' && cid[cid.size()-1] == '>' ) + cid = cid.substr( 1, cid.size() - 2 ); + } + else if ( headerName == "Content-Type" ) + type = libcmis::trim( headerValue ); + // TODO Handle the Content-Transfer-Encoding + } + } + else + { + if ( !partBody.empty() ) + partBody += lineEnd; + partBody += line; + } + } + + // If we found the end of the multipart, no need to continue looping + if ( boost::starts_with( line, endBoundaryString ) ) + break; + + lastPos = pos + lineEnd.length(); + pos = bodyFixed.find( lineEnd, lastPos ); + } +} + +vector< string > RelatedMultipart::getIds( ) +{ + vector< string > ids; + + for ( map< string, RelatedPartPtr >::iterator it = m_parts.begin( ); + it != m_parts.end( ); ++it ) + { + ids.push_back( it->first ); + } + + return ids; +} + +RelatedPartPtr RelatedMultipart::getPart( string& cid ) +{ + RelatedPartPtr part; + map< string, RelatedPartPtr >::iterator it = m_parts.find( cid ); + if ( it != m_parts.end( ) ) + part = it->second; + + return part; +} + +string RelatedMultipart::addPart( RelatedPartPtr part ) +{ + string cid = createPartId( part->getName( ) ); + m_parts[cid] = part; + return cid; +} + +void RelatedMultipart::setStart( string& cid, string& startInfo ) +{ + RelatedPartPtr start = getPart( cid ); + + if ( start.get( ) != NULL ) + { + m_startId = cid; + m_startInfo = startInfo; + } +} + +string RelatedMultipart::getContentType( ) +{ + string type = "multipart/related;"; + + RelatedPartPtr start = getPart( getStartId( ) ); + if ( start.get( ) != NULL ) + { + type += "start=\"" + getStartId( ) + "\";"; + + string startType = start->getContentType( ); + size_t pos = startType.find( ";" ); + if ( pos != string::npos ) + startType = startType.substr( 0, pos ); + + type += "type=\"" + startType + "\";"; + } + type += "boundary=\"" + m_boundary + "\";"; + type += "start-info=\"" + m_startInfo + "\""; + + return type; +} + +boost::shared_ptr< istringstream > RelatedMultipart::toStream( ) +{ + string buf; + + // Output the start part first + buf += "\r\n--" + m_boundary + "\r\n"; + RelatedPartPtr part = getPart( getStartId( ) ); + if ( part.get( ) != NULL ) + { + buf += part->toString( getStartId( ) ); + } + + for ( map< string, RelatedPartPtr >::iterator it = m_parts.begin( ); + it != m_parts.end( ); ++it ) + { + if ( it->first != getStartId( ) ) + { + buf += "\r\n--" + m_boundary + "\r\n"; + buf += it->second->toString( it->first ); + } + } + + buf += "\r\n--" + m_boundary + "--\r\n"; + + boost::shared_ptr< istringstream > is( new istringstream( buf ) ); + return is; +} + +string RelatedMultipart::createPartId( const string& name ) +{ + stringstream tmpStream(name); + tmpStream << "*"; + tmpStream << random_generator()(); + tmpStream << "@libcmis.sourceforge.net"; + + return tmpStream.str(); +} + +boost::shared_ptr< istream > getStreamFromNode( xmlNodePtr node, RelatedMultipart& multipart ) +{ + boost::shared_ptr< stringstream > stream; + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "Include" ) ) ) + { + // Get the content from the multipart + xmlChar* value = xmlGetProp( child, BAD_CAST( "href" ) ); + string href( ( char* )value ); + xmlFree( value ); + // Get the Content ID from the href (cid:content-id) + string id = href; + if ( href.substr( 0, 4 ) == "cid:" ) + { + id = href.substr( 4 ); + // URL-decode the id + id = libcmis::unescape( id ); + } + RelatedPartPtr part = multipart.getPart( id ); + if ( part != NULL ) + stream.reset( new stringstream( part->getContent( ) ) ); + } + } + + // If there was no xop:Include, then use the content as base64 data + if ( stream.get( ) == NULL ) + { + xmlChar* content = xmlNodeGetContent( node ); + + stream.reset( new stringstream( ) ); + libcmis::EncodedData decoder( stream.get( ) ); + decoder.setEncoding( "base64" ); + decoder.decode( ( void* )content, 1, xmlStrlen( content ) ); + decoder.finish( ); + + xmlFree( content ); + } + return stream; +} diff --git a/src/libcmis/ws-relatedmultipart.hxx b/src/libcmis/ws-relatedmultipart.hxx new file mode 100644 index 0000000..cd17e73 --- /dev/null +++ b/src/libcmis/ws-relatedmultipart.hxx @@ -0,0 +1,138 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_RELATEDMULTIPART_HXX_ +#define _WS_RELATEDMULTIPART_HXX_ + +#include <exception> +#include <map> +#include <string> +#include <sstream> +#include <vector> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +class RelatedPart +{ + private: + std::string m_name; + std::string m_contentType; + std::string m_content; + + public: + RelatedPart( std::string& name, std::string& type, std::string& content ); + ~RelatedPart( ) { }; + + std::string getName( ) { return m_name; } + std::string getContentType( ) { return m_contentType; } + std::string getContent( ) { return m_content; } + + /** Create the string to place between the boundaries in the multipart. + + \param cid the content Id to output + */ + std::string toString( std::string cid ); +}; +typedef boost::shared_ptr< RelatedPart > RelatedPartPtr; + +/** Represents a multipart/related content as specified by RFC-2387. + */ +class RelatedMultipart +{ + private: + + std::string m_startId; + std::string m_startInfo; + std::map< std::string, RelatedPartPtr > m_parts; + std::string m_boundary; + + public: + + /** Create a multipart/related from scratch (most probably + to output it later). + */ + RelatedMultipart( ); + + /** Parse a multipart body to extract the entries from it. + */ + RelatedMultipart( const std::string& body, const std::string& contentType ); + + /** Get the Content ids of all the parts; + */ + std::vector< std::string > getIds( ); + + /** Get the entry corresponding to the given ID. + */ + RelatedPartPtr getPart( std::string& cid ); + + /** Get the id of the multipart start entry. + */ + std::string& getStartId( ) { return m_startId; } + + std::string& getStartInfo( ) { return m_startInfo; } + + /** Add an entry to the multipart and returns the content ID that + was created for it. + */ + std::string addPart( RelatedPartPtr part ); + + /** Define the start of the multipart. That method needs to be + called before running toString(): the output order of the entries + is not guaranteed. + + \param cid the Content-Id of the start entry + \param startInfo the type to use as start-info in the Content-Type + */ + void setStart( std::string& cid, std::string& startInfo ); + + /** Compute the content type for the multipart object to set as the + Content-Type HTTP header. + */ + std::string getContentType( ); + + /** Dump the multipart to an input stream: this can be provided as is as + an HTTP post request body. + */ + boost::shared_ptr< std::istringstream > toStream( ); + + /** Provide an access to the boundary token for the unit tests. + */ + std::string getBoundary( ) { return m_boundary; } + + private: + + /** Generate a content id, using an entry name and some random uuid. + */ + std::string createPartId( const std::string& name ); +}; + +/** Extract stream from xs:base64Binary node using either xop:Include or base64 encoded data. + */ +boost::shared_ptr< std::istream > getStreamFromNode( xmlNodePtr node, RelatedMultipart& multipart ); + +#endif diff --git a/src/libcmis/ws-repositoryservice.cxx b/src/libcmis/ws-repositoryservice.cxx new file mode 100644 index 0000000..700078a --- /dev/null +++ b/src/libcmis/ws-repositoryservice.cxx @@ -0,0 +1,136 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-repositoryservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; + +RepositoryService::RepositoryService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +RepositoryService::RepositoryService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "RepositoryService" ) ) +{ +} + +RepositoryService::RepositoryService( const RepositoryService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +RepositoryService::~RepositoryService( ) +{ +} + +RepositoryService& RepositoryService::operator=( const RepositoryService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +map< string, string > RepositoryService::getRepositories( ) +{ + map< string, string > repositories; + + GetRepositoriesRequest request; + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + + if ( responses.size() == 1 ) + { + GetRepositoriesResponse* response = dynamic_cast< GetRepositoriesResponse* >( responses.front( ).get( ) ); + if ( response != NULL ) + { + repositories = response->getRepositories(); + } + } + return repositories; +} + +libcmis::RepositoryPtr RepositoryService::getRepositoryInfo( string id ) +{ + libcmis::RepositoryPtr repository; + + GetRepositoryInfoRequest request( id ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetRepositoryInfoResponse* response = dynamic_cast< GetRepositoryInfoResponse* >( resp ); + if ( response != NULL ) + repository = response->getRepository( ); + } + + return repository; +} + +libcmis::ObjectTypePtr RepositoryService::getTypeDefinition( string repoId, string typeId ) +{ + libcmis::ObjectTypePtr type; + + GetTypeDefinitionRequest request( repoId, typeId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetTypeDefinitionResponse* response = dynamic_cast< GetTypeDefinitionResponse* >( resp ); + if ( response != NULL ) + type = response->getType( ); + } + + return type; +} + +vector< libcmis::ObjectTypePtr > RepositoryService::getTypeChildren( string repoId, string typeId ) +{ + vector< libcmis::ObjectTypePtr > children; + + GetTypeChildrenRequest request( repoId, typeId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetTypeChildrenResponse* response = dynamic_cast< GetTypeChildrenResponse* >( resp ); + if ( response != NULL ) + children = response->getChildren( ); + } + + return children; +} diff --git a/src/libcmis/ws-repositoryservice.hxx b/src/libcmis/ws-repositoryservice.hxx new file mode 100644 index 0000000..4ef794f --- /dev/null +++ b/src/libcmis/ws-repositoryservice.hxx @@ -0,0 +1,71 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_REPOSITORYSERVICE_HXX_ +#define _WS_REPOSITORYSERVICE_HXX_ + +#include <map> +#include <string> +#include <vector> + +#include <libcmis/repository.hxx> + +#include "base-session.hxx" +#include "ws-soap.hxx" + +class WSSession; + +class RepositoryService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + RepositoryService( WSSession* session ); + RepositoryService( const RepositoryService& copy ); + ~RepositoryService( ); + + RepositoryService& operator=( const RepositoryService& copy ); + + std::map< std::string, std::string > getRepositories( ); + + /** Get the repository information based on its identifier. + */ + libcmis::RepositoryPtr getRepositoryInfo( std::string id ); + + libcmis::ObjectTypePtr getTypeDefinition( std::string repoId, std::string typeId ); + + std::vector< libcmis::ObjectTypePtr > getTypeChildren( std::string repoId, std::string typeId ); + + private: + + RepositoryService(); +}; + +#endif diff --git a/src/libcmis/ws-requests.cxx b/src/libcmis/ws-requests.cxx new file mode 100644 index 0000000..e34d59e --- /dev/null +++ b/src/libcmis/ws-requests.cxx @@ -0,0 +1,877 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-requests.hxx" + +#include <libcmis/xml-utils.hxx> + +#include "ws-document.hxx" +#include "ws-folder.hxx" +#include "ws-object.hxx" +#include "ws-object-type.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +CmisSoapFaultDetail::CmisSoapFaultDetail( xmlNodePtr node ) : + SoapFaultDetail( ), + m_type( ), + m_code( 0 ), + m_message( ) +{ + // Extract the type, code and message + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + xmlChar* content = xmlNodeGetContent( child ); + string value( ( char * )content ); + xmlFree( content ); + + if ( xmlStrEqual( child->name, BAD_CAST( "type" ) ) ) + { + m_type = value; + } + else if ( xmlStrEqual( child->name, BAD_CAST( "code" ) ) ) + { + try + { + m_code = libcmis::parseInteger( value ); + } + catch ( const libcmis::Exception& ) + { + // Simply leave the default error code if unparsable + } + } + else if ( xmlStrEqual( child->name, BAD_CAST( "message" ) ) ) + { + m_message = value; + } + } +} + +libcmis::Exception CmisSoapFaultDetail::toException( ) +{ + libcmis::Exception e( m_message, m_type ); + return e; +} + +boost::shared_ptr< libcmis::Exception > getCmisException( const SoapFault& fault ) +{ + boost::shared_ptr< libcmis::Exception > exception; + + vector< SoapFaultDetailPtr > details = fault.getDetail( ); + for ( vector< SoapFaultDetailPtr >::iterator it = details.begin( ); + it != details.end( ) && exception.get( ) == NULL; ++ it ) + { + boost::shared_ptr< CmisSoapFaultDetail > cmisDetail = boost::dynamic_pointer_cast< CmisSoapFaultDetail >( *it ); + if ( cmisDetail.get( ) != NULL ) + exception.reset( new libcmis::Exception( cmisDetail->toException( ) ) ); + } + + return exception; +} + +void writeCmismStream( xmlTextWriterPtr writer, RelatedMultipart& multipart, boost::shared_ptr< ostream > os, string& contentType, string filename ) +{ + // Get the stream as a string + istream is( os->rdbuf( ) ); + is.seekg( 0, ios::end ); + long size = is.tellg( ); + is.seekg( 0, ios::beg ); + + char* buf = new char[ size ]; + is.read( buf, size ); + string content( buf, size ); + delete[ ] buf; + + xmlTextWriterWriteFormatElement( writer, BAD_CAST( "cmism:length" ), "%ld", static_cast<long int>(content.size( )) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:mimeType" ), BAD_CAST( contentType.c_str( ) ) ); + if ( !filename.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:filename" ), BAD_CAST( filename.c_str( ) ) ); + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:stream" ) ); + + string name( "stream" ); + RelatedPartPtr streamPart( new RelatedPart( name, contentType, content ) ); + string partHref = "cid:"; + partHref += multipart.addPart( streamPart ); + + xmlTextWriterStartElement( writer, BAD_CAST( "xop:Include" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:xop" ), BAD_CAST( "http://www.w3.org/2004/08/xop/include" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "href" ), BAD_CAST( partHref.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // xop:Include + xmlTextWriterEndElement( writer ); // cmism:stream +} + +SoapFaultDetailPtr CmisSoapFaultDetail::create( xmlNodePtr node ) +{ + return SoapFaultDetailPtr( new CmisSoapFaultDetail( node ) ); +} + +void GetRepositoriesRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getRepositories" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetRepositoriesResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + GetRepositoriesResponse* response = new GetRepositoriesResponse( ); + + // Look for the cmiss:repositories children + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "repositories" ) ) ) + { + string id; + string name; + + // Look for repositoryId and repositoryName + for ( xmlNodePtr repoChild = child->children; repoChild; repoChild = repoChild->next ) + { + xmlChar* content = xmlNodeGetContent( repoChild ); + string value( ( char* ) content ); + xmlFree( content ); + + if ( xmlStrEqual( repoChild->name, BAD_CAST( "repositoryId" ) ) ) + { + id = value; + } + else if ( xmlStrEqual( repoChild->name, BAD_CAST( "repositoryName" ) ) ) + { + name = value; + } + + } + + if ( !id.empty( ) ) + response->m_repositories[ id ] = name; + } + } + + return SoapResponsePtr( response ); +} + +void GetRepositoryInfoRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getRepositoryInfo" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_id.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetRepositoryInfoResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + GetRepositoryInfoResponse* response = new GetRepositoryInfoResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "repositoryInfo" ) ) ) + { + libcmis::RepositoryPtr repository( new libcmis::Repository( child ) ); + response->m_repository = repository; + } + } + + return SoapResponsePtr( response ); +} + +void GetTypeDefinitionRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getTypeDefinition" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:typeId" ), BAD_CAST( m_typeId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetTypeDefinitionResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetTypeDefinitionResponse* response = new GetTypeDefinitionResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "type" ) ) ) + { + libcmis::ObjectTypePtr type( new WSObjectType( wsSession, child ) ); + response->m_type = type; + } + } + + return SoapResponsePtr( response ); +} + +void GetTypeChildrenRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getTypeChildren" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:typeId" ), BAD_CAST( m_typeId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includePropertyDefinitions" ), BAD_CAST( "true" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetTypeChildrenResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetTypeChildrenResponse* response = new GetTypeChildrenResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "types" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "types" ) ) ) + { + libcmis::ObjectTypePtr type( new WSObjectType( wsSession, gdchild ) ); + response->m_children.push_back( type ); + } + } + } + } + + return SoapResponsePtr( response ); +} + +void GetObjectRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getObject" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_id.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + + // Ask for renditions... some servers like Alfresco are providing them only this way + // and it saves time (another HTTP request) anyway. + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetObjectResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetObjectResponse* response = new GetObjectResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "object" ) ) ) + { + libcmis::ObjectPtr object; + WSObject tmp( wsSession, child ); + if ( tmp.getBaseType( ) == "cmis:folder" ) + { + object.reset( new WSFolder( tmp ) ); + } + else if ( tmp.getBaseType( ) == "cmis:document" ) + { + object.reset( new WSDocument( tmp ) ); + } + else + { + // This should never happen... but who knows if the standard is 100% repected? + object.reset( new WSObject( wsSession, child ) ); + } + response->m_object = object; + } + } + + return SoapResponsePtr( response ); +} + +void GetObjectByPathRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getObjectByPath" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:path" ), BAD_CAST( m_path.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +void UpdatePropertiesRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:updateProperties" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + if ( !m_changeToken.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:changeToken" ), BAD_CAST( m_changeToken.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + if( property->getPropertyType( )->isUpdatable( ) ) + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr UpdatePropertiesResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + UpdatePropertiesResponse* response = new UpdatePropertiesResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_id = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void DeleteObjectRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:deleteObject" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + string allVersionsStr( "false" ); + if ( m_allVersions ) + allVersionsStr = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:allVersions" ), BAD_CAST( allVersionsStr.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +void DeleteTreeRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:deleteTree" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + + string allVersionsStr( "false" ); + if ( m_allVersions ) + allVersionsStr = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:allVersions" ), BAD_CAST( allVersionsStr.c_str( ) ) ); + + string unfileStr( "" ); + switch ( m_unfile ) + { + case libcmis::UnfileObjects::Unfile: + unfileStr = "unfile"; + break; + case libcmis::UnfileObjects::DeleteSingleFiled: + unfileStr = "deletesinglefiled"; + break; + case libcmis::UnfileObjects::Delete: + unfileStr = "delete"; + break; + default: + break; + } + if ( !unfileStr.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:unfileObjects" ), BAD_CAST( unfileStr.c_str( ) ) ); + + string continueStr( "false" ); + if ( m_continueOnFailure ) + continueStr = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:continueOnFailure" ), BAD_CAST( continueStr.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr DeleteTreeResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + DeleteTreeResponse* response = new DeleteTreeResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "failedToDelete" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "objectIds" ) ) ) + { + xmlChar* content = xmlNodeGetContent( gdchild ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_failedIds.push_back( value ); + } + } + } + } + } + + return SoapResponsePtr( response ); +} + +void MoveObjectRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:moveObject" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:targetFolderId" ), BAD_CAST( m_destId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:sourceFolderId" ), BAD_CAST( m_srcId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +void GetContentStreamRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getContentStream" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetContentStreamResponse::create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* ) +{ + GetContentStreamResponse* response = new GetContentStreamResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "contentStream" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "stream" ) ) ) + { + xmlChar* content = xmlNodeGetContent( gdchild ); + if ( content != NULL ) + { + // We can either have directly the base64 encoded data or + // an <xop:Include> pointing to another part of the multipart + response->m_stream = getStreamFromNode( gdchild, multipart ); + } + xmlFree( content ); + } + } + } + } + + return SoapResponsePtr( response ); +} + +void GetObjectParentsRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getObjectParents" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetObjectParentsResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetObjectParentsResponse* response = new GetObjectParentsResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "parents" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "object" ) ) ) + { + libcmis::FolderPtr parent; + WSObject tmp( wsSession, gdchild ); + if ( tmp.getBaseType( ) == "cmis:folder" ) + { + parent.reset( new WSFolder( tmp ) ); + response->m_parents.push_back( parent ); + } + } + } + } + } + + return SoapResponsePtr( response ); +} + +void GetChildrenRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getChildren" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( "*" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetChildrenResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetChildrenResponse* response = new GetChildrenResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objects" ) ) ) + { + for ( xmlNodePtr gdchild = child->children; gdchild; gdchild = gdchild->next ) + { + if ( xmlStrEqual( gdchild->name, BAD_CAST( "objects" ) ) ) + { + for ( xmlNodePtr gdgdchild = gdchild->children; gdgdchild; gdgdchild = gdgdchild->next ) + { + if ( xmlStrEqual( gdgdchild->name, BAD_CAST( "object" ) ) ) + { + libcmis::ObjectPtr object; + WSObject tmp( wsSession, gdgdchild ); + if ( tmp.getBaseType( ) == "cmis:folder" ) + { + object.reset( new WSFolder( tmp ) ); + } + else if ( tmp.getBaseType( ) == "cmis:document" ) + { + object.reset( new WSDocument( tmp ) ); + } + else + { + // This should never happen... but who knows if the standard is 100% repected? + object.reset( new WSObject( wsSession, gdgdchild ) ); + } + response->m_children.push_back( object ); + } + } + } + } + } + } + + return SoapResponsePtr( response ); +} + +void CreateFolderRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:createFolder" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr CreateFolderResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + CreateFolderResponse* response = new CreateFolderResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_id = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void CreateDocumentRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:createDocument" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:folderId" ), BAD_CAST( m_folderId.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:contentStream" ) ); + writeCmismStream( writer, m_multipart, m_stream, m_contentType, m_filename ); + xmlTextWriterEndElement( writer ); // cmism:contentStream + + xmlTextWriterEndElement( writer ); +} + +void SetContentStreamRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:setContentStream" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + string overwrite( "false" ); + if ( m_overwrite ) + overwrite = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:overwriteFlag" ), BAD_CAST( overwrite.c_str( ) ) ); + + if ( !m_changeToken.empty( ) ) + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:changeToken" ), BAD_CAST( m_changeToken.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:contentStream" ) ); + writeCmismStream( writer, m_multipart, m_stream, m_contentType, m_filename ); + + xmlTextWriterEndElement( writer ); // cmism:contentStream + + xmlTextWriterEndElement( writer ); +} + +void GetRenditionsRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getRenditions" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:renditionFilter" ), BAD_CAST( m_filter.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetRenditionsResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + GetRenditionsResponse* response = new GetRenditionsResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "renditions" ) ) ) + { + libcmis::RenditionPtr rendition( new libcmis::Rendition( child ) ); + response->m_renditions.push_back( rendition ); + } + } + + return SoapResponsePtr( response ); +} + +void CheckOutRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:checkOut" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr CheckOutResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + CheckOutResponse* response = new CheckOutResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_objectId = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void CancelCheckOutRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:cancelCheckOut" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +void CheckInRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:checkIn" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + + string major = "false"; + if ( m_isMajor ) + major = "true"; + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:major" ), BAD_CAST( major.c_str( ) ) ); + + if ( m_properties.empty( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:properties" ) ); + for ( PropertyPtrMap::const_iterator it = m_properties.begin( ); + it != m_properties.end( ); ++it ) + { + libcmis::PropertyPtr property = it->second; + property->toXml( writer ); + } + xmlTextWriterEndElement( writer ); // cmis:properties + } + + if ( m_stream.get( ) ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:contentStream" ) ); + writeCmismStream( writer, m_multipart, m_stream, m_contentType, m_fileName ); + xmlTextWriterEndElement( writer ); // cmism:contentStream + } + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:checkinComment" ), BAD_CAST( m_comment.c_str( ) ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr CheckInResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* ) +{ + CheckInResponse* response = new CheckInResponse( ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objectId" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + if ( content != NULL ) + { + string value( ( char* ) content ); + xmlFree( content ); + response->m_objectId = value; + } + } + } + + return SoapResponsePtr( response ); +} + +void GetAllVersionsRequest::toXml( xmlTextWriterPtr writer ) +{ + xmlTextWriterStartElement( writer, BAD_CAST( "cmism:getAllVersions" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:repositoryId" ), BAD_CAST( m_repositoryId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:objectId" ), BAD_CAST( m_objectId.c_str( ) ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "cmism:includeAllowableActions" ), BAD_CAST( "true" ) ); + + xmlTextWriterEndElement( writer ); +} + +SoapResponsePtr GetAllVersionsResponse::create( xmlNodePtr node, RelatedMultipart&, SoapSession* session ) +{ + GetAllVersionsResponse* response = new GetAllVersionsResponse( ); + WSSession* wsSession = dynamic_cast< WSSession* >( session ); + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "objects" ) ) ) + { + WSObject tmp( wsSession, child ); + if ( tmp.getBaseType( ) == "cmis:document" ) + { + libcmis::DocumentPtr object( new WSDocument( tmp ) ); + response->m_objects.push_back( object ); + } + } + } + + return SoapResponsePtr( response ); +} diff --git a/src/libcmis/ws-requests.hxx b/src/libcmis/ws-requests.hxx new file mode 100644 index 0000000..1782768 --- /dev/null +++ b/src/libcmis/ws-requests.hxx @@ -0,0 +1,786 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_REQUESTS_HXX_ +#define _WS_REQUESTS_HXX_ + +#include <istream> +#include <map> +#include <ostream> +#include <string> +#include <vector> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include <libcmis/document.hxx> +#include <libcmis/exception.hxx> +#include <libcmis/folder.hxx> +#include <libcmis/object.hxx> +#include <libcmis/object-type.hxx> +#include <libcmis/repository.hxx> + +#include "ws-soap.hxx" + +class CmisSoapFaultDetail : public SoapFaultDetail +{ + private: + std::string m_type; + long m_code; + std::string m_message; + + CmisSoapFaultDetail( xmlNodePtr node ); + + public: + ~CmisSoapFaultDetail( ) noexcept { }; + + std::string getType( ) { return m_type; } + int getCode( ) { return m_code; } + std::string getMessage( ) { return m_message; } + + libcmis::Exception toException( ); + + static SoapFaultDetailPtr create( xmlNodePtr node ); +}; + +boost::shared_ptr< libcmis::Exception > getCmisException( const SoapFault& fault ); + +void writeCmismStream( xmlTextWriterPtr writer, RelatedMultipart& multipart, + boost::shared_ptr< std::ostream >, std::string& contentType, std::string filename ); + +/** getRepositories request. + */ +class GetRepositoriesRequest : public SoapRequest +{ + public: + GetRepositoriesRequest( ) { }; + ~GetRepositoriesRequest( ) { }; + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRepositoriesResponse : public SoapResponse +{ + private: + std::map< std::string, std::string > m_repositories; + + GetRepositoriesResponse( ) : SoapResponse( ), m_repositories( ) { }; + + public: + + /** Parse cmism:getRepositoriesResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::map< std::string, std::string > getRepositories( ) { return m_repositories; } +}; + +class GetRepositoryInfoRequest : public SoapRequest +{ + private: + std::string m_id; + + public: + GetRepositoryInfoRequest( std::string id ) : m_id( id ) { }; + ~GetRepositoryInfoRequest( ) { }; + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRepositoryInfoResponse : public SoapResponse +{ + private: + libcmis::RepositoryPtr m_repository; + + GetRepositoryInfoResponse( ) : SoapResponse( ), m_repository( ) { }; + + public: + + /** Parse cmism:getRepositoriesResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + libcmis::RepositoryPtr getRepository( ) { return m_repository; } +}; + +class GetTypeDefinitionRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_typeId; + + public: + GetTypeDefinitionRequest( std::string repoId, std::string typeId ) : + m_repositoryId( repoId ), + m_typeId( typeId ) + { + } + + ~GetTypeDefinitionRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetTypeDefinitionResponse : public SoapResponse +{ + private: + libcmis::ObjectTypePtr m_type; + + GetTypeDefinitionResponse( ) : SoapResponse( ), m_type( ) { } + + public: + + /** Parse cmism:getTypeDefinitionResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + libcmis::ObjectTypePtr getType( ) { return m_type; } +}; + +class GetTypeChildrenRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_typeId; + + public: + GetTypeChildrenRequest( std::string repoId, std::string typeId ) : + m_repositoryId( repoId ), + m_typeId( typeId ) + { + } + + ~GetTypeChildrenRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetTypeChildrenResponse : public SoapResponse +{ + private: + std::vector< libcmis::ObjectTypePtr > m_children; + + GetTypeChildrenResponse( ) : SoapResponse( ), m_children( ) { } + + public: + + /** Parse cmism:getTypeChildrenResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::ObjectTypePtr > getChildren( ) { return m_children; } +}; + +class GetObjectRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_id; + + public: + GetObjectRequest( std::string repoId, std::string id ) : + m_repositoryId( repoId ), + m_id( id ) + { + } + + ~GetObjectRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetObjectResponse : public SoapResponse +{ + private: + libcmis::ObjectPtr m_object; + + GetObjectResponse( ) : SoapResponse( ), m_object( ) { } + + public: + + /** Parse cmism:getObjectResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + libcmis::ObjectPtr getObject( ) { return m_object; } +}; + +class GetObjectByPathRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_path; + + public: + GetObjectByPathRequest( std::string repoId, std::string path ) : + m_repositoryId( repoId ), + m_path( path ) + { + } + + ~GetObjectByPathRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class UpdatePropertiesRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + std::string m_changeToken; + + public: + UpdatePropertiesRequest( std::string repoId, std::string objectId, + const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string changeToken ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_properties( properties ), + m_changeToken( changeToken ) + { + } + + ~UpdatePropertiesRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class UpdatePropertiesResponse : public SoapResponse +{ + private: + std::string m_id; + + UpdatePropertiesResponse( ) : SoapResponse( ), m_id( ) { } + + public: + + /** Parse cmism:updatePropertiesResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_id; } +}; + +class DeleteObjectRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + bool m_allVersions; + + public: + DeleteObjectRequest( std::string repoId, std::string objectId, bool allVersions ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_allVersions( allVersions ) + { + } + + ~DeleteObjectRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class DeleteTreeRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_folderId; + bool m_allVersions; + libcmis::UnfileObjects::Type m_unfile; + bool m_continueOnFailure; + + public: + DeleteTreeRequest( std::string repoId, + std::string folderId, + bool allVersions, + libcmis::UnfileObjects::Type unfile, + bool continueOnFailure ) : + m_repositoryId( repoId ), + m_folderId( folderId ), + m_allVersions( allVersions ), + m_unfile( unfile ), + m_continueOnFailure( continueOnFailure ) + { + } + + ~DeleteTreeRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class DeleteTreeResponse : public SoapResponse +{ + private: + std::vector< std::string > m_failedIds; + + DeleteTreeResponse( ) : SoapResponse( ), m_failedIds( ) { } + + public: + + /** Parse cmism:deleteTreeResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< std::string > getFailedIds( ) { return m_failedIds; } +}; + +class MoveObjectRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + std::string m_destId; + std::string m_srcId; + + public: + MoveObjectRequest( std::string repoId, std::string objectId, std::string destId, std::string srcId ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_destId( destId ), + m_srcId( srcId ) + { + } + + ~MoveObjectRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetContentStreamRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + GetContentStreamRequest( std::string repoId, std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~GetContentStreamRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetContentStreamResponse : public SoapResponse +{ + private: + boost::shared_ptr< std::istream > m_stream; + + GetContentStreamResponse( ) : SoapResponse( ), m_stream( ) { } + + public: + + /** Parse cmism:getContentStreamResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + boost::shared_ptr< std::istream> getStream( ) { return m_stream; } +}; + +class GetObjectParentsRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + GetObjectParentsRequest( std::string repoId, + std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~GetObjectParentsRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetObjectParentsResponse : public SoapResponse +{ + private: + std::vector< libcmis::FolderPtr > m_parents; + + GetObjectParentsResponse( ) : SoapResponse( ), m_parents( ) { } + + public: + + /** Parse cmism:getObjectParentsResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::FolderPtr > getParents( ) { return m_parents; } +}; + +class GetChildrenRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_folderId; + + public: + GetChildrenRequest( std::string repoId, + std::string folderId ) : + m_repositoryId( repoId ), + m_folderId( folderId ) + { + } + + ~GetChildrenRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetChildrenResponse : public SoapResponse +{ + private: + std::vector< libcmis::ObjectPtr > m_children; + + GetChildrenResponse( ) : SoapResponse( ), m_children( ) { } + + public: + + /** Parse cmism:getChildrenResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::ObjectPtr > getChildren( ) { return m_children; } +}; + +class CreateFolderRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + std::string m_folderId; + + public: + CreateFolderRequest( std::string repoId, + const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId ) : + m_repositoryId( repoId ), + m_properties( properties ), + m_folderId( folderId ) + { + } + + ~CreateFolderRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CreateFolderResponse : public SoapResponse +{ + private: + std::string m_id; + + CreateFolderResponse( ) : SoapResponse( ), m_id( ) { } + + public: + + /** Parse cmism:createFolderResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_id; } +}; + +class CreateDocumentRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + std::string m_folderId; + boost::shared_ptr< std::ostream > m_stream; + std::string m_contentType; + std::string m_filename; + + public: + CreateDocumentRequest( std::string repoId, + const std::map< std::string, libcmis::PropertyPtr >& properties, + std::string folderId, boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string filename ) : + m_repositoryId( repoId ), + m_properties( properties ), + m_folderId( folderId ), + m_stream( stream ), + m_contentType( contentType ), + m_filename( filename ) + { + } + + ~CreateDocumentRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class SetContentStreamRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + bool m_overwrite; + std::string m_changeToken; + boost::shared_ptr< std::ostream > m_stream; + std::string m_contentType; + std::string m_filename; + + public: + SetContentStreamRequest( std::string repoId, + std::string objectId, + bool overwrite, + std::string changeToken, + boost::shared_ptr< std::ostream > stream, + std::string contentType, + std::string filename ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_overwrite( overwrite ), + m_changeToken( changeToken ), + m_stream( stream ), + m_contentType( contentType ), + m_filename( filename ) + { + } + + ~SetContentStreamRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRenditionsRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + std::string m_filter; + + public: + GetRenditionsRequest( std::string repoId, std::string objectId, std::string filter ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_filter( filter ) + { + } + + ~GetRenditionsRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetRenditionsResponse : public SoapResponse +{ + private: + std::vector< libcmis::RenditionPtr > m_renditions; + + GetRenditionsResponse( ) : SoapResponse( ), m_renditions( ) { } + + public: + + /** Parse cmism:getRenditionsResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::RenditionPtr > getRenditions( ) { return m_renditions; } +}; + +class CheckOutRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + CheckOutRequest( std::string repoId, + std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~CheckOutRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CheckOutResponse : public SoapResponse +{ + private: + std::string m_objectId; + + CheckOutResponse( ) : SoapResponse( ), m_objectId( ) { } + + public: + + /** Parse cmism:checkOutResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_objectId; } +}; + +class CancelCheckOutRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + CancelCheckOutRequest( std::string repoId, + std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~CancelCheckOutRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CheckInRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + bool m_isMajor; + const std::map< std::string, libcmis::PropertyPtr >& m_properties; + boost::shared_ptr< std::ostream > m_stream; + std::string m_contentType; + std::string m_fileName; + std::string m_comment; + + public: + CheckInRequest( std::string repoId, + std::string objectId, bool isMajor, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, + std::string contentType, std::string fileName, std::string comment ) : + m_repositoryId( repoId ), + m_objectId( objectId ), + m_isMajor( isMajor ), + m_properties( properties ), + m_stream( stream ), + m_contentType( contentType ), + m_fileName( fileName ), + m_comment( comment ) + { + } + + ~CheckInRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class CheckInResponse : public SoapResponse +{ + private: + std::string m_objectId; + + CheckInResponse( ) : SoapResponse( ), m_objectId( ) { } + + public: + + /** Parse cmism:checkInResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::string getObjectId( ) { return m_objectId; } +}; + +class GetAllVersionsRequest : public SoapRequest +{ + private: + std::string m_repositoryId; + std::string m_objectId; + + public: + GetAllVersionsRequest( std::string repoId, std::string objectId ) : + m_repositoryId( repoId ), + m_objectId( objectId ) + { + } + + ~GetAllVersionsRequest( ) { } + + void toXml( xmlTextWriterPtr writer ); +}; + +class GetAllVersionsResponse : public SoapResponse +{ + private: + std::vector< libcmis::DocumentPtr > m_objects; + + GetAllVersionsResponse( ) : SoapResponse( ), m_objects( ) { } + + public: + + /** Parse cmism:getAllVersionsResponse. This function + assumes that the node is the expected one: this is + normally ensured by the SoapResponseFactory. + */ + static SoapResponsePtr create( xmlNodePtr node, RelatedMultipart& multipart, SoapSession* session ); + + std::vector< libcmis::DocumentPtr > getObjects( ) { return m_objects; } +}; + +#endif diff --git a/src/libcmis/ws-session.cxx b/src/libcmis/ws-session.cxx new file mode 100644 index 0000000..3b1b291 --- /dev/null +++ b/src/libcmis/ws-session.cxx @@ -0,0 +1,417 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-session.hxx" + +#include <sstream> + +#include <boost/date_time.hpp> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include <libcmis/xml-utils.hxx> + +#include "ws-requests.hxx" + +using namespace std; + +WSSession::WSSession( string bindingUrl, string repositoryId, string username, + string password, bool noSslCheck, libcmis::OAuth2DataPtr oauth2, + bool verbose ) : + BaseSession( bindingUrl, repositoryId, username, password, noSslCheck, oauth2, verbose ), + m_servicesUrls( ), + m_navigationService( NULL ), + m_objectService( NULL ), + m_repositoryService( NULL ), + m_versioningService( NULL ), + m_responseFactory( ) +{ + // We don't want to have the HTTP exceptions as the errors are coming + // back as SoapFault elements. + setNoHttpErrors( true ); + initialize( ); +} + +WSSession::WSSession( string bindingUrl, string repositoryId, + const HttpSession& httpSession, + libcmis::HttpResponsePtr response ) : + BaseSession( bindingUrl, repositoryId, httpSession ), + m_servicesUrls( ), + m_navigationService( NULL ), + m_objectService( NULL ), + m_repositoryService( NULL ), + m_versioningService( NULL ), + m_responseFactory( ) +{ + // We don't want to have the HTTP exceptions as the errors are coming + // back as SoapFault elements. + setNoHttpErrors( true ); + initialize( response ); +} + +WSSession::WSSession( ) : + BaseSession( ), + m_servicesUrls( ), + m_navigationService( NULL ), + m_objectService( NULL ), + m_repositoryService( NULL ), + m_versioningService( NULL ), + m_responseFactory( ) +{ + setNoHttpErrors( true ); +} + +WSSession::~WSSession( ) +{ + delete m_navigationService; + delete m_objectService; + delete m_repositoryService; + delete m_versioningService; +} + +string WSSession::getWsdl( string url, libcmis::HttpResponsePtr response ) +{ + string buf; + if ( response ) + buf = response->getStream( )->str( ); + else + buf = httpGetRequest( url )->getStream( )->str( ); + + // Do we have a wsdl file? + bool isWsdl = false; + + xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), m_bindingUrl.c_str(), NULL, 0 ); + if ( NULL != doc ) + { + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc ); + libcmis::registerCmisWSNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + string definitionsXPath( "/wsdl:definitions" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( definitionsXPath.c_str() ), xpathCtx ); + + isWsdl = ( xpathObj != NULL ) && ( xpathObj->nodesetval != NULL ) && ( xpathObj->nodesetval->nodeNr > 0 ); + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + xmlFreeDoc( doc ); + + // If we don't have a wsdl file we may have received an HTML explanation for it, + // try to add ?wsdl to the URL (last chance to get something) + if ( !isWsdl ) + { + if ( url.find( "?" ) == string::npos ) + url += "?"; + else + url += "&"; + url += "wsdl"; + + buf = httpGetRequest( url )->getStream( )->str( ); + } + + return buf; +} + +vector< SoapResponsePtr > WSSession::soapRequest( string& url, SoapRequest& request ) +{ + vector< SoapResponsePtr > responses; + + try + { + // Place the request in an envelope + RelatedMultipart& multipart = request.getMultipart( getUsername( ), getPassword( ) ); + libcmis::HttpResponsePtr response = httpPostRequest( url, *multipart.toStream( ).get( ), multipart.getContentType( ) ); + + string responseType; + map< string, string >::iterator it = response->getHeaders( ).find( "Content-Type" ); + if ( it != response->getHeaders( ).end( ) ) + { + responseType = it->second; + if ( string::npos != responseType.find( "multipart/related" ) ) + { + RelatedMultipart answer( response->getStream( )->str( ), responseType ); + + responses = getResponseFactory( ).parseResponse( answer ); + } + else if ( string::npos != responseType.find( "text/xml" ) ) + { + // Parse the envelope + string xml = response->getStream( )->str( ); + responses = getResponseFactory( ).parseResponse( xml ); + } + } + } + catch ( const SoapFault& fault ) + { + boost::shared_ptr< libcmis::Exception > cmisException = getCmisException( fault ); + if ( cmisException ) + { + throw *cmisException; + } + throw libcmis::Exception( fault.what( ), "runtime" ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + return responses; +} + +void WSSession::parseWsdl( string buf ) +{ + // parse the content + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( buf.c_str(), buf.size(), m_bindingUrl.c_str(), NULL, 0 ), xmlFreeDoc ); + + if ( bool( doc ) ) + { + // Check that we have a WSDL document + xmlNodePtr root = xmlDocGetRootElement( doc.get() ); + if ( !xmlStrEqual( root->name, BAD_CAST( "definitions" ) ) ) + throw libcmis::Exception( "Not a WSDL document" ); + + // Get all the services soap URLs + m_servicesUrls.clear( ); + + xmlXPathContextPtr xpathCtx = xmlXPathNewContext( doc.get() ); + libcmis::registerCmisWSNamespaces( xpathCtx ); + + if ( NULL != xpathCtx ) + { + string serviceXPath( "//wsdl:service" ); + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( serviceXPath.c_str() ), xpathCtx ); + + if ( xpathObj != NULL ) + { + int nbServices = 0; + if ( xpathObj->nodesetval ) + nbServices = xpathObj->nodesetval->nodeNr; + + for ( int i = 0; i < nbServices; i++ ) + { + // What service do we have here? + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + string name = libcmis::getXmlNodeAttributeValue( node, "name" ); + + // Gimme you soap:address location attribute + string locationXPath = serviceXPath + "[@name='" + name + "']/wsdl:port/soap:address/attribute::location"; + string location = libcmis::getXPathValue( xpathCtx, locationXPath ); + + m_servicesUrls[name] = location; + } + } + xmlXPathFreeObject( xpathObj ); + } + xmlXPathFreeContext( xpathCtx ); + } + else + throw libcmis::Exception( "Failed to parse service document" ); +} + +void WSSession::initializeResponseFactory( ) +{ + map< string, string > ns; + ns[ "wsssecurity" ] = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; + ns[ NS_SOAP_ENV_PREFIX ] = NS_SOAP_ENV_URL; + ns[ "cmism" ] = NS_CMISM_URL; + ns[ "cmisw" ] = NS_CMISW_URL; + ns[ "cmis" ] = NS_CMIS_URL; + m_responseFactory.setNamespaces( ns ); + m_responseFactory.setMapping( getResponseMapping() ); + m_responseFactory.setDetailMapping( getDetailMapping( ) ); + m_responseFactory.setSession( this ); +} + +void WSSession::initializeRepositories( map< string, string > repositories ) +{ + for ( map< string, string >::iterator it = repositories.begin( ); + it != repositories.end( ); ++it ) + { + string repoId = it->first; + m_repositories.push_back( getRepositoryService( ).getRepositoryInfo( repoId ) ); + } +} + +void WSSession::initialize( libcmis::HttpResponsePtr response ) +{ + if ( m_repositories.empty() ) + { + // Get the wsdl file + string buf; + try + { + buf = getWsdl( m_bindingUrl, response ); + } + catch ( const CurlException& e ) + { + throw e.getCmisException( ); + } + + parseWsdl( buf ); + initializeResponseFactory( ); + map< string, string > repositories = getRepositoryService( ).getRepositories( ); + initializeRepositories( repositories ); + } +} + +map< string, SoapResponseCreator > WSSession::getResponseMapping( ) +{ + map< string, SoapResponseCreator > mapping; + + mapping[ "{" + string( NS_CMISM_URL ) + "}getRepositoriesResponse" ] = &GetRepositoriesResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getRepositoryInfoResponse" ] = &GetRepositoryInfoResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getTypeDefinitionResponse" ] = &GetTypeDefinitionResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getTypeChildrenResponse" ] = &GetTypeChildrenResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getObjectResponse" ] = &GetObjectResponse::create; + // No need to create a GetObjectByPathResponse as it would do the same than GetObjectResponse + mapping[ "{" + string( NS_CMISM_URL ) + "}getObjectByPathResponse" ] = &GetObjectResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}updatePropertiesResponse" ] = &UpdatePropertiesResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}deleteTreeResponse" ] = &DeleteTreeResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getContentStreamResponse" ] = &GetContentStreamResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getObjectParentsResponse" ] = &GetObjectParentsResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getChildrenResponse" ] = &GetChildrenResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}createFolderResponse" ] = &CreateFolderResponse::create; + // Use the same response object than folders as it contains the same elements + mapping[ "{" + string( NS_CMISM_URL ) + "}createDocumentResponse" ] = &CreateFolderResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}checkOutResponse" ] = &CheckOutResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}checkInResponse" ] = &CheckInResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getAllVersionsResponse" ] = &GetAllVersionsResponse::create; + mapping[ "{" + string( NS_CMISM_URL ) + "}getRenditionsResponse" ] = &GetRenditionsResponse::create; + + return mapping; +} + +map< string, SoapFaultDetailCreator > WSSession::getDetailMapping( ) +{ + map< string, SoapFaultDetailCreator > mapping; + + mapping[ "{" + string( NS_CMISM_URL ) + "}cmisFault" ] = &CmisSoapFaultDetail::create; + + return mapping; +} + +string WSSession::getServiceUrl( string name ) +{ + string url; + + map< string, string >::iterator it = m_servicesUrls.find( name ); + if ( it != m_servicesUrls.end( ) ) + url = it->second; + + return url; +} + +RepositoryService& WSSession::getRepositoryService( ) +{ + if ( m_repositoryService == NULL ) + m_repositoryService = new RepositoryService( this ); + return *m_repositoryService; +} + +ObjectService& WSSession::getObjectService( ) +{ + if ( m_objectService == NULL ) + m_objectService = new ObjectService( this ); + return *m_objectService; +} + +NavigationService& WSSession::getNavigationService( ) +{ + if ( m_navigationService == NULL ) + m_navigationService = new NavigationService( this ); + return *m_navigationService; +} + +VersioningService& WSSession::getVersioningService( ) +{ + if ( m_versioningService == NULL ) + m_versioningService = new VersioningService( this ); + return *m_versioningService; +} + +libcmis::RepositoryPtr WSSession::getRepository( ) +{ + // Check if we already have the repository + libcmis::RepositoryPtr repo; + vector< libcmis::RepositoryPtr >::iterator it = m_repositories.begin(); + while ( !repo && it != m_repositories.end() ) + { + if ( ( *it )->getId() == m_repositoryId ) + repo = *it; + ++it; + } + + // We found nothing cached, so try to get it from the server + if ( !repo ) + { + repo = getRepositoryService( ).getRepositoryInfo( m_repositoryId ); + if ( repo ) + m_repositories.push_back( repo ); + } + + return repo; +} + +bool WSSession::setRepository( string repositoryId ) +{ + bool success = false; + try + { + libcmis::RepositoryPtr repo = getRepositoryService( ).getRepositoryInfo( repositoryId ); + if (repo && repo->getId( ) == repositoryId ) + m_repositoryId = repositoryId; + success = true; + } + catch ( const libcmis::Exception& ) + { + } + return success; +} + +libcmis::ObjectPtr WSSession::getObject( string id ) +{ + return getObjectService( ).getObject( getRepositoryId( ), id ); +} + +libcmis::ObjectPtr WSSession::getObjectByPath( string path ) +{ + return getObjectService( ).getObjectByPath( getRepositoryId( ), path ); +} + +libcmis::ObjectTypePtr WSSession::getType( string id ) +{ + return getRepositoryService( ).getTypeDefinition( m_repositoryId, id ); +} + +vector< libcmis::ObjectTypePtr > WSSession::getBaseTypes( ) +{ + return getRepositoryService().getTypeChildren( m_repositoryId, "" ); +} diff --git a/src/libcmis/ws-session.hxx b/src/libcmis/ws-session.hxx new file mode 100644 index 0000000..a7e6710 --- /dev/null +++ b/src/libcmis/ws-session.hxx @@ -0,0 +1,124 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_SESSION_HXX_ +#define _WS_SESSION_HXX_ + +#include <map> +#include <string> + +#include "base-session.hxx" +#include "ws-navigationservice.hxx" +#include "ws-objectservice.hxx" +#include "ws-repositoryservice.hxx" +#include "ws-soap.hxx" +#include "ws-versioningservice.hxx" + +class WSSession : public BaseSession, public SoapSession +{ + private: + std::map< std::string, std::string > m_servicesUrls; + NavigationService* m_navigationService; + ObjectService* m_objectService; + RepositoryService* m_repositoryService; + VersioningService* m_versioningService; + + SoapResponseFactory m_responseFactory; + + public: + WSSession( std::string bindingUrl, std::string repositoryId, + std::string username, std::string password, + bool noSslCheck = false, + libcmis::OAuth2DataPtr oauth2 = libcmis::OAuth2DataPtr(), + bool verbose = false ); + + /** This constructor uses the response of an HTTP request made + before to spare some HTTP request. This constructor has mostly + been designed for the SessionFactory use. + */ + WSSession( std::string bindingUrl, std::string repositoryId, + const HttpSession& HttpSession, + libcmis::HttpResponsePtr response ); + ~WSSession( ); + + // Utility methods + + /** Get an instance of the SoapResponseFactory, setup with all the + CMIS namespaces and function pointers. + */ + SoapResponseFactory& getResponseFactory( ) { return m_responseFactory; } + + /** Try hard to get a WSDL file at the given URL (tries to add ?wsdl if needed) + */ + std::string getWsdl( std::string url, libcmis::HttpResponsePtr response ); + + std::vector< SoapResponsePtr > soapRequest( std::string& url, SoapRequest& request ); + + /** Get the service location URL given its name. + */ + std::string getServiceUrl( std::string name ); + + RepositoryService& getRepositoryService( ); + + ObjectService& getObjectService( ); + + NavigationService& getNavigationService( ); + + VersioningService& getVersioningService( ); + + + // Override session methods + + virtual libcmis::RepositoryPtr getRepository( ); + + virtual bool setRepository( std::string repositoryId ); + + virtual libcmis::ObjectPtr getObject( std::string id ); + + virtual libcmis::ObjectPtr getObjectByPath( std::string path ); + + virtual libcmis::ObjectTypePtr getType( std::string id ); + + virtual std::vector< libcmis::ObjectTypePtr > getBaseTypes( ); + + private: + + // Default constructor shouldn't be called + WSSession( ); + WSSession( const WSSession& copy ) = delete; + WSSession& operator=( const WSSession& copy ) = delete; + + void parseWsdl( std::string buf ); + void initializeResponseFactory( ); + void initializeRepositories( std::map< std::string, std::string > repositories ); + void initialize( libcmis::HttpResponsePtr response = libcmis::HttpResponsePtr() ); + + std::map< std::string, SoapResponseCreator > getResponseMapping( ); + std::map< std::string, SoapFaultDetailCreator > getDetailMapping( ); +}; + +#endif diff --git a/src/libcmis/ws-soap.cxx b/src/libcmis/ws-soap.cxx new file mode 100644 index 0000000..20e9c68 --- /dev/null +++ b/src/libcmis/ws-soap.cxx @@ -0,0 +1,335 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-soap.hxx" + +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xmlstring.h> + +#include <libcmis/xml-utils.hxx> + +using namespace std; +using namespace boost::uuids; + +SoapFault::SoapFault( xmlNodePtr node, SoapResponseFactory* factory ) : + exception( ), + m_faultcode( ), + m_faultstring( ), + m_detail( ), + m_message( ) +{ + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + if ( xmlStrEqual( child->name, BAD_CAST( "faultcode" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + xmlChar* prefix = NULL; + xmlChar* localName = xmlSplitQName2( content, &prefix ); + if (localName == NULL) + localName = xmlStrdup( content ); + m_faultcode = string( ( char* )localName ); + xmlFree( content ); + xmlFree( prefix ); + xmlFree( localName ); + } + else if ( xmlStrEqual( child->name, BAD_CAST( "faultstring" ) ) ) + { + xmlChar* content = xmlNodeGetContent( child ); + m_faultstring = string( ( char* )content ); + xmlFree( content ); + } + else if ( xmlStrEqual( child->name, BAD_CAST( "detail" ) ) ) + { + m_detail = factory->parseFaultDetail( child ); + } + } + + m_message = getFaultcode() + ": " + getFaultstring(); + for ( vector< SoapFaultDetailPtr >::const_iterator it = m_detail.begin( ); it != m_detail.end( ); ++it ) + { + m_message += "\n" + ( *it )->toString( ); + } + +} + +// LCOV_EXCL_START +const char* SoapFault::what( ) const noexcept +{ + return m_message.c_str( ); +} +// LCOV_EXCL_STOP + + +SoapResponseFactory::SoapResponseFactory( ) : + m_mapping( ), + m_namespaces( ), + m_detailMapping( ), + m_session( NULL ) +{ +} + +SoapResponseFactory::SoapResponseFactory( const SoapResponseFactory& copy ) : + m_mapping( copy.m_mapping ), + m_namespaces( copy.m_namespaces ), + m_detailMapping( copy.m_detailMapping ), + m_session( copy.m_session ) +{ +} + +SoapResponseFactory& SoapResponseFactory::operator=( const SoapResponseFactory& copy ) +{ + if ( this != © ) + { + m_mapping = copy.m_mapping; + m_namespaces = copy.m_namespaces; + m_detailMapping = copy.m_detailMapping; + m_session = copy.m_session; + } + + return *this; +} + +vector< SoapResponsePtr > SoapResponseFactory::parseResponse( string& xml ) +{ + // Create a fake multipart + RelatedMultipart multipart; + string name = "root"; + string type = "text/xml"; + string info; + RelatedPartPtr part( new RelatedPart( name, type, xml ) ); + string cid = multipart.addPart( part ); + multipart.setStart( cid, info ); + + // Then parse it normally + return parseResponse( multipart ); +} + +vector< SoapResponsePtr > SoapResponseFactory::parseResponse( RelatedMultipart& multipart ) +{ + string xml; + RelatedPartPtr part = multipart.getPart( multipart.getStartId( ) ); + if ( part.get() != NULL ) + xml = part->getContent( ); + + vector< SoapResponsePtr > responses; + + const boost::shared_ptr< xmlDoc > doc( xmlReadMemory( xml.c_str(), xml.size(), "", NULL, 0 ), xmlFreeDoc ); + + if ( bool( doc ) ) + { + const boost::shared_ptr< xmlXPathContext > xpathCtx( xmlXPathNewContext( doc.get() ), xmlXPathFreeContext ); + libcmis::registerSoapNamespaces( xpathCtx.get() ); + + for ( map< string, string >::iterator it = m_namespaces.begin( ); + it != m_namespaces.end( ); ++it ) + { + xmlXPathRegisterNs( xpathCtx.get(), BAD_CAST( it->first.c_str() ), BAD_CAST( it->second.c_str( ) ) ); + } + + if ( bool( xpathCtx ) ) + { + string bodyXPath( "//soap-env:Body/*" ); + const boost::shared_ptr< xmlXPathObject > xpathObj( xmlXPathEvalExpression( BAD_CAST( bodyXPath.c_str() ), xpathCtx.get() ), xmlXPathFreeObject ); + + if ( bool( xpathObj ) ) + { + int nbChildren = 0; + if ( xpathObj->nodesetval ) + nbChildren = xpathObj->nodesetval->nodeNr; + + for ( int i = 0; i < nbChildren; i++ ) + { + xmlNodePtr node = xpathObj->nodesetval->nodeTab[i]; + + // Is it a fault? + if ( xmlStrEqual( BAD_CAST( NS_SOAP_ENV_URL ), node->ns->href ) && + xmlStrEqual( BAD_CAST( "Fault" ), node->name ) ) + { + throw SoapFault( node, this ); + } + SoapResponsePtr response = createResponse( node, multipart ); + if ( NULL != response.get( ) ) + responses.push_back( response ); + } + } + } + } + + return responses; +} + +SoapResponsePtr SoapResponseFactory::createResponse( xmlNodePtr node, RelatedMultipart& multipart ) +{ + SoapResponsePtr response; + + string ns( ( const char* ) node->ns->href ); + string name( ( const char* ) node->name ); + string id = "{" + ns + "}" + name; + map< string, SoapResponseCreator >::iterator it = m_mapping.find( id ); + + if ( it != m_mapping.end( ) ) + { + SoapResponseCreator creator = it->second; + response = creator( node, multipart, m_session ); + } + + return response; +} + +vector< SoapFaultDetailPtr > SoapResponseFactory::parseFaultDetail( xmlNodePtr node ) +{ + vector< SoapFaultDetailPtr > detail; + + for ( xmlNodePtr child = node->children; child; child = child->next ) + { + string ns; + if ( child->ns != NULL ) + ns = string( ( const char* ) child->ns->href ); + string name( ( const char* ) child->name ); + string id = "{" + ns + "}" + name; + map< string, SoapFaultDetailCreator >::iterator it = m_detailMapping.find( id ); + + if ( it != m_detailMapping.end( ) ) + { + SoapFaultDetailCreator creator = it->second; + detail.push_back( creator( child ) ); + } + } + + return detail; +} + +RelatedMultipart& SoapRequest::getMultipart( string& username, string& password ) +{ + // Generate the envelope and add it to the multipart + string envelope = createEnvelope( username, password ); + string name( "root" ); + string type( "application/xop+xml;charset=UTF-8;type=\"text/xml\"" ); + RelatedPartPtr envelopePart( new RelatedPart( name, type, envelope ) ); + string rootId = m_multipart.addPart( envelopePart ); + + // Set the envelope as the start part of the multipart + string startInfo( "text/xml" ); + m_multipart.setStart( rootId, startInfo ); + + return m_multipart; +} + +string SoapRequest::createEnvelope( string& username, string& password ) +{ + xmlBufferPtr buf = xmlBufferCreate( ); + xmlTextWriterPtr writer = xmlNewTextWriterMemory( buf, 0 ); + + xmlTextWriterStartDocument( writer, NULL, NULL, NULL ); + + /* Sample envelope: + <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> + <S:Header> + <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + <Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> + <Created>2012-06-14T09:20:29Z</Created> + <Expires>2012-06-15T09:20:29Z</Expires> + </Timestamp> + <UsernameToken> + <Username>admin</Username> + <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">admin</Password> + <Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2012-06-14T09:20:29Z</Created> + </UsernameToken> + </Security> + </S:Header> + <S:Body> + <ns2:getRepositories xmlns="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/messaging/200908/"/> + </S:Body> + </S:Envelope> + */ + xmlChar* wsseUrl = BAD_CAST( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ); + xmlChar* wsuUrl = BAD_CAST( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ); + + // Use an unsecure password transmission (PasswordText) because some clients can't support the PasswordDigest. + xmlChar* passTypeStr = BAD_CAST( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText" ); + + // Created must be a UTC time with no more than 3 digits fractional seconds. + boost::posix_time::ptime created( boost::posix_time::second_clock::universal_time( ) ); + boost::posix_time::ptime expires( created ); + expires = expires + boost::gregorian::days( 1 ); + string createdStr = libcmis::writeDateTime( created ); + string expiresStr = libcmis::writeDateTime( expires ); + + xmlTextWriterStartElement( writer, BAD_CAST( "S:Envelope" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:S" ), BAD_CAST( NS_SOAP_ENV_URL ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:wsu" ), wsuUrl ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "xmlns:wsse" ), wsseUrl ); + + xmlTextWriterStartElement( writer, BAD_CAST( "S:Header" ) ); + + // Write out the Basic Security Profile 1.0 compliant headers + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Security" ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Timestamp" ) ); + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Created" ) ); + xmlTextWriterWriteRaw( writer, BAD_CAST( createdStr.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Created + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Expires" ) ); + xmlTextWriterWriteRaw( writer, BAD_CAST( expiresStr.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Expires + xmlTextWriterEndElement( writer ); // End of Timestamp + + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:UsernameToken" ) ); + xmlTextWriterWriteElement( writer, BAD_CAST( "wsse:Username" ), BAD_CAST( username.c_str( ) ) ); + + xmlTextWriterStartElement( writer, BAD_CAST( "wsse:Password" ) ); + xmlTextWriterWriteAttribute( writer, BAD_CAST( "Type" ), passTypeStr ); + xmlTextWriterWriteRaw( writer, BAD_CAST( password.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Password + xmlTextWriterStartElement( writer, BAD_CAST( "wsu:Created" ) ); + xmlTextWriterWriteRaw( writer, BAD_CAST( createdStr.c_str( ) ) ); + xmlTextWriterEndElement( writer ); // End of Created + xmlTextWriterEndElement( writer ); // End of UsernameToken + + xmlTextWriterEndElement( writer ); // End of Security + + xmlTextWriterEndElement( writer ); // End of S:Header + + xmlTextWriterStartElement( writer, BAD_CAST( "S:Body" ) ); + toXml( writer ); + xmlTextWriterEndElement( writer ); // End of S:Body + + xmlTextWriterEndElement( writer ); // End of S:Envelope + xmlTextWriterEndDocument( writer ); + + string str( ( const char * )xmlBufferContent( buf ) ); + + xmlFreeTextWriter( writer ); + xmlBufferFree( buf ); + + return str; +} diff --git a/src/libcmis/ws-soap.hxx b/src/libcmis/ws-soap.hxx new file mode 100644 index 0000000..62b8b36 --- /dev/null +++ b/src/libcmis/ws-soap.hxx @@ -0,0 +1,178 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_SOAP_HXX_ +#define _WS_SOAP_HXX_ + +#include <exception> +#include <map> +#include <vector> + +#include <boost/shared_ptr.hpp> +#include <libxml/tree.h> + +#include <libcmis/xmlserializable.hxx> + +#include "ws-relatedmultipart.hxx" + +/** Interface for soap sessions to communicate to response objects. + + \attention + It currently doesn't provide anything, but it may later + provide all useful methods for SOAP requests low level handling. + */ +class SoapSession +{ + public: + SoapSession( ) { } + virtual ~SoapSession( ) { } +}; + +/** Base class for all SOAP response objects. + + The factory will need to create the response objects using a static + creator method in each class. + */ +class SoapResponse +{ + public: + virtual ~SoapResponse( ) { }; +}; +typedef boost::shared_ptr< SoapResponse > SoapResponsePtr; +typedef SoapResponsePtr ( *SoapResponseCreator ) ( xmlNodePtr, RelatedMultipart&, SoapSession* session ); + +/** Base clas for SoapFault details parsed data. + */ +class SoapFaultDetail +{ + public: + virtual ~SoapFaultDetail() {}; + + virtual const std::string toString( ) const { return std::string( ); } +}; +typedef boost::shared_ptr< SoapFaultDetail > SoapFaultDetailPtr; +typedef SoapFaultDetailPtr ( *SoapFaultDetailCreator ) ( xmlNodePtr ); + +class SoapResponseFactory; +/** Class representing a SOAP Fault element, to be used as an exception. + */ +class SoapFault : public std::exception +{ + private: + std::string m_faultcode; + std::string m_faultstring; + std::vector< SoapFaultDetailPtr > m_detail; + std::string m_message; + + public: + SoapFault( xmlNodePtr faultNode, SoapResponseFactory* factory ); + virtual ~SoapFault( ) noexcept { }; + + const std::string& getFaultcode ( ) const { return m_faultcode; } + const std::string& getFaultstring ( ) const { return m_faultstring; } + std::vector< SoapFaultDetailPtr > getDetail( ) const { return m_detail; } + + virtual const char* what() const noexcept; +}; + + +/** Class parsing the SOAP response message and extracting the SoapResponse objects. + */ +class SoapResponseFactory +{ + private: + std::map< std::string, SoapResponseCreator > m_mapping; + std::map< std::string, std::string > m_namespaces; + std::map< std::string, SoapFaultDetailCreator > m_detailMapping; + SoapSession* m_session; + + public: + + SoapResponseFactory( ); + SoapResponseFactory( const SoapResponseFactory& copy ); + + SoapResponseFactory& operator=( const SoapResponseFactory& copy ); + + void setMapping( std::map< std::string, SoapResponseCreator > mapping ) { m_mapping = mapping; } + + /** Set the additional namespaces to parse the responses. There is no need to + add the soap / wsdl namespaces... they are automatically added. + */ + void setNamespaces( std::map< std::string, std::string > namespaces ) { m_namespaces = namespaces; } + + void setDetailMapping( std::map< std::string, SoapFaultDetailCreator > mapping ) { m_detailMapping = mapping; } + + void setSession( SoapSession* session ) { m_session = session; } + + /** Get the Soap envelope from the multipart and extract the response objects from it. This + method will also read the possible related parts to construct the response. + */ + std::vector< SoapResponsePtr > parseResponse( RelatedMultipart& multipart ); + + /** Get the Soap envelope from an XML-only file and extract the response objects from it. + */ + std::vector< SoapResponsePtr > parseResponse( std::string& xml ); + + /** Create a SoapResponse object depending on the node we have. This shouldn't be used + directly: only from parseResponse or unit tests. + */ + SoapResponsePtr createResponse( xmlNodePtr node, RelatedMultipart& multipart ); + + std::vector< SoapFaultDetailPtr > parseFaultDetail( xmlNodePtr detailNode ); +}; + + +/** Base class for all SOAP request objects. + + The implementer's toXml() method needs to take care of two things: + \li generate the XML to put in the Soap envelope body + \li add the potential attachement to the multipart. + + There is no need to add the envelope to the multipart: it will + automatically be added as the start part of it by getMultipart(). + This also means that adding parts to the multipart will have to + be done directly on the m_multipart protected member. + + The RelatedMultipart object is the final result to be used. + */ +class SoapRequest : public libcmis::XmlSerializable +{ + protected: + RelatedMultipart m_multipart; + + public: + SoapRequest( ) : m_multipart( ) { }; + virtual ~SoapRequest( ) { }; + + RelatedMultipart& getMultipart( std::string& username, std::string& password ); + + protected: + + std::string createEnvelope( std::string& username, std::string& password ); +}; + +#endif diff --git a/src/libcmis/ws-versioningservice.cxx b/src/libcmis/ws-versioningservice.cxx new file mode 100644 index 0000000..579ec11 --- /dev/null +++ b/src/libcmis/ws-versioningservice.cxx @@ -0,0 +1,138 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "ws-versioningservice.hxx" + +#include "ws-requests.hxx" +#include "ws-session.hxx" + +using namespace std; +using libcmis::PropertyPtrMap; + +VersioningService::VersioningService( ) : + m_session( NULL ), + m_url( "" ) +{ +} + +VersioningService::VersioningService( WSSession* session ) : + m_session( session ), + m_url( session->getServiceUrl( "VersioningService" ) ) +{ +} + +VersioningService::VersioningService( const VersioningService& copy ) : + m_session( copy.m_session ), + m_url( copy.m_url ) +{ +} + +VersioningService::~VersioningService( ) +{ +} + +VersioningService& VersioningService::operator=( const VersioningService& copy ) +{ + if ( this != © ) + { + m_session = copy.m_session; + m_url = copy.m_url; + } + + return *this; +} + +libcmis::DocumentPtr VersioningService::checkOut( string repoId, string documentId ) +{ + libcmis::DocumentPtr pwc; + + CheckOutRequest request( repoId, documentId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CheckOutResponse* response = dynamic_cast< CheckOutResponse* >( resp ); + if ( response != NULL ) + { + string pwcId = response->getObjectId( ); + libcmis::ObjectPtr object = m_session->getObject( pwcId ); + pwc = boost::dynamic_pointer_cast< libcmis::Document >( object ); + } + } + + return pwc; +} + +void VersioningService::cancelCheckOut( string repoId, string documentId ) +{ + CancelCheckOutRequest request( repoId, documentId ); + m_session->soapRequest( m_url, request ); +} + +libcmis::DocumentPtr VersioningService::checkIn( string repoId, string objectId, bool isMajor, + const PropertyPtrMap& properties, + boost::shared_ptr< ostream > stream, string contentType, string fileName, + string comment ) +{ + libcmis::DocumentPtr newVersion; + + CheckInRequest request( repoId, objectId, isMajor, properties, stream, contentType, fileName, comment ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + CheckInResponse* response = dynamic_cast< CheckInResponse* >( resp ); + if ( response != NULL ) + { + string newId = response->getObjectId( ); + libcmis::ObjectPtr object = m_session->getObject( newId ); + newVersion = boost::dynamic_pointer_cast< libcmis::Document >( object ); + } + } + + return newVersion; +} + +vector< libcmis::DocumentPtr > VersioningService::getAllVersions( string repoId, string objectId ) +{ + vector< libcmis::DocumentPtr > versions; + + GetAllVersionsRequest request( repoId, objectId ); + vector< SoapResponsePtr > responses = m_session->soapRequest( m_url, request ); + if ( responses.size( ) == 1 ) + { + SoapResponse* resp = responses.front( ).get( ); + GetAllVersionsResponse* response = dynamic_cast< GetAllVersionsResponse* >( resp ); + if ( response != NULL ) + { + versions = response->getObjects( ); + } + } + + return versions; +} diff --git a/src/libcmis/ws-versioningservice.hxx b/src/libcmis/ws-versioningservice.hxx new file mode 100644 index 0000000..5156ebb --- /dev/null +++ b/src/libcmis/ws-versioningservice.hxx @@ -0,0 +1,67 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _WS_VERSIONINGSERVICE_HXX_ +#define _WS_VERSIONINGSERVICE_HXX_ + +#include <string> + +#include <libcmis/document.hxx> + +class WSSession; + +class VersioningService +{ + private: + WSSession* m_session; + std::string m_url; + + public: + + VersioningService( WSSession* session ); + VersioningService( const VersioningService& copy ); + ~VersioningService( ); + + VersioningService& operator=( const VersioningService& copy ); + + libcmis::DocumentPtr checkOut( std::string repoId, std::string documentId ); + + void cancelCheckOut( std::string repoId, std::string documentId ); + + libcmis::DocumentPtr checkIn( std::string repoId, std::string objectId, bool isMajor, + const std::map< std::string, libcmis::PropertyPtr >& properties, + boost::shared_ptr< std::ostream > stream, std::string contentType, + std::string fileName, std::string comment ); + + std::vector< libcmis::DocumentPtr > getAllVersions( std::string repoId, std::string objectId ); + + private: + + VersioningService( ); +}; + +#endif diff --git a/src/libcmis/xml-utils.cxx b/src/libcmis/xml-utils.cxx new file mode 100644 index 0000000..3fa2df7 --- /dev/null +++ b/src/libcmis/xml-utils.cxx @@ -0,0 +1,573 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <libcmis/xml-utils.hxx> + +#include <errno.h> +#include <memory> +#include <sstream> +#include <stdlib.h> + +#include <boost/algorithm/string.hpp> +#include <boost/version.hpp> + +#if BOOST_VERSION >= 106800 +#include <boost/uuid/detail/sha1.hpp> +#else +#include <boost/uuid/sha1.hpp> +#endif +#include <curl/curl.h> + +#if LIBCURL_VERSION_VALUE < 0x070F04 +#define curl_easy_escape( dummy, str, len ) curl_escape( str, len ) +#define curl_easy_unescape( dummy, str, len, dummy2 ) curl_unescape( str, len ) +#endif + +using namespace std; + +namespace +{ + static const char chars64[]= + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + bool lcl_getBufValue( char encoded, int* value ) + { + bool found = false; + const char *i = chars64; + while ( !found && *i ) + { + if ( *i == encoded ) + { + found = true; + *value = ( i - chars64 ); + } + ++i; + } + return found; + } +} + +namespace libcmis +{ + EncodedData::EncodedData( FILE* stream ) : + m_writer( NULL ), + m_stream( stream ), + m_outStream( NULL ), + m_encoding( ), + m_decode( false ), + m_pendingValue( 0 ), + m_pendingRank( 0 ), + m_missingBytes( 0 ) + { + } + + EncodedData::EncodedData( ostream* stream ) : + m_writer( NULL ), + m_stream( NULL ), + m_outStream( stream ), + m_encoding( ), + m_decode( false ), + m_pendingValue( 0 ), + m_pendingRank( 0 ), + m_missingBytes( 0 ) + { + } + + EncodedData::EncodedData( xmlTextWriterPtr writer ) : + m_writer( writer ), + m_stream( NULL ), + m_outStream( NULL ), + m_encoding( ), + m_decode( false ), + m_pendingValue( 0 ), + m_pendingRank( 0 ), + m_missingBytes( 0 ) + { + } + + EncodedData::EncodedData( const EncodedData& copy ) : + m_writer( copy.m_writer ), + m_stream( copy.m_stream ), + m_outStream( copy.m_outStream ), + m_encoding( copy.m_encoding ), + m_decode( copy.m_decode ), + m_pendingValue( copy.m_pendingValue ), + m_pendingRank( copy.m_pendingRank ), + m_missingBytes( copy.m_missingBytes ) + { + } + + EncodedData& EncodedData::operator=( const EncodedData& copy ) + { + if ( this != © ) + { + m_writer = copy.m_writer; + m_stream = copy.m_stream; + m_outStream = copy.m_outStream; + m_encoding = copy.m_encoding; + m_decode = copy.m_decode; + m_pendingValue = copy.m_pendingValue; + m_pendingRank = copy.m_pendingRank; + m_missingBytes = copy.m_missingBytes; + } + return *this; + } + + void EncodedData::write( void* buf, size_t size, size_t nmemb ) + { + if ( m_writer ) + xmlTextWriterWriteRawLen( m_writer, ( xmlChar* )buf, size * nmemb ); + else if ( m_stream ) + fwrite( buf, size, nmemb, m_stream ); + else if ( m_outStream ) + m_outStream->write( ( const char* )buf, size * nmemb ); + } + + void EncodedData::decode( void* buf, size_t size, size_t nmemb ) + { + m_decode = true; + if ( 0 == m_encoding.compare( "base64" ) ) + { + decodeBase64( ( const char* )buf, size * nmemb ); + } + else + write( buf, size, nmemb ); + } + + void EncodedData::encode( void* buf, size_t size, size_t nmemb ) + { + m_decode = false; + if ( 0 == m_encoding.compare( "base64" ) ) + { + encodeBase64( ( const char* )buf, size * nmemb ); + } + else + write( buf, size, nmemb ); + } + + void EncodedData::finish( ) + { + // Flushes the last bytes in base64 encoding / decoding if any + if ( 0 == m_encoding.compare( "base64" ) ) + { + if ( m_decode && ( m_pendingValue != 0 || m_pendingRank != 0 || m_missingBytes != 0 ) ) + { + int missingBytes = m_missingBytes; + if ( 0 == m_missingBytes ) + missingBytes = 4 - m_pendingRank; + + char decoded[3]; + decoded[0] = ( m_pendingValue & 0xFF0000 ) >> 16; + decoded[1] = ( m_pendingValue & 0xFF00 ) >> 8; + decoded[2] = ( m_pendingValue & 0xFF ); + + write( decoded, 1, 3 - missingBytes ); + + m_pendingRank = 0; + m_pendingValue = 0; + m_missingBytes = 0; + } + else if ( !m_decode && ( m_pendingValue != 0 || m_pendingRank != 0 ) ) + { + // Missing bytes should be zeroed: no need to do it + char encoded[4]; + encoded[0] = chars64[ ( m_pendingValue & 0xFC0000 ) >> 18 ]; + encoded[1] = chars64[ ( m_pendingValue & 0x03F000 ) >> 12 ]; + encoded[2] = chars64[ ( m_pendingValue & 0x000FC0 ) >> 6 ]; + encoded[3] = chars64[ ( m_pendingValue & 0x00003F ) ]; + + // Output the padding + int nEquals = 3 - m_pendingRank; + for ( int i = 0; i < nEquals; ++i ) + encoded[ 3 - i ] = '='; + + write( encoded, 1, 4 ); + + m_pendingRank = 0; + m_pendingValue = 0; + } + } + } + + void EncodedData::decodeBase64( const char* buf, size_t len ) + { + unsigned long blockValue = m_pendingValue; + int byteRank = m_pendingRank; + int missingBytes = m_missingBytes; + + size_t i = 0; + while ( i < len ) + { + int value = 0; + if ( lcl_getBufValue( buf[i], &value ) ) + { + blockValue += value << ( ( 3 - byteRank ) * 6 ); + ++byteRank; + } + else if ( buf[i] == '=' ) + { + ++missingBytes; + ++byteRank; + } + + // Reached the end of a block, decode it + if ( byteRank >= 4 ) + { + char decoded[3]; + decoded[0] = ( blockValue & 0xFF0000 ) >> 16; + decoded[1] = ( blockValue & 0xFF00 ) >> 8; + decoded[2] = ( blockValue & 0xFF ); + + write( decoded, 1, 3 - missingBytes ); + + byteRank = 0; + blockValue = 0; + missingBytes = 0; + } + ++i; + } + + // Store the values if the last block is incomplete: they may come later + m_pendingValue = blockValue; + m_pendingRank = byteRank; + m_missingBytes = missingBytes; + } + + void EncodedData::encodeBase64( const char* buf, size_t len ) + { + unsigned long blockValue = m_pendingValue; + int byteRank = m_pendingRank; + + size_t i = 0; + while ( i < len ) + { + // Cast the char to an unsigned char or we'll shift negative values + blockValue += static_cast< unsigned char >( buf[i] ) << ( 2 - byteRank ) * 8; + ++byteRank; + + // Reached the end of a block, encode it + if ( byteRank >= 3 ) + { + char encoded[4]; + encoded[0] = chars64[ ( blockValue & 0xFC0000 ) >> 18 ]; + encoded[1] = chars64[ ( blockValue & 0x03F000 ) >> 12 ]; + encoded[2] = chars64[ ( blockValue & 0x000FC0 ) >> 6 ]; + encoded[3] = chars64[ ( blockValue & 0x00003F ) ]; + + write( encoded, 1, 4 ); + + byteRank = 0; + blockValue = 0; + } + ++i; + } + + // Store the values if the last block is incomplete: they may come later + m_pendingValue = blockValue; + m_pendingRank = byteRank; + } + + HttpResponse::HttpResponse( ) : + m_headers( ), + m_stream( ), + m_data( ) + { + m_stream.reset( new stringstream( ) ); + m_data.reset( new EncodedData( m_stream.get( ) ) ); + } + + void registerNamespaces( xmlXPathContextPtr xpathCtx ) + { + if ( xpathCtx != NULL ) + { + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "app" ), BAD_CAST( NS_APP_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "atom" ), BAD_CAST( NS_ATOM_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmisra" ), BAD_CAST( NS_CMISRA_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmism" ), BAD_CAST( NS_CMISM_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "xsi" ), BAD_CAST( "http://www.w3.org/2001/XMLSchema-instance" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "type" ), BAD_CAST( "cmis:cmisTypeDocumentDefinitionType" ) ); + } + } + + void registerCmisWSNamespaces( xmlXPathContextPtr xpathCtx ) + { + if ( xpathCtx != NULL ) + { + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmisw" ), BAD_CAST( NS_CMISW_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmis" ), BAD_CAST( NS_CMIS_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmisra" ), BAD_CAST( NS_CMISRA_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "cmism" ), BAD_CAST( NS_CMISM_URL ) ); + + registerSoapNamespaces( xpathCtx ); + } + } + + void registerSoapNamespaces( xmlXPathContextPtr xpathCtx ) + { + if ( xpathCtx != NULL ) + { + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "soap" ), BAD_CAST( NS_SOAP_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "soap-env" ), BAD_CAST( NS_SOAP_ENV_URL ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "wsdl" ), BAD_CAST ( "http://schemas.xmlsoap.org/wsdl/" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "ns" ), BAD_CAST ( "http://schemas.xmlsoap.org/soap/encoding/" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "jaxws" ), BAD_CAST( "http://java.sun.com/xml/ns/jaxws" ) ); + xmlXPathRegisterNs( xpathCtx, BAD_CAST( "xsd" ), BAD_CAST ( "http://www.w3.org/2001/XMLSchema" ) ); + } + } + + string getXPathValue( xmlXPathContextPtr xpathCtx, string req ) + { + string value; + if ( xpathCtx != NULL ) + { + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression( BAD_CAST( req.c_str() ), xpathCtx ); + if ( xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 ) + { + xmlChar* pContent = xmlNodeGetContent( xpathObj->nodesetval->nodeTab[0] ); + value = string( ( char* )pContent ); + xmlFree( pContent ); + } + xmlXPathFreeObject( xpathObj ); + } + + return value; + } + + xmlDocPtr wrapInDoc( xmlNodePtr entryNd ) + { + xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); + if ( entryNd != NULL ) + { + xmlNodePtr entryCopy = xmlCopyNode( entryNd, 1 ); + xmlDocSetRootElement( doc, entryCopy ); + } + return doc; + } + + string getXmlNodeAttributeValue( xmlNodePtr node, + const char* attributeName, + const char* defaultValue ) + { + xmlChar* xmlStr = xmlGetProp( node, BAD_CAST( attributeName ) ); + if ( xmlStr == NULL ) + { + if ( !defaultValue ) + throw Exception( "Missing attribute" ); + else + return string( defaultValue ); + } + string value( ( char * ) xmlStr ); + xmlFree( xmlStr ); + return value; + } + + boost::posix_time::ptime parseDateTime( string dateTimeStr ) + { + boost::posix_time::ptime t( boost::date_time::not_a_date_time ); + // Get the time zone offset + boost::posix_time::time_duration tzOffset( boost::posix_time::duration_from_string( "+00:00" ) ); + + if ( dateTimeStr.empty( ) ) + return t; // obviously not a time + + size_t teePos = dateTimeStr.find( 'T' ); + if ( teePos == string::npos || teePos == dateTimeStr.size() - 1 ) + return t; // obviously not a time + + string noTzStr = dateTimeStr.substr( 0, teePos + 1 ); + string timeStr = dateTimeStr.substr( teePos + 1 ); + + // Get the TZ if any + if ( timeStr[ timeStr.size() - 1] == 'Z' ) + { + noTzStr += timeStr.substr( 0, timeStr.size() - 1 ); + } + else + { + size_t tzPos = timeStr.find( '+' ); + if ( tzPos == string::npos ) + tzPos = timeStr.find( '-' ); + + if ( tzPos != string::npos ) + { + noTzStr += timeStr.substr( 0, tzPos ); + + // Check the validity of the TZ value + string tzStr = timeStr.substr( tzPos ); + try + { + tzOffset = boost::posix_time::time_duration( boost::posix_time::duration_from_string( tzStr.c_str() ) ); + } + catch ( const std::exception& ) + { + // Error converting, not a datetime + return t; + } + + } + else + noTzStr += timeStr; + } + + // Remove all the '-' and ':' + size_t pos = noTzStr.find_first_of( ":-" ); + while ( pos != string::npos ) + { + noTzStr.erase( pos, 1 ); + pos = noTzStr.find_first_of( ":-" ); + } + try + { + t = boost::posix_time::from_iso_string( noTzStr.c_str( ) ); + t = t + tzOffset; + } + catch ( const std::exception& ) + { + // Ignore boost parsing errors: will result in not_a_date_time + } + + return t; + } + + string writeDateTime( boost::posix_time::ptime time ) + { + string str; + if ( !time.is_special( ) ) + { + str = boost::posix_time::to_iso_extended_string( time ); + str += "Z"; + } + return str; + } + + bool parseBool( string boolStr ) + { + bool value = false; + if ( boolStr == "true" || boolStr == "1" ) + value = true; + else if ( boolStr == "false" || boolStr == "0" ) + value = false; + else + throw Exception( string( "Invalid xsd:boolean input: " ) + boolStr ); + return value; + } + + long parseInteger( string intStr ) + { + char* end; + errno = 0; + long value = strtol( intStr.c_str(), &end, 0 ); + + if ( ( ERANGE == errno && ( LONG_MAX == value || LONG_MIN == value ) ) || + ( errno != 0 && value == 0 ) ) + { + throw Exception( string( "xsd:integer input can't fit to long: " ) + intStr ); + } + else if ( !string( end ).empty( ) ) + { + throw Exception( string( "Invalid xsd:integer input: " ) + intStr ); + } + + return value; + } + + double parseDouble( string doubleStr ) + { + char* end; + errno = 0; + double value = strtod( doubleStr.c_str(), &end ); + + if ( ( ERANGE == errno ) || ( errno != 0 && value == 0 ) ) + { + throw Exception( string( "xsd:decimal input can't fit to double: " ) + doubleStr ); + } + else if ( !string( end ).empty( ) ) + { + throw Exception( string( "Invalid xsd:decimal input: " ) + doubleStr ); + } + + return value; + } + + string trim( const string& str ) + { + return boost::trim_copy_if( str, boost::is_any_of( " \t\r\n" ) ); + } + + std::string base64encode( const std::string& str ) + { + stringstream stream; + EncodedData data( &stream ); + data.setEncoding( "base64" ); + data.encode( ( void * )str.c_str( ), size_t( 1 ), str.size() ); + data.finish( ); + return stream.str(); + } + + std::string sha1( const std::string& str ) + { + boost::uuids::detail::sha1 sha1; + sha1.process_bytes( str.c_str(), str.size() ); + + unsigned int digest[5]; + sha1.get_digest( digest ); + + stringstream out; + // Setup writing mode. Every number must produce eight + // hexadecimal digits, including possible leading 0s, or we get + // less than 40 digits as result. + out << hex << setfill('0') << right; + for ( int i = 0; i < 5; ++i ) + out << setw(8) << digest[i]; + return out.str(); + } + + int stringstream_write_callback( void * context, const char * s, int len ) + { + stringstream * ss=static_cast< stringstream * >( context ); + if ( ss ) + { + ss->write( s, len ); + return len; + } + return 0; + } + + string escape( string str ) + { + std::unique_ptr< char, void(*)( void* ) > escaped{ curl_easy_escape( NULL, str.c_str(), str.length() ), curl_free }; + return escaped.get(); + } + + string unescape( string str ) + { + std::unique_ptr< char, void(*)( void* ) > unescaped{ curl_easy_unescape( NULL, str.c_str(), str.length(), NULL ), curl_free }; + return unescaped.get(); + } +} |