xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Base64Handler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // Base64Handler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/MyBuffer.h"
9 #include "../../Common/IntToString.h"
10 #include "../../Common/MyVector.h"
11 
12 #include "../../Windows/PropVariant.h"
13 
14 #include "../Common/ProgressUtils.h"
15 #include "../Common/RegisterArc.h"
16 #include "../Common/StreamUtils.h"
17 #include "../Common/InBuffer.h"
18 
19 /*
20 spaces:
21   9(TAB),10(LF),13(CR),32(SPACE)
22   Non-breaking space:
23     0xa0 : Unicode, Windows code pages 1250-1258
24     0xff (unused): DOS code pages
25 
26 end of stream markers: '=' (0x3d):
27   "="  , if numBytes (% 3 == 2)
28   "==" , if numBytes (% 3 == 1)
29 */
30 
31 
32 static const Byte k_Base64Table[256] =
33 {
34   66,77,77,77,77,77,77,77,77,65,65,77,77,65,77,77,
35   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
36   65,77,77,77,77,77,77,77,77,77,77,62,77,77,77,63,
37   52,53,54,55,56,57,58,59,60,61,77,77,77,64,77,77,
38   77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
39   15,16,17,18,19,20,21,22,23,24,25,77,77,77,77,77,
40   77,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41   41,42,43,44,45,46,47,48,49,50,51,77,77,77,77,77,
42   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
43   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
44   65,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
45   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
46   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
47   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
48   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
49   77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77
50 };
51 
52 static const unsigned k_Code_Equals = 64;
53 static const unsigned k_Code_Space = 65;
54 static const unsigned k_Code_Zero = 66;
55 
IsArc_Base64(const Byte * p,size_t size)56 API_FUNC_static_IsArc IsArc_Base64(const Byte *p, size_t size)
57 {
58   size_t num = 0;
59   size_t firstSpace = 0;
60 
61   for (;;)
62   {
63     if (size == 0)
64       return k_IsArc_Res_NEED_MORE;
65     UInt32 c = k_Base64Table[(Byte)(*p++)];
66     size--;
67     if (c < 64)
68     {
69       num++;
70       continue;
71     }
72 
73     if (c == k_Code_Space)
74     {
75       if (p[-1] == ' ' && firstSpace == 0)
76         firstSpace = num;
77       continue;
78     }
79 
80     if (c != k_Code_Equals)
81       return k_IsArc_Res_NO;
82     break;
83   }
84 
85   {
86     // we try to redece false positive detection here.
87     // we don't expect space character in starting base64 line
88     const unsigned kNumExpectedNonSpaceSyms = 20;
89     if (firstSpace != 0 && firstSpace < num && firstSpace < kNumExpectedNonSpaceSyms)
90       return k_IsArc_Res_NO;
91   }
92 
93   num &= 3;
94 
95   if (num <= 1)
96     return k_IsArc_Res_NO;
97   if (num != 3)
98   {
99     if (size == 0)
100       return k_IsArc_Res_NEED_MORE;
101     UInt32 c = k_Base64Table[(Byte)(*p++)];
102     size--;
103     if (c != k_Code_Equals)
104       return k_IsArc_Res_NO;
105   }
106 
107   for (;;)
108   {
109     if (size == 0)
110       return k_IsArc_Res_YES;
111     UInt32 c = k_Base64Table[(Byte)(*p++)];
112     size--;
113     if (c == k_Code_Space)
114       continue;
115     return k_IsArc_Res_NO;
116   }
117 }
118 }
119 
120 
121 enum EBase64Res
122 {
123   k_Base64_RES_MaybeFinished,
124   k_Base64_RES_Finished,
125   k_Base64_RES_NeedMoreInput,
126   k_Base64_RES_UnexpectedChar
127 };
128 
129 
130 static EBase64Res Base64ToBin(Byte *p, size_t size, const Byte **srcEnd, Byte **destEnd)
131 {
132   Byte *dest = p;
133   UInt32 val = 1;
134   EBase64Res res = k_Base64_RES_NeedMoreInput;
135 
136   for (;;)
137   {
138     if (size == 0)
139     {
140       if (val == 1)
141         res = k_Base64_RES_MaybeFinished;
142       break;
143     }
144     UInt32 c = k_Base64Table[(Byte)(*p++)];
145     size--;
146     if (c < 64)
147     {
148       val = (val << 6) | c;
149       if ((val & ((UInt32)1 << 24)) == 0)
150         continue;
151       dest[0] = (Byte)(val >> 16);
152       dest[1] = (Byte)(val >> 8);
153       dest[2] = (Byte)(val);
154       dest += 3;
155       val = 1;
156       continue;
157     }
158 
159     if (c == k_Code_Space)
160       continue;
161 
162     if (c == k_Code_Equals)
163     {
164       if (val >= (1 << 12))
165       {
166         if (val & (1 << 18))
167         {
168           res = k_Base64_RES_Finished;
169           break;
170         }
171         if (size == 0)
172           break;
173         c = k_Base64Table[(Byte)(*p++)];
174         size--;
175         if (c == k_Code_Equals)
176         {
177           res = k_Base64_RES_Finished;
178           break;
179         }
180       }
181     }
182 
183     p--;
184     res = k_Base64_RES_UnexpectedChar;
185     break;
186   }
187 
188   if (val >= ((UInt32)1 << 12))
189   {
190     if (val & (1 << 18))
191     {
192       *dest++ = (Byte)(val >> 10);
193       val <<= 2;
194     }
195     *dest++ = (Byte)(val >> 4);
196   }
197 
198   *srcEnd = p;
199   *destEnd = dest;
200   return res;
201 }
202 
203 
204 static const Byte *Base64_SkipSpaces(const Byte *p, size_t size)
205 {
206   for (;;)
207   {
208     if (size == 0)
209       return p;
210     const UInt32 c = k_Base64Table[(Byte)(*p++)];
211     size--;
212     if (c == k_Code_Space)
213       continue;
214     return p - 1;
215   }
216 }
217 
218 
219 // the following function is used by DmgHandler.cpp
220 
221 Byte *Base64ToBin(Byte *dest, const char *src);
222 Byte *Base64ToBin(Byte *dest, const char *src)
223 {
224   UInt32 val = 1;
225 
226   for (;;)
227   {
228     const UInt32 c = k_Base64Table[(Byte)(*src++)];
229 
230     if (c < 64)
231     {
232       val = (val << 6) | c;
233       if ((val & ((UInt32)1 << 24)) == 0)
234         continue;
235       dest[0] = (Byte)(val >> 16);
236       dest[1] = (Byte)(val >> 8);
237       dest[2] = (Byte)(val);
238       dest += 3;
239       val = 1;
240       continue;
241     }
242 
243     if (c == k_Code_Space)
244       continue;
245 
246     if (c == k_Code_Equals)
247       break;
248 
249     if (c == k_Code_Zero && val == 1) // end of string
250       return dest;
251 
252     return NULL;
253   }
254 
255   if (val < (1 << 12))
256     return NULL;
257 
258   if (val & (1 << 18))
259   {
260     *dest++ = (Byte)(val >> 10);
261     val <<= 2;
262   }
263   else if (k_Base64Table[(Byte)(*src++)] != k_Code_Equals)
264     return NULL;
265   *dest++ = (Byte)(val >> 4);
266 
267   for (;;)
268   {
269     const Byte c = k_Base64Table[(Byte)(*src++)];
270     if (c == k_Code_Space)
271       continue;
272     if (c == k_Code_Zero)
273       return dest;
274     return NULL;
275   }
276 }
277 
278 
279 namespace NArchive {
280 namespace NBase64 {
281 
282 Z7_CLASS_IMP_CHandler_IInArchive_0
283 
284   bool _isArc;
285   UInt64 _phySize;
286   size_t _size;
287   EBase64Res _sres;
288   CByteBuffer _data;
289 };
290 
291 static const Byte kProps[] =
292 {
293   kpidSize,
294   kpidPackSize,
295 };
296 
297 IMP_IInArchive_Props
298 IMP_IInArchive_ArcProps_NO_Table
299 
300 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
301 {
302   *numItems = 1;
303   return S_OK;
304 }
305 
306 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
307 {
308   NWindows::NCOM::CPropVariant prop;
309   switch (propID)
310   {
311     case kpidPhySize: if (_phySize != 0) prop = _phySize; break;
312     case kpidErrorFlags:
313     {
314       UInt32 v = 0;
315       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
316       if (_sres == k_Base64_RES_NeedMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
317       if (v != 0)
318         prop = v;
319       break;
320     }
321   }
322   prop.Detach(value);
323   return S_OK;
324 }
325 
326 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
327 {
328   // COM_TRY_BEGIN
329   NWindows::NCOM::CPropVariant prop;
330   switch (propID)
331   {
332     case kpidSize: prop = (UInt64)_size; break;
333     case kpidPackSize: prop = _phySize; break;
334   }
335   prop.Detach(value);
336   return S_OK;
337   // COM_TRY_END
338 }
339 
340 
341 static HRESULT ReadStream_OpenProgress(ISequentialInStream *stream, void *data, size_t size, IArchiveOpenCallback *openCallback) throw()
342 {
343   UInt64 bytes = 0;
344   while (size != 0)
345   {
346     const UInt32 kBlockSize = ((UInt32)1 << 24);
347     const UInt32 curSize = (size < kBlockSize) ? (UInt32)size : kBlockSize;
348     UInt32 processedSizeLoc;
349     RINOK(stream->Read(data, curSize, &processedSizeLoc))
350     if (processedSizeLoc == 0)
351       return E_FAIL;
352     data = (void *)((Byte *)data + processedSizeLoc);
353     size -= processedSizeLoc;
354     bytes += processedSizeLoc;
355     const UInt64 files = 1;
356     RINOK(openCallback->SetCompleted(&files, &bytes))
357   }
358   return S_OK;
359 }
360 
361 
362 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openCallback))
363 {
364   COM_TRY_BEGIN
365   {
366     Close();
367     {
368       const unsigned kStartSize = 1 << 12;
369       _data.Alloc(kStartSize);
370       size_t size = kStartSize;
371       RINOK(ReadStream(stream, _data, &size))
372       if (IsArc_Base64(_data, size) == k_IsArc_Res_NO)
373         return S_FALSE;
374     }
375     _isArc = true;
376 
377     UInt64 packSize64;
378     RINOK(InStream_GetSize_SeekToEnd(stream, packSize64))
379     if (packSize64 == 0)
380       return S_FALSE;
381     size_t curSize = 1 << 16;
382     if (curSize > packSize64)
383       curSize = (size_t)packSize64;
384     const unsigned kLogStep = 4;
385 
386     for (;;)
387     {
388       RINOK(InStream_SeekSet(stream, 0))
389 
390       _data.Alloc(curSize);
391       RINOK(ReadStream_OpenProgress(stream, _data, curSize, openCallback))
392 
393       const Byte *srcEnd;
394       Byte *dest;
395       _sres = Base64ToBin(_data, curSize, &srcEnd, &dest);
396       _size = (size_t)(dest - _data);
397       const size_t mainSize = (size_t)(srcEnd - _data);
398       _phySize = mainSize;
399       if (_sres == k_Base64_RES_UnexpectedChar)
400         break;
401       if (curSize != mainSize)
402       {
403         const Byte *end2 = Base64_SkipSpaces(srcEnd, curSize - mainSize);
404         if ((size_t)(end2 - _data) != curSize)
405           break;
406         _phySize = curSize;
407       }
408 
409       if (curSize == packSize64)
410         break;
411 
412       UInt64 curSize64 = packSize64;
413       if (curSize < (packSize64 >> kLogStep))
414         curSize64 = (UInt64)curSize << kLogStep;
415       curSize = (size_t)curSize64;
416       if (curSize != curSize64)
417         return E_OUTOFMEMORY;
418     }
419     if (_size == 0)
420       return S_FALSE;
421     return S_OK;
422   }
423   COM_TRY_END
424 }
425 
426 Z7_COM7F_IMF(CHandler::Close())
427 {
428   _phySize = 0;
429   _size = 0;
430   _isArc = false;
431   _sres = k_Base64_RES_MaybeFinished;
432   _data.Free();
433   return S_OK;
434 }
435 
436 
437 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
438     Int32 testMode, IArchiveExtractCallback *extractCallback))
439 {
440   COM_TRY_BEGIN
441   if (numItems == 0)
442     return S_OK;
443   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
444     return E_INVALIDARG;
445 
446   RINOK(extractCallback->SetTotal(_size))
447   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
448   lps->Init(extractCallback, false);
449   // RINOK(lps->SetCur())
450   Int32 opRes;
451   {
452     CMyComPtr<ISequentialOutStream> realOutStream;
453     const Int32 askMode = testMode ?
454         NExtract::NAskMode::kTest :
455         NExtract::NAskMode::kExtract;
456     RINOK(extractCallback->GetStream(0, &realOutStream, askMode))
457     if (!testMode && !realOutStream)
458       return S_OK;
459     RINOK(extractCallback->PrepareOperation(askMode))
460     if (realOutStream)
461     {
462       RINOK(WriteStream(realOutStream, (const Byte *)_data, _size))
463     }
464     opRes = NExtract::NOperationResult::kOK;
465     if (_sres != k_Base64_RES_Finished)
466     {
467       if (_sres == k_Base64_RES_NeedMoreInput)
468         opRes = NExtract::NOperationResult::kUnexpectedEnd;
469       else if (_sres == k_Base64_RES_UnexpectedChar)
470         opRes = NExtract::NOperationResult::kDataError;
471     }
472   }
473   RINOK(extractCallback->SetOperationResult(opRes))
474   lps->InSize = _phySize;
475   lps->OutSize = _size;
476   return lps->SetCur();
477   COM_TRY_END
478 }
479 
480 REGISTER_ARC_I_NO_SIG(
481   "Base64", "b64", NULL, 0xC5,
482   0,
483     NArcInfoFlags::kKeepName
484   | NArcInfoFlags::kStartOpen
485   | NArcInfoFlags::kByExtOnlyOpen,
486   IsArc_Base64)
487 
488 }}
489