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