1 /*
2  * SessionViewPdf.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 "SessionViewPdf.hpp"
17 
18 #include <shared_core/Error.hpp>
19 #include <shared_core/FilePath.hpp>
20 #include <core/Exec.hpp>
21 
22 #include <core/http/Util.hpp>
23 
24 #include <session/SessionModuleContext.hpp>
25 
26 #define kPdfJsPath "/pdf_js/"
27 
28 using namespace rstudio::core;
29 
30 namespace rstudio {
31 namespace session {
32 namespace modules {
33 namespace tex {
34 namespace view_pdf {
35 
36 namespace {
37 
handleViewPdf(const http::Request & request,http::Response * pResponse)38 void handleViewPdf(const http::Request& request, http::Response* pResponse)
39 {
40    // get the file path
41    FilePath filePath(request.queryParamValue("path"));
42    if (!filePath.exists() || !module_context::isPathViewAllowed(filePath))
43    {
44       pResponse->setNotFoundError(request);
45       return;
46    }
47 
48    // send it back
49    pResponse->setNoCacheHeaders();
50    pResponse->setFile(filePath, request);
51    pResponse->setContentType("application/pdf");
52 }
53 
handlePdfJs(const http::Request & request,http::Response * pResponse)54 void handlePdfJs(const http::Request& request, http::Response* pResponse)
55 {
56    std::string path("pdfjs/");
57    path.append(http::util::pathAfterPrefix(request, kPdfJsPath));
58    if (request.queryString().find("file=") != std::string::npos &&
59        request.queryString() != "file=")
60    {
61       // when a file is specified, we expect it to be empty; deny requests for
62       // specific files (we don't want arbitrary URLs to be loaded)
63       pResponse->setError(
64                http::status::BadRequest,
65                "Incorrect parameters");
66       return;
67    }
68 
69    core::FilePath pdfJsResource = options().rResourcesPath().completeChildPath(path);
70    if (pdfJsResource.exists())
71    {
72       pResponse->setCacheableFile(pdfJsResource, request);
73       return;
74    }
75 }
76 
77 } // anonymous namespace
78 
createViewPdfUrl(const core::FilePath & filePath)79 std::string createViewPdfUrl(const core::FilePath& filePath)
80 {
81    return "view_pdf?path=" + http::util::urlEncode(
82       filePath.getAbsolutePath(),
83                                                    true);
84 }
85 
initialize()86 Error initialize()
87 {
88    // install rpc methods
89    using boost::bind;
90    using namespace module_context;
91    ExecBlock initBlock;
92    initBlock.addFunctions()
93       (bind(registerUriHandler, "/view_pdf", handleViewPdf))
94       (bind(registerUriHandler, kPdfJsPath, handlePdfJs))
95    ;
96    return initBlock.execute();
97 }
98 
99 } // namespace view_pdf
100 } // namespace tex
101 } // namespace modules
102 } // namespace session
103 } // namespace rstudio
104 
105