• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "core/fpdfdoc/cpdf_filespec.h"
8 
9 #include <iterator>
10 #include <utility>
11 
12 #include "build/build_config.h"
13 #include "constants/stream_dict_common.h"
14 #include "core/fpdfapi/parser/cpdf_dictionary.h"
15 #include "core/fpdfapi/parser/cpdf_name.h"
16 #include "core/fpdfapi/parser/cpdf_object.h"
17 #include "core/fpdfapi/parser/cpdf_stream.h"
18 #include "core/fpdfapi/parser/cpdf_string.h"
19 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
20 #include "core/fxcrt/fx_system.h"
21 #include "third_party/base/check.h"
22 #include "third_party/base/notreached.h"
23 
24 namespace {
25 
26 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
ChangeSlashToPlatform(const wchar_t * str)27 WideString ChangeSlashToPlatform(const wchar_t* str) {
28   WideString result;
29   while (*str) {
30     if (*str == '/') {
31 #if BUILDFLAG(IS_APPLE)
32       result += L':';
33 #else
34       result += L'\\';
35 #endif
36     } else {
37       result += *str;
38     }
39     str++;
40   }
41   return result;
42 }
43 
ChangeSlashToPDF(const wchar_t * str)44 WideString ChangeSlashToPDF(const wchar_t* str) {
45   WideString result;
46   while (*str) {
47     if (*str == '\\' || *str == ':')
48       result += L'/';
49     else
50       result += *str;
51 
52     str++;
53   }
54   return result;
55 }
56 #endif  // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
57 
58 }  // namespace
59 
CPDF_FileSpec(RetainPtr<const CPDF_Object> pObj)60 CPDF_FileSpec::CPDF_FileSpec(RetainPtr<const CPDF_Object> pObj)
61     : m_pObj(std::move(pObj)) {
62   DCHECK(m_pObj);
63 }
64 
65 CPDF_FileSpec::~CPDF_FileSpec() = default;
66 
DecodeFileName(const WideString & filepath)67 WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) {
68   if (filepath.GetLength() <= 1)
69     return WideString();
70 
71 #if BUILDFLAG(IS_APPLE)
72   if (filepath.First(sizeof("/Mac") - 1) == WideStringView(L"/Mac"))
73     return ChangeSlashToPlatform(filepath.c_str() + 1);
74   return ChangeSlashToPlatform(filepath.c_str());
75 #elif BUILDFLAG(IS_WIN)
76 
77   if (filepath[0] != L'/')
78     return ChangeSlashToPlatform(filepath.c_str());
79   if (filepath[1] == L'/')
80     return ChangeSlashToPlatform(filepath.c_str() + 1);
81   if (filepath[2] == L'/') {
82     WideString result;
83     result += filepath[1];
84     result += L':';
85     result += ChangeSlashToPlatform(filepath.c_str() + 2);
86     return result;
87   }
88   WideString result;
89   result += L'\\';
90   result += ChangeSlashToPlatform(filepath.c_str());
91   return result;
92 #else
93   return WideString(filepath);
94 #endif
95 }
96 
GetFileName() const97 WideString CPDF_FileSpec::GetFileName() const {
98   WideString csFileName;
99   if (const CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
100     RetainPtr<const CPDF_String> pUF =
101         ToString(pDict->GetDirectObjectFor("UF"));
102     if (pUF)
103       csFileName = pUF->GetUnicodeText();
104     if (csFileName.IsEmpty()) {
105       RetainPtr<const CPDF_String> pK =
106           ToString(pDict->GetDirectObjectFor(pdfium::stream::kF));
107       if (pK)
108         csFileName = WideString::FromDefANSI(pK->GetString().AsStringView());
109     }
110     if (pDict->GetByteStringFor("FS") == "URL")
111       return csFileName;
112 
113     if (csFileName.IsEmpty()) {
114       for (const auto* key : {"DOS", "Mac", "Unix"}) {
115         RetainPtr<const CPDF_String> pValue =
116             ToString(pDict->GetDirectObjectFor(key));
117         if (pValue) {
118           csFileName =
119               WideString::FromDefANSI(pValue->GetString().AsStringView());
120           break;
121         }
122       }
123     }
124   } else if (const CPDF_String* pString = m_pObj->AsString()) {
125     csFileName = WideString::FromDefANSI(pString->GetString().AsStringView());
126   }
127   return DecodeFileName(csFileName);
128 }
129 
GetFileStream() const130 RetainPtr<const CPDF_Stream> CPDF_FileSpec::GetFileStream() const {
131   const CPDF_Dictionary* pDict = m_pObj->AsDictionary();
132   if (!pDict)
133     return nullptr;
134 
135   // Get the embedded files dictionary.
136   RetainPtr<const CPDF_Dictionary> pFiles = pDict->GetDictFor("EF");
137   if (!pFiles)
138     return nullptr;
139 
140   // List of keys to check for the file specification string.
141   // Follows the same precedence order as GetFileName().
142   static constexpr const char* kKeys[] = {"UF", "F", "DOS", "Mac", "Unix"};
143   size_t end = pDict->GetByteStringFor("FS") == "URL" ? 2 : std::size(kKeys);
144   for (size_t i = 0; i < end; ++i) {
145     ByteString key = kKeys[i];
146     if (!pDict->GetUnicodeTextFor(key).IsEmpty()) {
147       RetainPtr<const CPDF_Stream> pStream = pFiles->GetStreamFor(key);
148       if (pStream)
149         return pStream;
150     }
151   }
152   return nullptr;
153 }
154 
GetParamsDict() const155 RetainPtr<const CPDF_Dictionary> CPDF_FileSpec::GetParamsDict() const {
156   RetainPtr<const CPDF_Stream> pStream = GetFileStream();
157   if (!pStream)
158     return nullptr;
159 
160   RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict();
161   return pDict ? pDict->GetDictFor("Params") : nullptr;
162 }
163 
GetMutableParamsDict()164 RetainPtr<CPDF_Dictionary> CPDF_FileSpec::GetMutableParamsDict() {
165   return pdfium::WrapRetain(
166       const_cast<CPDF_Dictionary*>(GetParamsDict().Get()));
167 }
168 
EncodeFileName(const WideString & filepath)169 WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) {
170   if (filepath.GetLength() <= 1)
171     return WideString();
172 
173 #if BUILDFLAG(IS_WIN)
174   if (filepath[1] == L':') {
175     WideString result(L'/');
176     result += filepath[0];
177     if (filepath[2] != L'\\')
178       result += L'/';
179 
180     result += ChangeSlashToPDF(filepath.c_str() + 2);
181     return result;
182   }
183   if (filepath[0] == L'\\' && filepath[1] == L'\\')
184     return ChangeSlashToPDF(filepath.c_str() + 1);
185 
186   if (filepath[0] == L'\\')
187     return L'/' + ChangeSlashToPDF(filepath.c_str());
188   return ChangeSlashToPDF(filepath.c_str());
189 #elif BUILDFLAG(IS_APPLE)
190   if (filepath.First(sizeof("Mac") - 1).EqualsASCII("Mac"))
191     return L'/' + ChangeSlashToPDF(filepath.c_str());
192   return ChangeSlashToPDF(filepath.c_str());
193 #else
194   return WideString(filepath);
195 #endif
196 }
197