1 // Copyright 2016 PDFium Authors. All rights reserved.
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 "core/fpdfapi/parser/cpdf_dictionary.h"
10 #include "core/fpdfapi/parser/cpdf_name.h"
11 #include "core/fpdfapi/parser/cpdf_object.h"
12 #include "core/fpdfapi/parser/cpdf_string.h"
13 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
14 #include "core/fxcrt/fx_system.h"
15 
16 namespace {
17 
18 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ || \
19     _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
ChangeSlashToPlatform(const FX_WCHAR * str)20 CFX_WideString ChangeSlashToPlatform(const FX_WCHAR* str) {
21   CFX_WideString result;
22   while (*str) {
23     if (*str == '/') {
24 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
25       result += ':';
26 #else
27       result += '\\';
28 #endif
29     } else {
30       result += *str;
31     }
32     str++;
33   }
34   return result;
35 }
36 
ChangeSlashToPDF(const FX_WCHAR * str)37 CFX_WideString ChangeSlashToPDF(const FX_WCHAR* str) {
38   CFX_WideString result;
39   while (*str) {
40     if (*str == '\\' || *str == ':')
41       result += '/';
42     else
43       result += *str;
44 
45     str++;
46   }
47   return result;
48 }
49 #endif  // _FXM_PLATFORM_APPLE_ || _FXM_PLATFORM_WINDOWS_
50 
51 }  // namespace
52 
DecodeFileName(const CFX_WideStringC & filepath)53 CFX_WideString CPDF_FileSpec::DecodeFileName(const CFX_WideStringC& filepath) {
54   if (filepath.GetLength() <= 1)
55     return CFX_WideString();
56 
57 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
58   if (filepath.Left(sizeof("/Mac") - 1) == CFX_WideStringC(L"/Mac"))
59     return ChangeSlashToPlatform(filepath.c_str() + 1);
60   return ChangeSlashToPlatform(filepath.c_str());
61 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
62 
63   if (filepath.GetAt(0) != '/')
64     return ChangeSlashToPlatform(filepath.c_str());
65   if (filepath.GetAt(1) == '/')
66     return ChangeSlashToPlatform(filepath.c_str() + 1);
67   if (filepath.GetAt(2) == '/') {
68     CFX_WideString result;
69     result += filepath.GetAt(1);
70     result += ':';
71     result += ChangeSlashToPlatform(filepath.c_str() + 2);
72     return result;
73   }
74   CFX_WideString result;
75   result += '\\';
76   result += ChangeSlashToPlatform(filepath.c_str());
77   return result;
78 #else
79   return CFX_WideString(filepath);
80 #endif
81 }
82 
GetFileName(CFX_WideString * csFileName) const83 bool CPDF_FileSpec::GetFileName(CFX_WideString* csFileName) const {
84   if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
85     *csFileName = pDict->GetUnicodeTextFor("UF");
86     if (csFileName->IsEmpty()) {
87       *csFileName =
88           CFX_WideString::FromLocal(pDict->GetStringFor("F").AsStringC());
89     }
90     if (pDict->GetStringFor("FS") == "URL")
91       return true;
92     if (csFileName->IsEmpty()) {
93       if (pDict->KeyExist("DOS")) {
94         *csFileName =
95             CFX_WideString::FromLocal(pDict->GetStringFor("DOS").AsStringC());
96       } else if (pDict->KeyExist("Mac")) {
97         *csFileName =
98             CFX_WideString::FromLocal(pDict->GetStringFor("Mac").AsStringC());
99       } else if (pDict->KeyExist("Unix")) {
100         *csFileName =
101             CFX_WideString::FromLocal(pDict->GetStringFor("Unix").AsStringC());
102       } else {
103         return false;
104       }
105     }
106   } else if (m_pObj->IsString()) {
107     *csFileName = CFX_WideString::FromLocal(m_pObj->GetString().AsStringC());
108   } else {
109     return false;
110   }
111   *csFileName = DecodeFileName(csFileName->AsStringC());
112   return true;
113 }
114 
CPDF_FileSpec(const CFX_WeakPtr<CFX_ByteStringPool> & pPool)115 CPDF_FileSpec::CPDF_FileSpec(const CFX_WeakPtr<CFX_ByteStringPool>& pPool) {
116   m_pObj = new CPDF_Dictionary(pPool);
117   m_pObj->AsDictionary()->SetNewFor<CPDF_Name>("Type", "Filespec");
118 }
119 
EncodeFileName(const CFX_WideStringC & filepath)120 CFX_WideString CPDF_FileSpec::EncodeFileName(const CFX_WideStringC& filepath) {
121   if (filepath.GetLength() <= 1)
122     return CFX_WideString();
123 
124 #if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
125   if (filepath.GetAt(1) == ':') {
126     CFX_WideString result;
127     result = '/';
128     result += filepath.GetAt(0);
129     if (filepath.GetAt(2) != '\\')
130       result += '/';
131 
132     result += ChangeSlashToPDF(filepath.c_str() + 2);
133     return result;
134   }
135   if (filepath.GetAt(0) == '\\' && filepath.GetAt(1) == '\\')
136     return ChangeSlashToPDF(filepath.c_str() + 1);
137 
138   if (filepath.GetAt(0) == '\\') {
139     CFX_WideString result;
140     result = '/';
141     result += ChangeSlashToPDF(filepath.c_str());
142     return result;
143   }
144   return ChangeSlashToPDF(filepath.c_str());
145 #elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
146   if (filepath.Left(sizeof("Mac") - 1) == L"Mac") {
147     CFX_WideString result;
148     result = '/';
149     result += ChangeSlashToPDF(filepath.c_str());
150     return result;
151   }
152   return ChangeSlashToPDF(filepath.c_str());
153 #else
154   return CFX_WideString(filepath);
155 #endif
156 }
157 
SetFileName(const CFX_WideStringC & wsFileName)158 void CPDF_FileSpec::SetFileName(const CFX_WideStringC& wsFileName) {
159   if (!m_pObj)
160     return;
161 
162   CFX_WideString wsStr = EncodeFileName(wsFileName);
163   if (m_pObj->IsString()) {
164     m_pObj->SetString(CFX_ByteString::FromUnicode(wsStr));
165   } else if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
166     pDict->SetNewFor<CPDF_String>("F", CFX_ByteString::FromUnicode(wsStr),
167                                   false);
168     pDict->SetNewFor<CPDF_String>("UF", PDF_EncodeText(wsStr), false);
169   }
170 }
171