1 diff --git a/src/libcmis/http-session.cxx b/src/libcmis/http-session.cxx
2 index 2638482..227667e 100644
3 --- a/src/libcmis/http-session.cxx
4 +++ b/src/libcmis/http-session.cxx
5 @@ -293,6 +293,94 @@ libcmis::HttpResponsePtr HttpSession::httpGetRequest( string url )
6 return response;
7 }
8
9 +libcmis::HttpResponsePtr HttpSession::httpPatchRequest( string url, istream& is, vector< string > headers )
10 +{
11 + checkOAuth2( url );
12 +
13 + // Duplicate istream in case we need to retry
14 + string isStr( static_cast< stringstream const&>( stringstream( ) << is.rdbuf( ) ).str( ) );
15 +
16 + istringstream isOriginal( isStr ), isBackup( isStr );
17 +
18 + // Reset the handle for the request
19 + curl_easy_reset( m_curlHandle );
20 + initProtocols( );
21 +
22 + libcmis::HttpResponsePtr response( new libcmis::HttpResponse( ) );
23 +
24 + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, lcl_bufferData );
25 + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, response->getData( ).get( ) );
26 +
27 + curl_easy_setopt( m_curlHandle, CURLOPT_HEADERFUNCTION, &lcl_getHeaders );
28 + curl_easy_setopt( m_curlHandle, CURLOPT_WRITEHEADER, response.get() );
29 +
30 + curl_easy_setopt( m_curlHandle, CURLOPT_MAXREDIRS, 20);
31 +
32 + // Get the stream length
33 + is.seekg( 0, ios::end );
34 + long size = is.tellg( );
35 + is.seekg( 0, ios::beg );
36 + curl_easy_setopt( m_curlHandle, CURLOPT_INFILESIZE, size );
37 + curl_easy_setopt( m_curlHandle, CURLOPT_READDATA, &isOriginal );
38 + curl_easy_setopt( m_curlHandle, CURLOPT_READFUNCTION, lcl_readStream );
39 + curl_easy_setopt( m_curlHandle, CURLOPT_UPLOAD, 1 );
40 + curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "PATCH" );
41 + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLFUNCTION, lcl_ioctlStream );
42 + curl_easy_setopt( m_curlHandle, CURLOPT_IOCTLDATA, &isOriginal );
43 +
44 + // If we know for sure that 100-Continue won't be accepted,
45 + // don't even try with it to save one HTTP request.
46 + if ( m_no100Continue )
47 + headers.push_back( "Expect:" );
48 + try
49 + {
50 + httpRunRequest( url, headers );
51 + response->getData( )->finish();
52 + }
53 + catch ( const CurlException& )
54 + {
55 + long status = getHttpStatus( );
56 + /** If we had a HTTP 417 response, this is likely to be due to some
57 + HTTP 1.0 proxy / server not accepting the "Expect: 100-continue"
58 + header. Try to disable this header and try again.
59 + */
60 + if ( status == 417 && !m_no100Continue)
61 + {
62 + // Remember that we don't want 100-Continue for the future requests
63 + m_no100Continue = true;
64 + response = httpPutRequest( url, isBackup, headers );
65 + }
66 +
67 + // If the access token is expired, we get 401 error,
68 + // Need to use the refresh token to get a new one.
69 + if ( status == 401 && !getRefreshToken( ).empty( ) && !m_refreshedToken )
70 + {
71 +
72 + // Refresh the token
73 + oauth2Refresh();
74 +
75 + // Resend the query
76 + try
77 + {
78 + // Avoid infinite recursive call
79 + m_refreshedToken = true;
80 + response = httpPutRequest( url, isBackup, headers );
81 + m_refreshedToken = false;
82 + }
83 + catch (const CurlException&)
84 + {
85 + m_refreshedToken = false;
86 + throw;
87 + }
88 + }
89 + // Has tried but failed
90 + if ( ( status != 417 || m_no100Continue ) &&
91 + ( status != 401 || getRefreshToken( ).empty( ) || m_refreshedToken ) ) throw;
92 + }
93 + m_refreshedToken = false;
94 + return response;
95 +}
96 +
97 libcmis::HttpResponsePtr HttpSession::httpPutRequest( string url, istream& is, vector< string > headers )
98 {
99 checkOAuth2( url );
100 diff --git a/src/libcmis/http-session.hxx b/src/libcmis/http-session.hxx
101 index 851d52d..29de64d 100644
102 --- a/src/libcmis/http-session.hxx
103 +++ b/src/libcmis/http-session.hxx
104 @@ -132,6 +132,9 @@ class HttpSession
105 virtual void setOAuth2Data( libcmis::OAuth2DataPtr oauth2 );
106
107 libcmis::HttpResponsePtr httpGetRequest( std::string url );
108 + libcmis::HttpResponsePtr httpPatchRequest( std::string url,
109 + std::istream& is,
110 + std::vector< std::string > headers );
111 libcmis::HttpResponsePtr httpPutRequest( std::string url,
112 std::istream& is,
113 std::vector< std::string > headers );
114 diff --git a/src/libcmis/oauth2-handler.cxx b/src/libcmis/oauth2-handler.cxx
115 index a3320e3..842769f 100644
116 --- a/src/libcmis/oauth2-handler.cxx
117 +++ b/src/libcmis/oauth2-handler.cxx
118 @@ -91,8 +91,8 @@ void OAuth2Handler::fetchTokens( string authCode )
119 string post =
120 "code=" + authCode +
121 "&client_id=" + m_data->getClientId() +
122 - "&client_secret=" + m_data->getClientSecret() +
123 "&redirect_uri=" + m_data->getRedirectUri() +
124 + "&scope=" + libcmis::escape( m_data->getScope() ) +
125 "&grant_type=authorization_code" ;
126
127 istringstream is( post );
128 @@ -121,7 +121,6 @@ void OAuth2Handler::refresh( )
129 string post =
130 "refresh_token=" + m_refresh +
131 "&client_id=" + m_data->getClientId() +
132 - "&client_secret=" + m_data->getClientSecret() +
133 "&grant_type=refresh_token" ;
134
135 istringstream is( post );
136 diff --git a/src/libcmis/oauth2-providers.cxx b/src/libcmis/oauth2-providers.cxx
137 index 8cf9652..654021f 100644
138 --- a/src/libcmis/oauth2-providers.cxx
139 +++ b/src/libcmis/oauth2-providers.cxx
140 @@ -312,7 +312,7 @@ OAuth2Parser OAuth2Providers::getOAuth2Parser( const std::string& url )
141 return OAuth2Alfresco;
142 else if ( boost::starts_with( url, "https://www.googleapis.com/drive/v2" ) )
143 return OAuth2Gdrive;
144 - else if ( boost::starts_with( url, "https://apis.live.net/v5.0" ) )
145 + else if ( boost::starts_with( url, "https://graph.microsoft.com/v1.0" ) )
146 return OAuth2Onedrive;
147
148 return OAuth2Gdrive;
149 diff --git a/src/libcmis/onedrive-document.cxx b/src/libcmis/onedrive-document.cxx
150 index f753b42..863a92f 100644
151 --- a/src/libcmis/onedrive-document.cxx
152 +++ b/src/libcmis/onedrive-document.cxx
153 @@ -73,7 +73,7 @@ boost::shared_ptr< istream > OneDriveDocument::getContentStream( string /*stream
154 boost::shared_ptr< istream > stream;
155 string streamUrl = getStringProperty( "source" );
156 if ( streamUrl.empty( ) )
157 - throw libcmis::Exception( "can not found stream url" );
158 + throw libcmis::Exception( "could not find stream url" );
159
160 try
161 {
162 @@ -89,15 +89,15 @@ boost::shared_ptr< istream > OneDriveDocument::getContentStream( string /*stream
163 void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os,
164 string /*contentType*/,
165 string fileName,
166 - bool /*overwrite*/ )
167 + bool bReplaceExisting )
168 {
169 if ( !os.get( ) )
170 throw libcmis::Exception( "Missing stream" );
171 -
172 +
173 string metaUrl = getUrl( );
174
175 // Update file name meta information
176 - if ( !fileName.empty( ) && fileName != getContentFilename( ) )
177 + if ( bReplaceExisting && !fileName.empty( ) && fileName != getContentFilename( ) )
178 {
179 Json metaJson;
180 Json fileJson( fileName.c_str( ) );
181 @@ -108,7 +108,7 @@ void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os,
182 headers.push_back( "Content-Type: application/json" );
183 try
184 {
185 - getSession()->httpPutRequest( metaUrl, is, headers );
186 + getSession()->httpPatchRequest( metaUrl, is, headers );
187 }
188 catch ( const CurlException& e )
189 {
190 @@ -117,9 +117,9 @@ void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os,
191 }
192
193 fileName = libcmis::escape( getStringProperty( "cmis:name" ) );
194 - string putUrl = getSession( )->getBindingUrl( ) + "/" +
195 - getStringProperty( "cmis:parentId" ) + "/files/" +
196 - fileName + "?overwrite=true";
197 + string putUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" +
198 + getStringProperty( "cmis:parentId" ) + ":/" +
199 + fileName + ":/content";
200
201 // Upload stream
202 boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) );
203 @@ -142,6 +142,7 @@ void OneDriveDocument::setContentStream( boost::shared_ptr< ostream > os,
204 libcmis::DocumentPtr OneDriveDocument::checkOut( )
205 {
206 // OneDrive doesn't have CheckOut, so just return the same document here
207 + // TODO: no longer true - onedrive now has checkout/checkin
208 libcmis::ObjectPtr obj = getSession( )->getObject( getId( ) );
209 libcmis::DocumentPtr checkout =
210 boost::dynamic_pointer_cast< libcmis::Document > ( obj );
211 diff --git a/src/libcmis/onedrive-folder.cxx b/src/libcmis/onedrive-folder.cxx
212 index a9ae694..c1980c8 100644
213 --- a/src/libcmis/onedrive-folder.cxx
214 +++ b/src/libcmis/onedrive-folder.cxx
215 @@ -57,7 +57,9 @@ OneDriveFolder::~OneDriveFolder( )
216 vector< libcmis::ObjectPtr > OneDriveFolder::getChildren( )
217 {
218 vector< libcmis::ObjectPtr > children;
219 - string query = getSession( )->getBindingUrl( ) + "/" + getId( ) + "/files";
220 + // TODO: limited to 200 items by default - to get more one would have to
221 + // follow @odata.nextLink or change pagination size
222 + string query = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children";
223
224 string res;
225 try
226 @@ -70,7 +72,7 @@ vector< libcmis::ObjectPtr > OneDriveFolder::getChildren( )
227 }
228
229 Json jsonRes = Json::parse( res );
230 - Json::JsonVector objs = jsonRes["data"].getList( );
231 + Json::JsonVector objs = jsonRes["value"].getList( );
232
233 // Create children objects from Json objects
234 for(unsigned int i = 0; i < objs.size(); i++)
235 @@ -85,8 +87,7 @@ libcmis::FolderPtr OneDriveFolder::createFolder(
236 const PropertyPtrMap& properties )
237 {
238 Json propsJson = OneDriveUtils::toOneDriveJson( properties );
239 -
240 - string uploadUrl = getSession( )->getBindingUrl( ) + "/" + getId( );
241 + string uploadUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( ) + "/children";
242
243 std::istringstream is( propsJson.toString( ) );
244 string response;
245 @@ -126,9 +127,10 @@ libcmis::DocumentPtr OneDriveFolder::createDocument(
246 }
247 }
248
249 + // TODO: limited to 4MB, larger uploads need dedicated UploadSession
250 fileName = libcmis::escape( fileName );
251 - string newDocUrl = getSession( )->getBindingUrl( ) + "/" +
252 - getId( ) + "/files/" + fileName;
253 + string newDocUrl = getSession( )->getBindingUrl( ) + "/me/drive/items/" +
254 + getId( ) + ":/" + fileName + ":/content";
255 boost::shared_ptr< istream> is ( new istream ( os->rdbuf( ) ) );
256 vector< string > headers;
257 string res;
258 diff --git a/src/libcmis/onedrive-object.cxx b/src/libcmis/onedrive-object.cxx
259 index 976a97b..8deb591 100644
260 --- a/src/libcmis/onedrive-object.cxx
261 +++ b/src/libcmis/onedrive-object.cxx
262 @@ -65,7 +65,7 @@ void OneDriveObject::initializeFromJson ( Json json, string /*id*/, string /*nam
263 Json::JsonObject objs = json.getObjects( );
264 Json::JsonObject::iterator it;
265 PropertyPtr property;
266 - bool isFolder = json["type"].toString( ) == "folder";
267 + bool isFolder = json["folder"].toString( ) != "";
268 for ( it = objs.begin( ); it != objs.end( ); ++it)
269 {
270 property.reset( new OneDriveProperty( it->first, it->second ) );
271 @@ -74,7 +74,12 @@ void OneDriveObject::initializeFromJson ( Json json, string /*id*/, string /*nam
272 {
273 property.reset( new OneDriveProperty( "cmis:contentStreamFileName", it->second ) );
274 m_properties[ property->getPropertyType( )->getId()] = property;
275 - }
276 + } else if ( it->first == "parentReference" ) {
277 + if (it->second["id"].toString() != "") {
278 + property.reset( new OneDriveProperty( "cmis:parentId", it->second["id"] ) );
279 + m_properties[ property->getPropertyType( )->getId()] = property;
280 + }
281 + }
282 }
283
284 m_refreshTimestamp = time( NULL );
285 @@ -122,7 +127,7 @@ void OneDriveObject::remove( bool /*allVersions*/ )
286
287 string OneDriveObject::getUrl( )
288 {
289 - return getSession( )->getBindingUrl( ) + "/" + getId( );
290 + return getSession( )->getBindingUrl( ) + "/me/drive/items/" + getId( );
291 }
292
293 string OneDriveObject::getUploadUrl( )
294 @@ -152,7 +157,7 @@ libcmis::ObjectPtr OneDriveObject::updateProperties(
295 {
296 vector< string > headers;
297 headers.push_back( "Content-Type: application/json" );
298 - response = getSession( )->httpPutRequest( getUrl( ), is, headers );
299 + response = getSession( )->httpPatchRequest( getUrl( ), is, headers );
300 }
301 catch ( const CurlException& e )
302 {
303 diff --git a/src/libcmis/onedrive-repository.cxx b/src/libcmis/onedrive-repository.cxx
304 index 3eaac9c..b01f5c2 100644
305 --- a/src/libcmis/onedrive-repository.cxx
306 +++ b/src/libcmis/onedrive-repository.cxx
307 @@ -35,7 +35,7 @@ OneDriveRepository::OneDriveRepository( ) :
308 m_description = "One Drive repository";
309 m_productName = "One Drive";
310 m_productVersion = "v5";
311 - m_rootId = "me/skydrive";
312 + m_rootId = "/me/drive/root";
313
314 m_capabilities[ ACL ] = "discover";
315 m_capabilities[ AllVersionsSearchable ] = "true";
316 diff --git a/src/libcmis/onedrive-session.cxx b/src/libcmis/onedrive-session.cxx
317 index c6f4270..a603278 100644
318 --- a/src/libcmis/onedrive-session.cxx
319 +++ b/src/libcmis/onedrive-session.cxx
320 @@ -79,7 +79,9 @@ libcmis::ObjectPtr OneDriveSession::getObject( string objectId )
321 {
322 // Run the http request to get the properties definition
323 string res;
324 - string objectLink = m_bindingUrl + "/" + objectId;
325 + string objectLink = m_bindingUrl + "/me/drive/items/" + objectId;
326 + if (objectId == getRootId())
327 + objectLink = m_bindingUrl + objectId;
328 try
329 {
330 res = httpGetRequest( objectLink )->getStream()->str();
331 @@ -95,12 +97,11 @@ libcmis::ObjectPtr OneDriveSession::getObject( string objectId )
332 libcmis::ObjectPtr OneDriveSession::getObjectFromJson( Json& jsonRes )
333 {
334 libcmis::ObjectPtr object;
335 - string kind = jsonRes["type"].toString( );
336 - if ( kind == "folder" || kind == "album" )
337 + if ( jsonRes["folder"].toString() != "" )
338 {
339 object.reset( new OneDriveFolder( this, jsonRes ) );
340 }
341 - else if ( kind == "file" )
342 + else if ( jsonRes["file"].toString() != "" )
343 {
344 object.reset( new OneDriveDocument( this, jsonRes ) );
345 }
346 @@ -113,44 +114,18 @@ libcmis::ObjectPtr OneDriveSession::getObjectFromJson( Json& jsonRes )
347
348 libcmis::ObjectPtr OneDriveSession::getObjectByPath( string path )
349 {
350 - string id;
351 - if ( path == "/" )
352 - {
353 - id = "me/skydrive";
354 - }
355 - else
356 + string res;
357 + string objectQuery = m_bindingUrl + "/me/drive/root:" + libcmis::escape( path );
358 + try
359 {
360 - path = "/SkyDrive" + path;
361 - size_t pos = path.rfind("/");
362 - string name = libcmis::escape( path.substr( pos + 1, path.size( ) ) );
363 - string res;
364 - string objectQuery = m_bindingUrl + "/me/skydrive/search?q=" + name;
365 - try
366 - {
367 - res = httpGetRequest( objectQuery )->getStream( )->str( );
368 - }
369 - catch ( const CurlException& e )
370 - {
371 - throw e.getCmisException( );
372 - }
373 - Json jsonRes = Json::parse( res );
374 - Json::JsonVector objs = jsonRes["data"].getList( );
375 -
376 - // Searching for a match in the path to the object
377 - for ( unsigned int i = 0; i < objs.size( ); i++ )
378 - {
379 - if ( isAPathMatch( objs[i], path ) )
380 - {
381 - id = objs[i]["id"].toString( );
382 - break;
383 - }
384 - }
385 + res = httpGetRequest( objectQuery )->getStream( )->str( );
386 }
387 - if ( id.empty( ) )
388 + catch ( const CurlException& e )
389 {
390 - throw libcmis::Exception( "No file could be found" );
391 + throw libcmis::Exception( "No file could be found for path " + path + ": " + e.what() );
392 }
393 - return getObject( id );
394 + Json jsonRes = Json::parse( res );
395 + return getObjectFromJson( jsonRes );
396 }
397
398 bool OneDriveSession::isAPathMatch( Json objectJson, string path )
399 diff --git a/src/libcmis/onedrive-utils.cxx b/src/libcmis/onedrive-utils.cxx
400 index dc6ec5d..17ed324 100644
401 --- a/src/libcmis/onedrive-utils.cxx
402 +++ b/src/libcmis/onedrive-utils.cxx
403 @@ -44,16 +44,16 @@ string OneDriveUtils::toCmisKey( const string& key )
404 convertedKey = "cmis:createdBy";
405 else if ( key == "description" )
406 convertedKey = "cmis:description";
407 - else if ( key == "created_time" )
408 + else if ( key == "createdDateTime" )
409 convertedKey = "cmis:creationDate";
410 - else if ( key == "updated_time" )
411 + else if ( key == "lastModifiedDateTime" )
412 convertedKey = "cmis:lastModificationDate";
413 else if ( key == "name" )
414 convertedKey = "cmis:name";
415 else if ( key == "size" )
416 convertedKey = "cmis:contentStreamLength";
417 - else if ( key == "parent_id" )
418 - convertedKey = "cmis:parentId";
419 + else if ( key == "@microsoft.graph.downloadUrl" )
420 + convertedKey = "source";
421 else convertedKey = key;
422 return convertedKey;
423 }
424 @@ -75,8 +75,6 @@ string OneDriveUtils::toOneDriveKey( const string& key )
425 convertedKey = "name";
426 else if ( key == "cmis:contentStreamLength" )
427 convertedKey = "file_size";
428 - else if ( key == "cmis:parentId" )
429 - convertedKey = "parent_id";
430 else convertedKey = key;
431 return convertedKey;
432 }
433 diff --git a/src/libcmis/session-factory.cxx b/src/libcmis/session-factory.cxx
434 index ba55cd9..e740afb 100644
435 --- a/src/libcmis/session-factory.cxx
436 +++ b/src/libcmis/session-factory.cxx
437 @@ -71,7 +71,7 @@ namespace libcmis
438 session = new GDriveSession( bindingUrl, username, password,
439 oauth2, verbose );
440 }
441 - else if ( bindingUrl == "https://apis.live.net/v5.0" )
442 + else if ( bindingUrl == "https://graph.microsoft.com/v1.0" )
443 {
444 session = new OneDriveSession( bindingUrl, username, password,
445 oauth2, verbose);
|