1 // Copyright 2014 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 "fpdfsdk/javascript/util.h"
8
9 #include <time.h>
10
11 #include <algorithm>
12 #include <string>
13 #include <vector>
14
15 #include "core/fxcrt/fx_ext.h"
16 #include "fpdfsdk/javascript/JS_Define.h"
17 #include "fpdfsdk/javascript/JS_EventHandler.h"
18 #include "fpdfsdk/javascript/JS_Object.h"
19 #include "fpdfsdk/javascript/JS_Value.h"
20 #include "fpdfsdk/javascript/PublicMethods.h"
21 #include "fpdfsdk/javascript/cjs_event_context.h"
22 #include "fpdfsdk/javascript/cjs_runtime.h"
23 #include "fpdfsdk/javascript/resource.h"
24
25 #if _FX_OS_ == _FX_ANDROID_
26 #include <ctype.h>
27 #endif
28
29 JSConstSpec CJS_Util::ConstSpecs[] = {{0, JSConstSpec::Number, 0, 0}};
30
31 JSPropertySpec CJS_Util::PropertySpecs[] = {{0, 0, 0}};
32
33 JSMethodSpec CJS_Util::MethodSpecs[] = {
34 {"printd", printd_static}, {"printf", printf_static},
35 {"printx", printx_static}, {"scand", scand_static},
36 {"byteToChar", byteToChar_static}, {0, 0}};
37
38 IMPLEMENT_JS_CLASS(CJS_Util, util)
39
40 #define UTIL_INT 0
41 #define UTIL_DOUBLE 1
42 #define UTIL_STRING 2
43
44 namespace {
45
46 // Map PDF-style directives to equivalent wcsftime directives. Not
47 // all have direct equivalents, though.
48 struct TbConvert {
49 const FX_WCHAR* lpszJSMark;
50 const FX_WCHAR* lpszCppMark;
51 };
52
53 // Map PDF-style directives lacking direct wcsftime directives to
54 // the value with which they will be replaced.
55 struct TbConvertAdditional {
56 const FX_WCHAR* lpszJSMark;
57 int iValue;
58 };
59
60 const TbConvert TbConvertTable[] = {
61 {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"},
62 {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"},
63 {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"},
64 {L"TT", L"%p"},
65 #if defined(_WIN32)
66 {L"tt", L"%p"}, {L"h", L"%#I"},
67 #else
68 {L"tt", L"%P"}, {L"h", L"%l"},
69 #endif
70 };
71
ParseDataType(std::wstring * sFormat)72 int ParseDataType(std::wstring* sFormat) {
73 bool bPercent = false;
74 for (size_t i = 0; i < sFormat->length(); ++i) {
75 wchar_t c = (*sFormat)[i];
76 if (c == L'%') {
77 bPercent = true;
78 continue;
79 }
80
81 if (bPercent) {
82 if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
83 c == L'u' || c == L'x' || c == L'X') {
84 return UTIL_INT;
85 }
86 if (c == L'e' || c == L'E' || c == L'f' || c == L'g' || c == L'G') {
87 return UTIL_DOUBLE;
88 }
89 if (c == L's' || c == L'S') {
90 // Map s to S since we always deal internally
91 // with wchar_t strings.
92 (*sFormat)[i] = L'S';
93 return UTIL_STRING;
94 }
95 if (c == L'.' || c == L'+' || c == L'-' || c == L'#' || c == L' ' ||
96 FXSYS_iswdigit(c)) {
97 continue;
98 }
99 break;
100 }
101 }
102
103 return -1;
104 }
105
106 } // namespace
107
util(CJS_Object * pJSObject)108 util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
109
~util()110 util::~util() {}
111
printf(CJS_Runtime * pRuntime,const std::vector<CJS_Value> & params,CJS_Value & vRet,CFX_WideString & sError)112 bool util::printf(CJS_Runtime* pRuntime,
113 const std::vector<CJS_Value>& params,
114 CJS_Value& vRet,
115 CFX_WideString& sError) {
116 int iSize = params.size();
117 if (iSize < 1)
118 return false;
119 std::wstring c_ConvChar(params[0].ToCFXWideString(pRuntime).c_str());
120 std::vector<std::wstring> c_strConvers;
121 int iOffset = 0;
122 int iOffend = 0;
123 c_ConvChar.insert(c_ConvChar.begin(), L'S');
124 while (iOffset != -1) {
125 iOffend = c_ConvChar.find(L"%", iOffset + 1);
126 std::wstring strSub;
127 if (iOffend == -1)
128 strSub = c_ConvChar.substr(iOffset);
129 else
130 strSub = c_ConvChar.substr(iOffset, iOffend - iOffset);
131 c_strConvers.push_back(strSub);
132 iOffset = iOffend;
133 }
134
135 std::wstring c_strResult;
136 std::wstring c_strFormat;
137 for (int iIndex = 0; iIndex < (int)c_strConvers.size(); iIndex++) {
138 c_strFormat = c_strConvers[iIndex];
139 if (iIndex == 0) {
140 c_strResult = c_strFormat;
141 continue;
142 }
143
144 CFX_WideString strSegment;
145 if (iIndex >= iSize) {
146 c_strResult += c_strFormat;
147 continue;
148 }
149
150 switch (ParseDataType(&c_strFormat)) {
151 case UTIL_INT:
152 strSegment.Format(c_strFormat.c_str(), params[iIndex].ToInt(pRuntime));
153 break;
154 case UTIL_DOUBLE:
155 strSegment.Format(c_strFormat.c_str(),
156 params[iIndex].ToDouble(pRuntime));
157 break;
158 case UTIL_STRING:
159 strSegment.Format(c_strFormat.c_str(),
160 params[iIndex].ToCFXWideString(pRuntime).c_str());
161 break;
162 default:
163 strSegment.Format(L"%S", c_strFormat.c_str());
164 break;
165 }
166 c_strResult += strSegment.GetBuffer(strSegment.GetLength() + 1);
167 }
168
169 c_strResult.erase(c_strResult.begin());
170 vRet = CJS_Value(pRuntime, c_strResult.c_str());
171 return true;
172 }
173
printd(CJS_Runtime * pRuntime,const std::vector<CJS_Value> & params,CJS_Value & vRet,CFX_WideString & sError)174 bool util::printd(CJS_Runtime* pRuntime,
175 const std::vector<CJS_Value>& params,
176 CJS_Value& vRet,
177 CFX_WideString& sError) {
178 int iSize = params.size();
179 if (iSize < 2)
180 return false;
181
182 CJS_Value p1 = params[0];
183 CJS_Value p2 = params[1];
184 CJS_Date jsDate;
185 if (!p2.ConvertToDate(pRuntime, jsDate)) {
186 sError = JSGetStringFromID(IDS_STRING_JSPRINT1);
187 return false;
188 }
189
190 if (!jsDate.IsValidDate(pRuntime)) {
191 sError = JSGetStringFromID(IDS_STRING_JSPRINT2);
192 return false;
193 }
194
195 if (p1.GetType() == CJS_Value::VT_number) {
196 CFX_WideString swResult;
197 switch (p1.ToInt(pRuntime)) {
198 case 0:
199 swResult.Format(L"D:%04d%02d%02d%02d%02d%02d", jsDate.GetYear(pRuntime),
200 jsDate.GetMonth(pRuntime) + 1, jsDate.GetDay(pRuntime),
201 jsDate.GetHours(pRuntime), jsDate.GetMinutes(pRuntime),
202 jsDate.GetSeconds(pRuntime));
203 break;
204 case 1:
205 swResult.Format(L"%04d.%02d.%02d %02d:%02d:%02d",
206 jsDate.GetYear(pRuntime), jsDate.GetMonth(pRuntime) + 1,
207 jsDate.GetDay(pRuntime), jsDate.GetHours(pRuntime),
208 jsDate.GetMinutes(pRuntime),
209 jsDate.GetSeconds(pRuntime));
210 break;
211 case 2:
212 swResult.Format(L"%04d/%02d/%02d %02d:%02d:%02d",
213 jsDate.GetYear(pRuntime), jsDate.GetMonth(pRuntime) + 1,
214 jsDate.GetDay(pRuntime), jsDate.GetHours(pRuntime),
215 jsDate.GetMinutes(pRuntime),
216 jsDate.GetSeconds(pRuntime));
217 break;
218 default:
219 sError = JSGetStringFromID(IDS_STRING_JSVALUEERROR);
220 return false;
221 }
222
223 vRet = CJS_Value(pRuntime, swResult.c_str());
224 return true;
225 }
226
227 if (p1.GetType() == CJS_Value::VT_string) {
228 if (iSize > 2 && params[2].ToBool(pRuntime)) {
229 sError = JSGetStringFromID(IDS_STRING_JSNOTSUPPORT);
230 return false; // currently, it doesn't support XFAPicture.
231 }
232
233 // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
234 // pre-existing %-directives before inserting our own.
235 std::basic_string<wchar_t> cFormat = p1.ToCFXWideString(pRuntime).c_str();
236 cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
237 cFormat.end());
238
239 for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) {
240 int iStart = 0;
241 int iEnd;
242 while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) !=
243 -1) {
244 cFormat.replace(iEnd, FXSYS_wcslen(TbConvertTable[i].lpszJSMark),
245 TbConvertTable[i].lpszCppMark);
246 iStart = iEnd;
247 }
248 }
249
250 int iYear = jsDate.GetYear(pRuntime);
251 int iMonth = jsDate.GetMonth(pRuntime);
252 int iDay = jsDate.GetDay(pRuntime);
253 int iHour = jsDate.GetHours(pRuntime);
254 int iMin = jsDate.GetMinutes(pRuntime);
255 int iSec = jsDate.GetSeconds(pRuntime);
256
257 TbConvertAdditional cTableAd[] = {
258 {L"m", iMonth + 1}, {L"d", iDay},
259 {L"H", iHour}, {L"h", iHour > 12 ? iHour - 12 : iHour},
260 {L"M", iMin}, {L"s", iSec},
261 };
262
263 for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) {
264 wchar_t tszValue[16];
265 CFX_WideString sValue;
266 sValue.Format(L"%d", cTableAd[i].iValue);
267 memcpy(tszValue, (wchar_t*)sValue.GetBuffer(sValue.GetLength() + 1),
268 (sValue.GetLength() + 1) * sizeof(wchar_t));
269
270 int iStart = 0;
271 int iEnd;
272 while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) {
273 if (iEnd > 0) {
274 if (cFormat[iEnd - 1] == L'%') {
275 iStart = iEnd + 1;
276 continue;
277 }
278 }
279 cFormat.replace(iEnd, FXSYS_wcslen(cTableAd[i].lpszJSMark), tszValue);
280 iStart = iEnd;
281 }
282 }
283
284 struct tm time = {};
285 time.tm_year = iYear - 1900;
286 time.tm_mon = iMonth;
287 time.tm_mday = iDay;
288 time.tm_hour = iHour;
289 time.tm_min = iMin;
290 time.tm_sec = iSec;
291
292 wchar_t buf[64] = {};
293 wcsftime(buf, 64, cFormat.c_str(), &time);
294 cFormat = buf;
295 vRet = CJS_Value(pRuntime, cFormat.c_str());
296 return true;
297 }
298
299 sError = JSGetStringFromID(IDS_STRING_JSTYPEERROR);
300 return false;
301 }
302
printx(CJS_Runtime * pRuntime,const std::vector<CJS_Value> & params,CJS_Value & vRet,CFX_WideString & sError)303 bool util::printx(CJS_Runtime* pRuntime,
304 const std::vector<CJS_Value>& params,
305 CJS_Value& vRet,
306 CFX_WideString& sError) {
307 if (params.size() < 2) {
308 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
309 return false;
310 }
311
312 vRet = CJS_Value(pRuntime, printx(params[0].ToCFXWideString(pRuntime),
313 params[1].ToCFXWideString(pRuntime))
314 .c_str());
315
316 return true;
317 }
318
319 enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
320
TranslateCase(FX_WCHAR input,CaseMode eMode)321 static FX_WCHAR TranslateCase(FX_WCHAR input, CaseMode eMode) {
322 if (eMode == kLowerCase && input >= 'A' && input <= 'Z')
323 return input | 0x20;
324 if (eMode == kUpperCase && input >= 'a' && input <= 'z')
325 return input & ~0x20;
326 return input;
327 }
328
printx(const CFX_WideString & wsFormat,const CFX_WideString & wsSource)329 CFX_WideString util::printx(const CFX_WideString& wsFormat,
330 const CFX_WideString& wsSource) {
331 CFX_WideString wsResult;
332 FX_STRSIZE iSourceIdx = 0;
333 FX_STRSIZE iFormatIdx = 0;
334 CaseMode eCaseMode = kPreserveCase;
335 bool bEscaped = false;
336 while (iFormatIdx < wsFormat.GetLength()) {
337 if (bEscaped) {
338 bEscaped = false;
339 wsResult += wsFormat[iFormatIdx];
340 ++iFormatIdx;
341 continue;
342 }
343 switch (wsFormat[iFormatIdx]) {
344 case '\\': {
345 bEscaped = true;
346 ++iFormatIdx;
347 } break;
348 case '<': {
349 eCaseMode = kLowerCase;
350 ++iFormatIdx;
351 } break;
352 case '>': {
353 eCaseMode = kUpperCase;
354 ++iFormatIdx;
355 } break;
356 case '=': {
357 eCaseMode = kPreserveCase;
358 ++iFormatIdx;
359 } break;
360 case '?': {
361 if (iSourceIdx < wsSource.GetLength()) {
362 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
363 ++iSourceIdx;
364 }
365 ++iFormatIdx;
366 } break;
367 case 'X': {
368 if (iSourceIdx < wsSource.GetLength()) {
369 if ((wsSource[iSourceIdx] >= '0' && wsSource[iSourceIdx] <= '9') ||
370 (wsSource[iSourceIdx] >= 'a' && wsSource[iSourceIdx] <= 'z') ||
371 (wsSource[iSourceIdx] >= 'A' && wsSource[iSourceIdx] <= 'Z')) {
372 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
373 ++iFormatIdx;
374 }
375 ++iSourceIdx;
376 } else {
377 ++iFormatIdx;
378 }
379 } break;
380 case 'A': {
381 if (iSourceIdx < wsSource.GetLength()) {
382 if ((wsSource[iSourceIdx] >= 'a' && wsSource[iSourceIdx] <= 'z') ||
383 (wsSource[iSourceIdx] >= 'A' && wsSource[iSourceIdx] <= 'Z')) {
384 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
385 ++iFormatIdx;
386 }
387 ++iSourceIdx;
388 } else {
389 ++iFormatIdx;
390 }
391 } break;
392 case '9': {
393 if (iSourceIdx < wsSource.GetLength()) {
394 if (wsSource[iSourceIdx] >= '0' && wsSource[iSourceIdx] <= '9') {
395 wsResult += wsSource[iSourceIdx];
396 ++iFormatIdx;
397 }
398 ++iSourceIdx;
399 } else {
400 ++iFormatIdx;
401 }
402 } break;
403 case '*': {
404 if (iSourceIdx < wsSource.GetLength()) {
405 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
406 ++iSourceIdx;
407 } else {
408 ++iFormatIdx;
409 }
410 } break;
411 default: {
412 wsResult += wsFormat[iFormatIdx];
413 ++iFormatIdx;
414 } break;
415 }
416 }
417 return wsResult;
418 }
419
scand(CJS_Runtime * pRuntime,const std::vector<CJS_Value> & params,CJS_Value & vRet,CFX_WideString & sError)420 bool util::scand(CJS_Runtime* pRuntime,
421 const std::vector<CJS_Value>& params,
422 CJS_Value& vRet,
423 CFX_WideString& sError) {
424 int iSize = params.size();
425 if (iSize < 2)
426 return false;
427
428 CFX_WideString sFormat = params[0].ToCFXWideString(pRuntime);
429 CFX_WideString sDate = params[1].ToCFXWideString(pRuntime);
430 double dDate = JS_GetDateTime();
431 if (sDate.GetLength() > 0) {
432 dDate = CJS_PublicMethods::MakeRegularDate(sDate, sFormat, nullptr);
433 }
434
435 if (!JS_PortIsNan(dDate)) {
436 vRet = CJS_Value(pRuntime, CJS_Date(pRuntime, dDate));
437 } else {
438 vRet.SetNull(pRuntime);
439 }
440
441 return true;
442 }
443
byteToChar(CJS_Runtime * pRuntime,const std::vector<CJS_Value> & params,CJS_Value & vRet,CFX_WideString & sError)444 bool util::byteToChar(CJS_Runtime* pRuntime,
445 const std::vector<CJS_Value>& params,
446 CJS_Value& vRet,
447 CFX_WideString& sError) {
448 if (params.size() < 1) {
449 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
450 return false;
451 }
452
453 int arg = params[0].ToInt(pRuntime);
454 if (arg < 0 || arg > 255) {
455 sError = JSGetStringFromID(IDS_STRING_JSVALUEERROR);
456 return false;
457 }
458
459 CFX_WideString wStr(static_cast<FX_WCHAR>(arg));
460 vRet = CJS_Value(pRuntime, wStr.c_str());
461 return true;
462 }
463