summaryrefslogtreecommitdiffstats
path: root/src/cmis-client.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmis-client.cxx')
-rw-r--r--src/cmis-client.cxx1158
1 files changed, 1158 insertions, 0 deletions
diff --git a/src/cmis-client.cxx b/src/cmis-client.cxx
new file mode 100644
index 0000000..73b6f42
--- /dev/null
+++ b/src/cmis-client.cxx
@@ -0,0 +1,1158 @@
+/* libcmis
+ * Version: MPL 1.1 / GPLv2+ / LGPLv2+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License or as specified alternatively below. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2011 SUSE <cbosdonnat@suse.com>
+ *
+ *
+ * All Rights Reserved.
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
+ * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
+ * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
+ * instead of those above.
+ */
+
+#include <stdio.h>
+
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <boost/program_options.hpp>
+
+#include <libcmis/libcmis.hxx>
+
+using namespace std;
+using namespace boost::program_options;
+using libcmis::PropertyPtrMap;
+
+namespace
+{
+ char* lcl_queryAuthCode( const char* url, const char* /*username*/, const char* /*password*/ )
+ {
+ string code;
+ cout << "Copy the following link to your browser and take the code: " << endl << endl << url << endl << endl;
+ cout << "Enter the code:" << endl;
+ cin >> code;
+
+ return strdup( code.c_str() );
+ }
+
+ class AuthCodeProvider
+ {
+ private:
+ static string m_authCode;
+
+ public:
+ static void setAuthCode( string authCode );
+ static char* getAuthCode( const char* /*url*/, const char* /*username*/, const char* /*password*/ );
+ };
+
+ string AuthCodeProvider::m_authCode = string( );
+ void AuthCodeProvider::setAuthCode( string authCode )
+ {
+ m_authCode = authCode;
+ }
+
+ char* AuthCodeProvider::getAuthCode( const char* /*url*/, const char* /*username*/, const char* /*password*/ )
+ {
+ return strdup( m_authCode.c_str( ) );
+ }
+
+ class CinAuthProvider : public libcmis::AuthProvider
+ {
+ private:
+ string m_user;
+ string m_pass;
+
+ public:
+ CinAuthProvider( ) : m_user( ), m_pass( ) { }
+ ~CinAuthProvider( ) { }
+
+ virtual bool authenticationQuery( string& username, string& password );
+ };
+
+ bool CinAuthProvider::authenticationQuery( string& username, string& password )
+ {
+ bool cancelled = false;
+ bool askUsername = username.empty();
+
+ if ( askUsername )
+ {
+ if ( m_user.empty() )
+ {
+ cout << "Username (empty to cancel): ";
+ getline( cin, m_user );
+ }
+
+ cancelled = m_user.empty();
+ username = m_user;
+ }
+
+ if ( !cancelled && ( askUsername || password.empty( ) ) )
+ {
+ if ( m_pass.empty() )
+ {
+ cout << "Password (empty to cancel): ";
+ getline( cin, m_pass );
+ }
+
+ cancelled = m_pass.empty();
+ password = m_pass;
+ }
+
+ return !cancelled;
+ }
+
+ class CinCertValidationHandler : public libcmis::CertValidationHandler
+ {
+ private:
+ map< string, bool > m_answers;
+
+ public:
+ CinCertValidationHandler( ) : m_answers( ) { }
+ ~CinCertValidationHandler( ) { }
+
+ virtual bool validateCertificate( vector< string > certificates )
+ {
+ if ( certificates.empty( ) )
+ return false; // Should never happen
+
+ // Show the first certificate (even base64-encoded)
+ string cert = certificates.front();
+ map< string, bool >::iterator it = m_answers.find( cert );
+ if ( it != m_answers.end( ) )
+ return it->second;
+
+ cout << "Invalid SSL certificate:" << endl << cert << endl;
+ cout << "'openssl x509 -noout -text' can show you the details of this certificate." << endl << endl;
+
+ // Ask whether to validate
+ cout << "Do you want to ignore this problem and go on? yes/no [default: no]: ";
+ string answer;
+ getline( cin, answer );
+
+ m_answers[cert] = answer == "yes";
+
+ return answer == "yes";
+ }
+ };
+}
+
+class CommandException : public exception
+{
+ private:
+ string m_msg;
+
+ public:
+ CommandException( string msg ) : m_msg( msg ) { }
+ ~CommandException( ) noexcept { }
+ CommandException( const CommandException& copy ) : m_msg( copy.m_msg ) { }
+
+ CommandException& operator=( const CommandException& copy )
+ {
+ if ( this != &copy )
+ m_msg = copy.m_msg;
+ return *this;
+ }
+
+ virtual const char* what() const noexcept { return m_msg.c_str(); }
+};
+
+class CmisClient
+{
+ private:
+ variables_map& m_vm;
+ public:
+ CmisClient( variables_map& vm ) : m_vm( vm ) { }
+
+ libcmis::Session* getSession( bool inGetRepositories = false );
+
+ void execute( );
+
+ void printHelp( );
+
+ static options_description getOptionsDescription( );
+
+ private:
+ map< int, string > getSessionParams();
+ map< string, string > getObjectProperties( );
+};
+
+map< string, string > CmisClient::getObjectProperties( )
+{
+ map< string, string > result;
+ if ( m_vm.count( "object-property" ) > 0 )
+ {
+ vector< string > params = m_vm["object-property"].as< vector< string > >( );
+ for ( vector< string >::iterator it = params.begin( ); it != params.end( ); ++it )
+ {
+ size_t pos = it->find( "=" );
+ if ( pos != string::npos )
+ {
+ string name = it->substr( 0, pos );
+ string value = it->substr( pos + 1 );
+ result.insert( pair< string, string >( name, value ) );
+ }
+ }
+ }
+
+ return result;
+}
+
+libcmis::Session* CmisClient::getSession( bool inGetRepositories )
+{
+ if ( m_vm.count( "url" ) == 0 )
+ throw CommandException( "Missing binding URL" );
+
+ // Setup the authentication provider in case we have missing credentials
+ libcmis::AuthProviderPtr provider( new CinAuthProvider( ) );
+ libcmis::SessionFactory::setAuthenticationProvider( provider );
+
+ libcmis::CertValidationHandlerPtr certValidator( new CinCertValidationHandler( ) );
+ libcmis::SessionFactory::setCertificateValidationHandler( certValidator );
+
+ string url = m_vm["url"].as<string>();
+
+ // Look for the credentials
+ string username;
+ string password;
+ if ( m_vm.count( "username" ) > 0 )
+ {
+ username = m_vm["username"].as< string >();
+
+ if ( m_vm.count( "password" ) > 0 )
+ password = m_vm["password"].as< string >();
+ }
+
+ // Look for proxy settings
+ string proxyUrl;
+ string proxyUser;
+ string proxyPass;
+ string noproxy;
+ if ( m_vm.count( "proxy" ) > 0 )
+ {
+ proxyUrl = m_vm["proxy"].as< string >();
+
+ if ( m_vm.count( "proxy-user" ) > 0 )
+ proxyUser = m_vm["proxy-user"].as< string >();
+
+ if ( m_vm.count( "proxy-password" ) > 0 )
+ proxyPass = m_vm["proxy-password"].as< string >();
+
+ if ( m_vm.count( "noproxy" ) > 0 )
+ noproxy = m_vm["noproxy"].as< string >();
+
+ libcmis::SessionFactory::setProxySettings( proxyUrl, noproxy, proxyUser, proxyPass );
+ }
+
+ bool verbose = m_vm.count( "verbose" ) > 0;
+
+ libcmis::Session* session = NULL;
+ string repoId;
+ // The repository ID is needed to initiate a session
+ if ( m_vm.count( "repository" ) == 0 && !inGetRepositories )
+ {
+ // Do we have a single repository on the server?
+ session = getSession( true );
+ if ( session != NULL )
+ {
+ vector< libcmis::RepositoryPtr > repos = session->getRepositories();
+ if ( repos.size() == 1 )
+ {
+ repoId = repos.front( )->getId( );
+ session->setRepository( repoId );
+ }
+ }
+
+ // We couldn't auto-guess the repository, then throw an error
+ if ( repoId.empty( ) )
+ {
+ delete session;
+ throw CommandException( "Missing repository ID" );
+ }
+ }
+ else if ( m_vm.count( "repository" ) > 0 )
+ {
+ repoId = m_vm["repository"].as< string >();
+ }
+
+ if ( session == NULL )
+ {
+ bool noSslCheck = m_vm.count( "no-ssl-check" ) > 0;
+
+ // Should we use OAuth2?
+ string oauth2ClientId;
+ string oauth2ClientSecret;
+ string oauth2AuthUrl;
+ string oauth2TokenUrl;
+ string oauth2RedirectUri;
+ string oauth2Scope;
+ if ( m_vm.count( "oauth2-client-id" ) > 0 )
+ oauth2ClientId = m_vm["oauth2-client-id"].as< string >();
+ if ( m_vm.count( "oauth2-client-secret" ) > 0 )
+ oauth2ClientSecret = m_vm["oauth2-client-secret"].as< string >();
+ if ( m_vm.count( "oauth2-auth-url" ) > 0 )
+ oauth2AuthUrl = m_vm["oauth2-auth-url"].as< string >();
+ if ( m_vm.count( "oauth2-token-url" ) > 0 )
+ oauth2TokenUrl = m_vm["oauth2-token-url"].as< string >();
+ if ( m_vm.count( "oauth2-redirect-uri" ) > 0 )
+ oauth2RedirectUri = m_vm["oauth2-redirect-uri"].as< string >();
+ if ( m_vm.count( "oauth2-scope" ) > 0 )
+ oauth2Scope = m_vm["oauth2-scope"].as< string >();
+ if ( m_vm.count( "oauth2-auth-code" ) > 0 )
+ AuthCodeProvider::setAuthCode( m_vm["oauth2-auth-code"].as< string >() );
+
+ libcmis::OAuth2DataPtr oauth2Data( new libcmis::OAuth2Data( oauth2AuthUrl, oauth2TokenUrl,
+ oauth2Scope, oauth2RedirectUri, oauth2ClientId, oauth2ClientSecret) );
+
+ if ( oauth2Data->isComplete( ) )
+ {
+ // Set the fallback AuthCode provider
+ if ( m_vm.count( "oauth2-auth-code" ) > 0 )
+ {
+ libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthCodeProvider::getAuthCode );
+ }
+ else
+ {
+ libcmis::SessionFactory::setOAuth2AuthCodeProvider( lcl_queryAuthCode );
+ }
+ }
+ else
+ {
+ oauth2Data.reset( );
+ }
+
+ session = libcmis::SessionFactory::createSession( url, username, password, repoId, noSslCheck, oauth2Data, verbose );
+ }
+
+ return session;
+}
+
+void CmisClient::execute( )
+{
+ if ( ( m_vm.count( "help" ) > 0 ) || m_vm.count( "command" ) != 1 )
+ {
+ printHelp();
+ return;
+ }
+
+ if ( m_vm.count( "command" ) == 1 )
+ {
+ string command = m_vm["command"].as<string>();
+ if ( "list-repos" == command )
+ {
+ libcmis::Session* session = getSession( true );
+ if ( session != NULL )
+ {
+ vector< libcmis::RepositoryPtr > repos = session->getRepositories();
+
+ cout << "Repositories: name (id)" << endl;
+ for ( vector< libcmis::RepositoryPtr >::iterator it = repos.begin(); it != repos.end(); ++it )
+ cout << "\t" << ( *it )->getName( ) << " (" << ( *it )->getId( ) << ")" << endl;
+ }
+ else
+ {
+ cerr << "Couldn't create a session for some reason" << endl;
+ }
+ }
+ else if ( "show-root" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ libcmis::FolderPtr root = session->getRootFolder();
+ if ( root.get() )
+ {
+ cout << "------------------------------------------------" << endl;
+ cout << root->toString() << endl;
+ }
+ }
+ else if ( "repo-infos" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+ libcmis::RepositoryPtr repo = session->getRepository( );
+
+ if ( repo )
+ {
+ cout << "------------------------------------------------" << endl;
+ cout << repo->toString() << endl;
+ }
+ else
+ throw CommandException( "Please select a repository" );
+ }
+ else if ( "type-by-id" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the ids of the types to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the node ids to show as command args" );
+
+ vector< string > ids = m_vm["args"].as< vector< string > >( );
+
+
+ for ( vector< string >::iterator it = ids.begin(); it != ids.end(); ++it )
+ {
+ cout << "------------------------------------------------" << endl;
+ try
+ {
+ libcmis::ObjectTypePtr type = session->getType( *it );
+ cout << type->toString() << endl;
+ }
+ catch ( const libcmis::Exception& e )
+ {
+ cout << e.what() << endl;
+ }
+ }
+ }
+ else if ( "show-by-id" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the ids of the objects to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the node ids to show as command args" );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+
+
+ for ( vector< string >::iterator it = objIds.begin(); it != objIds.end(); ++it )
+ {
+ libcmis::ObjectPtr cmisObj = session->getObject( *it );
+ cout << "------------------------------------------------" << endl;
+ if ( cmisObj.get() )
+ cout << cmisObj->toString() << endl;
+ else
+ cout << "No such node: " << *it << endl;
+ }
+ }
+ else if ( "show-by-path" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the paths of the objects to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the node paths to show as command args" );
+
+ vector< string > objPaths = m_vm["args"].as< vector< string > >( );
+
+
+ for ( vector< string >::iterator it = objPaths.begin(); it != objPaths.end(); ++it )
+ {
+ libcmis::ObjectPtr cmisObj = session->getObjectByPath( *it );
+ cout << "------------------------------------------------" << endl;
+ if ( cmisObj.get() )
+ cout << cmisObj->toString() << endl;
+ else
+ cout << "No such node: " << *it << endl;
+ }
+ }
+ else if ( "get-content" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+ if ( objIds.empty( ) )
+ throw CommandException( "Please provide a content object Id" );
+
+ libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() );
+ libcmis::Document* document = dynamic_cast< libcmis::Document* >( cmisObj.get() );
+ if ( NULL != document )
+ {
+ // TODO Handle name clashes
+ string streamId;
+ if ( m_vm.count( "stream-id" ) > 0 )
+ streamId = m_vm["stream-id"].as<string>();
+
+ boost::shared_ptr< istream > in = document->getContentStream( streamId );
+ ofstream out( document->getContentFilename().c_str() );
+ out << in->rdbuf();
+ out.close();
+ }
+ else
+ throw CommandException( string( "Not a document object id: " ) + objIds.front() );
+ }
+ else if ( "set-content" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+ if ( objIds.empty( ) )
+ throw CommandException( "Please provide a content object Id" );
+
+ libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() );
+ libcmis::Document* document = dynamic_cast< libcmis::Document* >( cmisObj.get() );
+ if ( NULL != document )
+ {
+ if ( m_vm.count( "input-file" ) == 0 )
+ throw CommandException( "Missing --input-file" );
+ if ( m_vm.count( "input-type" ) == 0 )
+ throw CommandException( "Missing --input-type" );
+
+ string type = m_vm["input-type"].as<string>();
+ string filename;
+ if ( m_vm.count( "input-name" ) > 0 )
+ filename = m_vm["input-name"].as<string>();
+ string file = m_vm["input-file"].as<string>();
+ ifstream is( file.c_str(), ifstream::in );
+ boost::shared_ptr< ostream > os ( new ostream ( is.rdbuf( ) ) );
+ if ( is.fail( ) )
+ throw CommandException( string( "Unable to open file " ) + file );
+
+ document->setContentStream( os, type, filename );
+
+ is.close( );
+ }
+ else
+ throw CommandException( string( "Not a document object id: " ) + objIds.front() );
+ }
+ else if ( "create-folder" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ vector< string > args = m_vm["args"].as< vector< string > >( );
+ if ( args.size() < 2 )
+ throw CommandException( "Please provide a parent Id and folder name" );
+
+ libcmis::FolderPtr parent = session->getFolder( args[0] );
+
+ // Get the folder type to create
+ string folderType( "cmis:folder" );
+ if ( m_vm.count( "object-type" ) != 0 )
+ folderType = m_vm["object-type"].as<string>( );
+
+ libcmis::ObjectTypePtr type = session->getType( folderType );
+ if ( "cmis:folder" != type->getBaseType( )->getId( ) )
+ throw CommandException( string( "Not a folder type: " ) + folderType );
+
+ PropertyPtrMap properties;
+ map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( );
+
+ // Set the name
+ map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( string( "cmis:name" ) );
+ if ( typeIt == propertiesTypes.end( ) )
+ throw CommandException( string( "No cmis:name on the object type... weird" ) );
+ vector< string > nameValues;
+ string newFolderName = args[1];
+ for ( unsigned int i = 2; i < args.size( ); i++ )
+ {
+ newFolderName += " " + args[i];
+ }
+ nameValues.push_back( newFolderName );
+ libcmis::PropertyPtr nameProperty( new libcmis::Property( typeIt->second, nameValues ) );
+ properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) );
+
+ // Set the objectTypeId
+ typeIt = propertiesTypes.find( string( "cmis:objectTypeId" ) );
+ if ( typeIt == propertiesTypes.end( ) )
+ throw CommandException( string( "No cmis:objectTypeId on the object type... weird" ) );
+ vector< string > typeIdValues;
+ typeIdValues.push_back( folderType );
+ libcmis::PropertyPtr typeIdProperty( new libcmis::Property( typeIt->second, typeIdValues ) );
+ properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeIdProperty ) );
+
+ // Checks for the properties to set if any
+ map< string, string > propsToSet = getObjectProperties( );
+ for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it )
+ {
+ // Create the CMIS property if it exists
+ typeIt = propertiesTypes.find( it->first );
+ if ( typeIt != propertiesTypes.end( ) )
+ {
+ vector< string > values;
+ values.push_back( it->second );
+ libcmis::PropertyPtr cmisProperty( new libcmis::Property( typeIt->second, values ) );
+ properties.insert( pair< string, libcmis::PropertyPtr >( it->first, cmisProperty ) );
+ }
+ }
+
+ libcmis::FolderPtr created = parent->createFolder( properties );
+
+ cout << "------------------------------------------------" << endl;
+ cout << created->toString() << endl;
+ }
+ else if ( "create-document" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ vector< string > args = m_vm["args"].as< vector< string > >( );
+ if ( args.size() < 2 )
+ throw CommandException( "Please provide a parent Id and document name" );
+
+ libcmis::FolderPtr parent = session->getFolder( args[0] );
+
+ // Get the document type to create
+ string documentType( "cmis:document" );
+ if ( m_vm.count( "object-type" ) != 0 )
+ documentType = m_vm["object-type"].as<string>( );
+
+ libcmis::ObjectTypePtr type = session->getType( documentType );
+ if ( "cmis:document" != type->getBaseType( )->getId( ) )
+ throw CommandException( string( "Not a document type: " ) + documentType );
+
+ PropertyPtrMap properties;
+ map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( );
+
+ // Set the name
+ map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( string( "cmis:name" ) );
+ if ( typeIt == propertiesTypes.end( ) )
+ throw CommandException( string( "No cmis:name on the object type... weird" ) );
+ vector< string > nameValues;
+ string newDocumentName = args[1];
+ for ( unsigned int i = 2; i < args.size( ); i++ )
+ {
+ newDocumentName += " " + args[i];
+ }
+ nameValues.push_back( newDocumentName );
+ libcmis::PropertyPtr nameProperty( new libcmis::Property( typeIt->second, nameValues ) );
+ properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:name" ), nameProperty ) );
+
+ // Set the objectTypeId
+ typeIt = propertiesTypes.find( string( "cmis:objectTypeId" ) );
+ if ( typeIt == propertiesTypes.end( ) )
+ throw CommandException( string( "No cmis:objectTypeId on the object type... weird" ) );
+ vector< string > typeIdValues;
+ typeIdValues.push_back( documentType );
+ libcmis::PropertyPtr typeIdProperty( new libcmis::Property( typeIt->second, typeIdValues ) );
+ properties.insert( pair< string, libcmis::PropertyPtr >( string( "cmis:objectTypeId" ), typeIdProperty ) );
+
+ // Checks for the properties to set if any
+ map< string, string > propsToSet = getObjectProperties( );
+ for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it )
+ {
+ // Create the CMIS property if it exists
+ typeIt = propertiesTypes.find( it->first );
+ if ( typeIt != propertiesTypes.end( ) )
+ {
+ vector< string > values;
+ values.push_back( it->second );
+ libcmis::PropertyPtr cmisProperty( new libcmis::Property( typeIt->second, values ) );
+ properties.insert( pair< string, libcmis::PropertyPtr >( it->first, cmisProperty ) );
+ }
+ }
+
+ // Get the content type and stream
+ boost::shared_ptr< ostream > contentStream;
+ string contentType;
+ string filename;
+
+ bool hasInputFile = m_vm.count( "input-file" ) != 0;
+ bool hasInputType = m_vm.count( "input-type" ) != 0;
+ bool hasInputName = m_vm.count( "input-name" ) != 0;
+
+ if ( hasInputType && !hasInputFile )
+ throw CommandException( "Missing --input-file" );
+ if ( hasInputFile && !hasInputType )
+ throw CommandException( "Missing --input-type" );
+
+ if ( hasInputFile && hasInputType )
+ {
+ contentType = m_vm["input-type"].as<string>();
+ string file = m_vm["input-file"].as<string>();
+ fstream is( file.c_str() );
+ if ( is.fail( ) )
+ throw CommandException( string( "Unable to open file " ) + file );
+ contentStream.reset( new ostringstream( ios_base::out | ios_base::in ) );
+
+ *contentStream << is.rdbuf();
+ }
+
+ if ( hasInputName )
+ filename = m_vm[ "input-name" ].as< string >( );
+
+ // Actually create the document
+ libcmis::DocumentPtr created = parent->createDocument( properties, contentStream, contentType, filename );
+
+ cout << "------------------------------------------------" << endl;
+ cout << created->toString() << endl;
+ }
+ else if ( "update-object" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ vector< string > args = m_vm["args"].as< vector< string > >( );
+ if ( args.size() != 1 )
+ throw CommandException( "Please provide an object id" );
+
+ libcmis::ObjectPtr object = session->getObject( args[0] );
+ libcmis::ObjectTypePtr type = session->getType( object->getType( ) );
+ map< string, libcmis::PropertyTypePtr >& propertiesTypes = type->getPropertiesTypes( );
+
+ PropertyPtrMap properties;
+
+ // Checks for the properties to set if any
+ map< string, string > propsToSet = getObjectProperties( );
+ for ( map< string, string >::iterator it = propsToSet.begin(); it != propsToSet.end(); ++it )
+ {
+ // Create the CMIS property if it exists
+ map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertiesTypes.find( it->first );
+ if ( typeIt != propertiesTypes.end( ) && typeIt->second->isUpdatable( ) )
+ {
+ vector< string > values;
+ values.push_back( it->second );
+ libcmis::PropertyPtr property( new libcmis::Property( typeIt->second, values ) );
+ properties[ it->first ] = property;
+ }
+ }
+
+ libcmis::ObjectPtr updated = object->updateProperties( properties );
+
+ cout << "------------------------------------------------" << endl;
+ // Output updated instead of object as it may be different depending on the server
+ cout << updated->toString() << endl;
+ }
+ else if ( "move-object" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ vector< string > args = m_vm["args"].as< vector< string > > ( );
+ if ( args.size() != 3 )
+ throw CommandException( "Please provide an object id and source and destination folder ids" );
+ string& objId = args[0];
+ string& srcId = args[1];
+ string& dstId = args[2];
+
+ libcmis::ObjectPtr obj = session->getObject( objId );
+
+ libcmis::ObjectPtr src = session->getObject( srcId );
+ libcmis::FolderPtr srcFolder = boost::dynamic_pointer_cast< libcmis::Folder > ( src );
+ if ( !srcFolder )
+ throw CommandException( "Source object is not a folder" );
+
+ libcmis::ObjectPtr dst = session->getObject( dstId );
+ libcmis::FolderPtr dstFolder = boost::dynamic_pointer_cast< libcmis::Folder > ( dst );
+ if ( !dstFolder )
+ throw CommandException( "Destinaton object is not a folder" );
+
+ obj->move( srcFolder, dstFolder );
+
+ cout << "------------------------------------------------" << endl;
+ cout << obj->toString( ) << endl;
+ }
+ else if ( "delete" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the ids of the objects to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the node ids to delete as command args" );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+
+ vector< string > errors;
+ for ( vector< string >::iterator it = objIds.begin(); it != objIds.end(); ++it )
+ {
+ libcmis::ObjectPtr cmisObj = session->getObject( *it );
+ libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( cmisObj.get() );
+ if ( NULL != folder )
+ {
+ try
+ {
+ vector< string > failed = folder->removeTree( );
+ string error;
+ for ( vector< string >::iterator dumpIt = failed.begin( );
+ dumpIt != failed.end( ); ++dumpIt )
+ {
+ if ( dumpIt == failed.begin( ) )
+ error += "Failed to remove children nodes: ";
+ else
+ error += ", ";
+ error = *dumpIt;
+ }
+ if ( !error.empty( ) )
+ errors.push_back( error );
+ }
+ catch ( const libcmis::Exception& e )
+ {
+ string msg = *it + ": " + e.what( );
+ errors.push_back( msg );
+ }
+ }
+ else if ( cmisObj.get() )
+ {
+ try
+ {
+ cmisObj->remove( );
+ }
+ catch ( const libcmis::Exception& e )
+ {
+ string msg = *it + ": " + e.what( );
+ errors.push_back( msg );
+ }
+ }
+ else
+ {
+ string msg = "No such node: " + *it;
+ errors.push_back( msg );
+ }
+ }
+
+ // Show the errors
+ if ( !errors.empty( ) )
+ {
+ cout << "Errors:" << endl;
+ for ( vector< string >::iterator it = errors.begin( ); it != errors.end( ); ++it )
+ {
+ cout << "\t" << *it << endl;
+ }
+ }
+ else
+ {
+ cout << "All nodes have been removed" << endl;
+ }
+ }
+ else if ( "checkout" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the ids of the objects to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the node id to checkout as command args" );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+
+ libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() );
+ if ( cmisObj.get() )
+ {
+ libcmis::Document* doc = dynamic_cast< libcmis::Document* >( cmisObj.get() );
+ if ( NULL != doc )
+ {
+ libcmis::DocumentPtr pwc = doc->checkOut( );
+ if ( pwc.get( ) )
+ {
+ cout << "------------------------------------------------" << endl;
+ cout << pwc->toString() << endl;
+ }
+ else
+ cout << "No Private Working Copy returned?" << endl;
+ }
+ else
+ throw CommandException( string( "Not a document object id: " ) + objIds.front() );
+ }
+ else
+ cout << "No such node: " << objIds.front() << endl;
+ }
+ else if ( "cancel-checkout" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the ids of the objects to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the private working copy object id to cancel as command args" );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+
+ libcmis::ObjectPtr cmisObj = session->getObject( objIds.front() );
+ cout << "------------------------------------------------" << endl;
+ if ( cmisObj.get() )
+ {
+ libcmis::Document* doc = dynamic_cast< libcmis::Document* >( cmisObj.get() );
+ if ( NULL != doc )
+ {
+ doc->cancelCheckout( );
+ cout << "Checkout cancelled" << endl;
+ }
+ else
+ throw CommandException( string( "Not a document object id: " ) + objIds.front() );
+ }
+ else
+ cout << "No such node: " << objIds.front() << endl;
+ }
+ else if ( "checkin" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the ids of the objects to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the node id to checkin as command args" );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+
+ libcmis::ObjectPtr object = session->getObject( objIds.front() );
+ if ( object.get() )
+ {
+ // Create the properties map
+ PropertyPtrMap properties;
+ map< string, string > propsToSet = getObjectProperties( );
+ libcmis::ObjectTypePtr type = session->getType( object->getType( ) );
+ map< string, libcmis::PropertyTypePtr > propertyTypes = type->getPropertiesTypes( );
+ for ( map< string, string >::iterator propIt = propsToSet.begin();
+ propIt != propsToSet.end(); ++propIt )
+ {
+ string name = propIt->first;
+ map< string, libcmis::PropertyTypePtr >::iterator typeIt = propertyTypes.find( name );
+ if ( typeIt != propertyTypes.end( ) )
+ {
+ vector< string > values;
+ values.push_back( propIt->second );
+ libcmis::PropertyPtr prop( new libcmis::Property( typeIt->second, values ) );
+ properties.insert( pair< string, libcmis::PropertyPtr >( name, prop ) );
+ }
+ }
+
+ // Get the content stream if any
+ string contentType;
+ string filename;
+ boost::shared_ptr< ostream > stream;
+ if ( m_vm.count( "input-file" ) > 0 )
+ {
+ string file = m_vm["input-file"].as<string>();
+ ifstream is( file.c_str(), ios_base::in );
+ stringstream os;
+ os << is.rdbuf( );
+ if ( is.fail( ) )
+ throw CommandException( string( "Unable to open file " ) + file );
+
+ string content = os.str( );
+ stream.reset( new stringstream( content ) );
+ is.close( );
+
+ if ( m_vm.count( "input-type" ) > 0 )
+ {
+ contentType = m_vm["input-type"].as<string>();
+ }
+ if ( m_vm.count( "input-name" ) > 0 )
+ {
+ filename = m_vm["input-name"].as<string>();
+ }
+ }
+
+ bool major = false;
+ if ( m_vm.count( "major" ) > 0 )
+ major = "yes";
+
+ string comment;
+ if ( m_vm.count( "message" ) > 0 )
+ comment = m_vm["message"].as< string >( );
+
+ libcmis::Document* doc = dynamic_cast< libcmis::Document* >( object.get() );
+ if ( NULL != doc )
+ {
+ libcmis::DocumentPtr newDoc = doc->checkIn( major, comment, properties, stream, contentType, filename );
+
+ cout << "------------------------------------------------" << endl;
+ cout << newDoc->toString() << endl;
+ }
+ else
+ throw CommandException( string( "Not a document object id: " ) + objIds.front() );
+ }
+ else
+ cout << "No such node: " << objIds.front() << endl;
+ }
+ else if ( "get-versions" == command )
+ {
+ unique_ptr<libcmis::Session> session( getSession( ) );
+
+ // Get the ids of the objects to fetch
+ if ( m_vm.count( "args" ) == 0 )
+ throw CommandException( "Please provide the node id to get versions from as command args" );
+
+ vector< string > objIds = m_vm["args"].as< vector< string > >( );
+
+ libcmis::ObjectPtr object = session->getObject( objIds.front() );
+ if ( object.get() )
+ {
+ libcmis::Document* doc = dynamic_cast< libcmis::Document* >( object.get() );
+ if ( NULL != doc )
+ {
+ vector< libcmis::DocumentPtr > versions = doc->getAllVersions( );
+
+ for ( vector< libcmis::DocumentPtr >::iterator it = versions.begin( );
+ it != versions.end( ); ++it )
+ {
+ cout << "------------------------------------------------" << endl;
+ cout << ( *it )->toString() << endl;
+ }
+ }
+ else
+ throw CommandException( string( "Not a document object id: " ) + objIds.front() );
+ }
+ else
+ cout << "No such node: " << objIds.front() << endl;
+ }
+ else if ( "help" == command )
+ {
+ printHelp();
+ }
+ else
+ {
+ cerr << "------------------------------------------------" << endl;
+ cerr << "ERROR: Unknown command: " << command << endl;
+ cerr << "------------------------------------------------" << endl;
+ printHelp( );
+ }
+
+ // TODO Add some more useful commands here
+ }
+}
+
+options_description CmisClient::getOptionsDescription( )
+{
+ options_description desc( "Allowed options" );
+ desc.add_options( )
+ ( "help", "Produce help message and exists" )
+ ( "verbose,v", "Show loads of useful messages for debugging" )
+ ( "url", value< string >(), "URL of the binding of the server" )
+ ( "repository,r", value< string >(), "Name of the repository to use" )
+ ( "username,u", value< string >(), "Username used to authenticate to the repository" )
+ ( "password,p", value< string >(), "Password used to authenticate to the repository" )
+ ( "no-ssl-check", "Disable the verification of SSL certificates. This may come handy"
+ "for self-signed certificates for example, though it lowers the security" )
+ ( "proxy", value< string >(), "HTTP proxy url to override the system settings" )
+ ( "noproxy", value< string >(), "Coma separated list if host and domain names not going"
+ "through the proxy" )
+ ( "proxy-username", value< string >(), "Username to authenticate on the proxy" )
+ ( "proxy-password", value< string >(), "Password to authenticate on the proxy" )
+ ( "oauth2-client-id", value< string >(), "OAuth2 application client_id" )
+ ( "oauth2-client-secret", value< string >(), "OAuth2 application client_secret" )
+ ( "oauth2-auth-url", value< string >(), "URL to authenticate in the OAuth2 flow" )
+ ( "oauth2-token-url", value< string >(), "URL to convert code to tokens in the OAuth2 flow" )
+ ( "oauth2-redirect-uri", value< string >(), "redirect URI indicating that the authentication is finished in OAuth2 flow" )
+ ( "oauth2-scope", value< string >(), "The authentication scope in OAuth2" )
+ ( "oauth2-auth-code", value< string >(), "The authentication code required to get the access token" )
+ ;
+
+ options_description setcontentOpts( "modification operations options" );
+ setcontentOpts.add_options( )
+ ( "input-file", value< string >(), "File to push to the repository" )
+ ( "input-type", value< string >(), "Mime type of the file to push to the repository" )
+ ( "input-name", value< string >(), "Name of the file to push to the repository"
+ "(may be different from the local name)" )
+ ( "object-type", value< string >(), "CMIS type of the object to create" )
+ ( "object-property", value< vector< string > >(), "under the form prop-id=prop-value, defines a property"
+ "to be set on the object" )
+ ( "message,m", value< string >(), "Check in message" )
+ ( "major", "The version to create during the check in will be a major version." )
+ ( "stream-id", value< string >(), "streamId of the rendition to get content." )
+ ;
+
+ desc.add( setcontentOpts );
+ return desc;
+}
+
+void CmisClient::printHelp( )
+{
+ cerr << "cmis-client [options] [command] arguments" << endl;
+
+ cerr << endl << "Commands" << endl;
+ cerr << " list-repos\n"
+ " Lists the repositories available on the server" << endl;
+ cerr << " repo-infos\n"
+ " Show the informations and capabilities of the selected repository" << endl;
+ cerr << " show-root\n"
+ " Dump the root node of the repository." << endl;
+ cerr << " type-by-id <Type Id 1> [... <Type Id N>]\n"
+ " Dumps the type informations for all the ids." << endl;
+ cerr << " show-by-id <Object Id 1> [... <Object Id N>]\n"
+ " Dumps the objects informations for all the ids." << endl;
+ cerr << " show-by-path <Object Path 1> [... <Object Path N>]\n"
+ " Dumps the objects informations for all the paths." << endl;
+ cerr << " get-content <Object Id>\n"
+ " Saves the stream of the content object in the\n"
+ " current folder. Any existing file is overwritten.\n"
+ " streamId can be used to get the desired rendition with --stream-id"<< endl;
+ cerr << " set-content <Object Id>\n"
+ " Replaces the stream of the content object by the\n"
+ " file selected with --input-file." << endl;
+ cerr << " create-folder <Parent Id> <Folder Name>\n"
+ " Creates a new folder inside the folder <Parent Id> named <Folder Name>." << endl;
+ cerr << " create-document <Parent Id> <Document Name>\n"
+ " Creates a new document inside the folder <Parent Id>\n"
+ " named <Document Name>.\n"
+ " Note that --input-file and --input-type may be requested if\n"
+ " the server requires a content stream." << endl;
+ cerr << " update-object <Object Id>\n"
+ " Update the object matching id <Object Id> with the properties\n"
+ " defined with --object-property." << endl;
+ cerr << " move-object <Object Id> <Source Folder Id> <Destination Folder Id>\n"
+ " Move the object matching id <Object Id> from the\n"
+ " folder <Source Folder Id> to the folder <Destination Folder Id>." << endl;
+ cerr << " delete <Object Id 1> [... <Object Id N>]\n"
+ " Delete the objects corresponding to the ids. If the node\n"
+ " is a folder, its content will be removed as well." << endl;
+ cerr << " checkout <Object Id>\n"
+ " Check out the document corresponding to the id and shows the\n"
+ " Private Working Copy document infos." << endl;
+ cerr << " cancel-checkout <Object Id>\n"
+ " Cancel the Private Working Copy corresponding to the id" << endl;
+ cerr << " checkin <Object Id>\n"
+ " Check in the Private Working copy corresponding to the id.\n"
+ " Use the --message and --major parameters to give more\n"
+ " details about the new version.\n"
+ " The modification options may be needed to set the new\n"
+ " version properties and content stream if the repository\n"
+ " doesn't allow to change the private working copies." << endl;
+ cerr << " get-versions <Object-Id>\n"
+ " Show all the versions of a document." << endl;
+ cerr << " help\n"
+ " Prints this help message and exits (like --help option)." << endl;
+
+ cerr << endl << getOptionsDescription() << endl;
+}
+
+int main ( int argc, char* argv[] )
+{
+ options_description hidden( "Hidden options" );
+ hidden.add_options( )
+ ( "command", value< string >(), "Command" )
+ ( "args", value< vector< string > >(), "Arguments for the command" )
+ ;
+
+ options_description allOptions = CmisClient::getOptionsDescription( );
+ allOptions.add( hidden );
+
+ positional_options_description pd;
+ pd.add( "command", 1 );
+ pd.add( "args", -1 );
+
+ variables_map vm;
+ store( command_line_parser( argc, argv ).options( allOptions ).positional( pd ).run( ), vm );
+ notify( vm );
+
+ CmisClient client( vm );
+ try
+ {
+ client.execute( );
+ }
+ catch ( const CommandException& e )
+ {
+ cerr << "------------------------------------------------" << endl;
+ cerr << "ERROR: " << e.what() << endl;
+ cerr << "------------------------------------------------" << endl;
+ client.printHelp();
+ return 1;
+ }
+ catch ( const libcmis::Exception& e )
+ {
+ cerr << "------------------------------------------------" << endl;
+ cerr << "ERROR: " << e.what() << endl;
+ cerr << "------------------------------------------------" << endl;
+ return 1;
+ }
+ catch ( const exception& e )
+ {
+ cerr << "------------------------------------------------" << endl;
+ cerr << "ERROR: " << e.what() << endl;
+ cerr << "------------------------------------------------" << endl;
+ return 1;
+ }
+
+ return 0;
+}