1 /*
2  * SessionPerFilePathStorage.cpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #include <shared_core/Error.hpp>
17 #include <shared_core/FilePath.hpp>
18 
19 #include <core/FileSerializer.hpp>
20 #include <core/FileUtils.hpp>
21 
22 #include <session/SessionModuleContext.hpp>
23 
24 using namespace rstudio::core;
25 
26 namespace rstudio {
27 namespace session {
28 namespace module_context {
29 
perFilePathStorage(const std::string & scope,const FilePath & filePath,bool directory,FilePath * pStorage)30 Error perFilePathStorage(const std::string& scope, const FilePath& filePath, bool directory, FilePath* pStorage)
31 {
32    // url escape path (so we can use key=value persistence)
33    std::string escapedPath = http::util::urlEncode(filePath.getAbsolutePath());
34 
35    // read index
36    FilePath dbPath = module_context::scopedScratchPath().completePath(scope);
37    Error error = dbPath.ensureDirectory();
38    if (error)
39       return error;
40    std::map<std::string,std::string> index;
41    FilePath indexFile = dbPath.completePath("INDEX");
42    if (indexFile.exists())
43    {
44       error = readStringMapFromFile(indexFile, &index);
45       if (error)
46          return error;
47    }
48 
49    // use existing storage if it exists, otherwise create new
50    std::string storage = index[escapedPath];
51    if (storage.empty())
52    {
53       // create storage
54       FilePath storageFile = file_utils::uniqueFilePath(dbPath);
55       if (directory)
56       {
57          error = storageFile.ensureDirectory();
58          if (error)
59             return error;
60       }
61       else
62       {
63          error = core::writeStringToFile(storageFile, "");
64          if (error)
65             return error;
66       }
67 
68       // update storage
69       storage = storageFile.getFilename();
70 
71       //  update index
72       index[escapedPath] = storage;
73       Error error = writeStringMapToFile(indexFile, index);
74       if (error)
75          return error;
76    }
77 
78    // return the path
79    *pStorage = dbPath.completeChildPath(storage);
80    return Success();
81 }
82 
83 
84 } // namespace module_context
85 } // namespace session
86 } // namespace rstudio
87