• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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