summaryrefslogtreecommitdiff
path: root/libcmis/libcmis_onedrive.patch
blob: 60d7e7b3be69b1873da6a6505f8e5f4bc1068a54 (plain)
    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);

Generated by cgit