1//
2// AutoDetectStream.cpp
3//
4// Library: Zip
5// Package: Zip
6// Module: AutoDetectStream
7//
8// Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Zip/AutoDetectStream.h"
16#include "Poco/Zip/ZipLocalFileHeader.h"
17#include "Poco/Zip/ZipArchiveInfo.h"
18#include "Poco/Zip/ZipDataInfo.h"
19#include "Poco/Zip/ZipFileInfo.h"
20#include "Poco/Exception.h"
21#include <cstring>
22
23
24namespace Poco {
25namespace Zip {
26
27
28AutoDetectStreamBuf::AutoDetectStreamBuf(std::istream& in, const std::string& pre, const std::string& post, bool reposition, Poco::UInt32 start, bool needsZip64):
29 Poco::BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::in),
30 _pIstr(&in),
31 _eofDetected(false),
32 _matchCnt(0),
33 _prefix(pre),
34 _postfix(post),
35 _reposition(reposition),
36 _start(start),
37 _needsZip64(needsZip64),
38 _length(0)
39{
40}
41
42
43AutoDetectStreamBuf::~AutoDetectStreamBuf()
44{
45}
46
47
48int AutoDetectStreamBuf::readFromDevice(char* buffer, std::streamsize length)
49{
50 poco_assert_dbg(length >= 8);
51 if (_pIstr == 0 || length == 0) return -1;
52
53 if (_reposition)
54 {
55 _pIstr->seekg(_start, std::ios_base::beg);
56 _reposition = false;
57 if (!_pIstr->good()) return -1;
58 }
59
60 if (!_prefix.empty())
61 {
62 std::streamsize n = (_prefix.size() > length) ? length : static_cast<std::streamsize>(_prefix.size());
63 std::memcpy(buffer, _prefix.data(), n);
64 _prefix.erase(0, n);
65 return static_cast<int>(n);
66 }
67
68 if (_eofDetected)
69 {
70 if (!_postfix.empty())
71 {
72 std::streamsize n = (_postfix.size() > length) ? length : static_cast<std::streamsize>(_postfix.size());
73 std::memcpy(buffer, _postfix.data(), n);
74 _postfix.erase(0, n);
75 return static_cast<int>(n);
76 }
77 else return -1;
78 }
79
80 if (!_pIstr->good()) return -1;
81
82 std::streamsize offset = 0;
83 static std::istream::int_type eof = std::istream::traits_type::eof();
84 while (_pIstr->good() && !_pIstr->eof() && (offset + 4) < length)
85 {
86 std::istream::int_type c = _pIstr->get();
87 if (c != eof)
88 {
89 if (_matchCnt < 3)
90 {
91 if (c == ZipDataInfo::HEADER[_matchCnt])
92 {
93 ++_matchCnt;
94 }
95 else
96 {
97 for (int i = 0; i < _matchCnt; i++)
98 {
99 buffer[offset++] = ZipDataInfo::HEADER[i];
100 }
101 if (c == ZipDataInfo::HEADER[0])
102 {
103 _matchCnt = 1;
104 }
105 else
106 {
107 _matchCnt = 0;
108 buffer[offset++] = static_cast<char>(c);
109 }
110 }
111 }
112 else if (_matchCnt == 3)
113 {
114 if (ZipDataInfo::HEADER[3] == c)
115 {
116 std::streamsize dataInfoSize = 0;
117 if (_needsZip64)
118 {
119 ZipDataInfo64 dataInfo(*_pIstr, true);
120 if (!_pIstr->good()) throw Poco::IOException("Failed to read data descriptor");
121
122 dataInfoSize = dataInfo.getFullHeaderSize();
123 if (dataInfo.getCompressedSize() == _length + offset)
124 {
125 _pIstr->seekg(-static_cast<int>(dataInfoSize), std::ios::cur);
126 if (!_pIstr->good()) throw Poco::IOException("Failed to seek on input stream");
127
128 _eofDetected = true;
129 _length += offset;
130
131 if (offset == 0 && !_postfix.empty())
132 {
133 offset = (_postfix.size() > length) ? length : static_cast<std::streamsize>(_postfix.size());
134 std::memcpy(buffer, _postfix.data(), offset);
135 _postfix.erase(0, offset);
136 }
137
138 return static_cast<int>(offset);
139 }
140 }
141 else
142 {
143 ZipDataInfo dataInfo(*_pIstr, true);
144 if (!_pIstr->good()) throw Poco::IOException("Failed to read data descriptor");
145
146 dataInfoSize = dataInfo.getFullHeaderSize();
147 if (dataInfo.getCompressedSize() == _length + offset)
148 {
149 _pIstr->seekg(-static_cast<int>(dataInfoSize), std::ios::cur);
150 if (!_pIstr->good()) throw Poco::IOException("Failed to seek on input stream");
151
152 _eofDetected = true;
153 _length += offset;
154
155 if (offset == 0 && !_postfix.empty())
156 {
157 offset = (_postfix.size() > length) ? length : static_cast<std::streamsize>(_postfix.size());
158 std::memcpy(buffer, _postfix.data(), offset);
159 _postfix.erase(0, offset);
160 }
161
162 return static_cast<int>(offset);
163 }
164 }
165
166 _pIstr->seekg(-static_cast<int>(dataInfoSize - 4), std::ios::cur);
167 if (!_pIstr->good()) throw Poco::IOException("Failed to seek on input stream");
168
169 buffer[offset++] = ZipDataInfo::HEADER[0];
170 buffer[offset++] = ZipDataInfo::HEADER[1];
171 buffer[offset++] = ZipDataInfo::HEADER[2];
172 buffer[offset++] = ZipDataInfo::HEADER[3];
173 _matchCnt = 0;
174 }
175 else
176 {
177 buffer[offset++] = ZipDataInfo::HEADER[0];
178 buffer[offset++] = ZipDataInfo::HEADER[1];
179 buffer[offset++] = ZipDataInfo::HEADER[2];
180 buffer[offset++] = c;
181 _matchCnt = 0;
182 }
183 }
184 }
185 }
186
187 _length += offset;
188 return static_cast<int>(offset);
189
190}
191
192
193int AutoDetectStreamBuf::writeToDevice(const char* /*buffer*/, std::streamsize /*length*/)
194{
195 return -1; // not supported
196}
197
198
199AutoDetectIOS::AutoDetectIOS(std::istream& istr, const std::string& pre, const std::string& post, bool reposition, Poco::UInt32 start, bool needsZip64):
200 _buf(istr, pre, post, reposition, start, needsZip64)
201{
202 poco_ios_init(&_buf);
203}
204
205
206AutoDetectIOS::~AutoDetectIOS()
207{
208}
209
210
211AutoDetectStreamBuf* AutoDetectIOS::rdbuf()
212{
213 return &_buf;
214}
215
216
217AutoDetectInputStream::AutoDetectInputStream(std::istream& istr, const std::string& pre, const std::string& post, bool reposition, Poco::UInt32 start, bool needsZip64):
218 AutoDetectIOS(istr, pre, post, reposition, start, needsZip64),
219 std::istream(&_buf)
220{
221}
222
223
224AutoDetectInputStream::~AutoDetectInputStream()
225{
226}
227
228
229} } // namespace Poco::Zip
230