1 /*
2  * Copyright (C) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <zlib.h>
17 
18 #include "data_buf.h"
19 #include "exif_metadata.h"
20 #include "image_log.h"
21 #include "media_errors.h"
22 #include "metadata_stream.h"
23 #include "png_exif_metadata_accessor.h"
24 #include "png_image_chunk_utils.h"
25 #include "tiff_parser.h"
26 
27 #undef LOG_DOMAIN
28 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
29 
30 #undef LOG_TAG
31 #define LOG_TAG "PngExifMetadataAccessor"
32 
33 namespace OHOS {
34 namespace Media {
35 namespace {
36 constexpr auto PNG_CHUNK_DATA_MAX = 0x7fffffff;
37 constexpr auto PNG_CHUNK_IEND = "IEND";
38 constexpr auto PNG_CHUNK_IHDR = "IHDR";
39 constexpr auto PNG_CHUNK_TEXT = "tEXt";
40 constexpr auto PNG_CHUNK_ZTXT = "zTXt";
41 constexpr auto PNG_CHUNK_ITXT = "iTXt";
42 constexpr auto PNG_CHUNK_EXIF = "eXIf";
43 constexpr auto PNG_CHUNK_HEAD_SIZE = 8;
44 constexpr auto PNG_CHUNK_LENGTH_SIZE = 4;
45 constexpr auto PNG_CHUNK_TYPE_SIZE = 4;
46 constexpr auto PNG_CHUNK_CRC_SIZE = 4;
47 constexpr auto PNG_SIGN_SIZE = 8;
48 }
49 
PngExifMetadataAccessor(std::shared_ptr<MetadataStream> & stream)50 PngExifMetadataAccessor::PngExifMetadataAccessor(std::shared_ptr<MetadataStream> &stream)
51     : AbstractExifMetadataAccessor(stream)
52 {}
53 
~PngExifMetadataAccessor()54 PngExifMetadataAccessor::~PngExifMetadataAccessor() {}
55 
IsPngType() const56 bool PngExifMetadataAccessor::IsPngType() const
57 {
58     if (imageStream_->IsEof()) {
59         return false;
60     }
61     const int32_t len = PNG_SIGN_SIZE;
62     byte buf[len];
63     if (imageStream_->Read(buf, len) == -1) {
64         return false;
65     }
66     if (imageStream_->IsEof()) {
67         return false;
68     }
69 
70     return !memcmp(buf, pngSignature, PNG_SIGN_SIZE);
71 }
72 
ReadChunk(DataBuf & buffer) const73 ssize_t PngExifMetadataAccessor::ReadChunk(DataBuf &buffer) const
74 {
75     return imageStream_->Read(buffer.Data(), buffer.Size());
76 }
77 
FindTiffFromText(const DataBuf & data,const std::string chunkType,DataBuf & tiffData)78 bool PngExifMetadataAccessor::FindTiffFromText(const DataBuf &data, const std::string chunkType,
79     DataBuf &tiffData)
80 {
81     PngImageChunkUtils::TextChunkType txtType;
82     if (chunkType == PNG_CHUNK_TEXT) {
83         txtType = PngImageChunkUtils::tEXtChunk;
84     } else if (chunkType == PNG_CHUNK_ZTXT) {
85         txtType = PngImageChunkUtils::zTXtChunk;
86     } else if (chunkType == PNG_CHUNK_ITXT) {
87         txtType = PngImageChunkUtils::iTXtChunk;
88     } else {
89         return false;
90     }
91     bool isCompressed;
92     if (PngImageChunkUtils::ParseTextChunk(data, txtType, tiffData, isCompressed) != 0) {
93         return false;
94     }
95     if (isCompressed) {
96         isCompressed_ = isCompressed;
97     }
98     return true;
99 }
100 
ProcessExifData(DataBuf & blob,std::string chunkType,uint32_t chunkLength)101 bool PngExifMetadataAccessor::ProcessExifData(DataBuf &blob, std::string chunkType, uint32_t chunkLength)
102 {
103     DataBuf chunkData(chunkLength);
104     if (chunkLength > 0) {
105         if (static_cast<size_t>(ReadChunk(chunkData)) != chunkData.Size()) {
106             IMAGE_LOGE("Failed to read chunk data. Expected size: %{public}zu", chunkData.Size());
107             return false;
108         }
109     }
110     if (chunkType != PNG_CHUNK_EXIF) {
111         return FindTiffFromText(chunkData, chunkType, blob);
112     }
113     blob = chunkData;
114     return true;
115 }
116 
ReadBlob(DataBuf & blob)117 bool PngExifMetadataAccessor::ReadBlob(DataBuf &blob)
118 {
119     if (!imageStream_->IsOpen()) {
120         IMAGE_LOGE("The output image stream is not open");
121         return false;
122     }
123     imageStream_->Seek(0, SeekPos::BEGIN);
124 
125     if (!IsPngType()) {
126         IMAGE_LOGE("The file is not a PNG file");
127         return false;
128     }
129 
130     const size_t imgSize = static_cast<size_t>(imageStream_->GetSize());
131     DataBuf chunkHead(PNG_CHUNK_HEAD_SIZE);
132 
133     while (!imageStream_->IsEof()) {
134         if (static_cast<size_t>(ReadChunk(chunkHead)) != chunkHead.Size()) {
135             IMAGE_LOGE("Failed to read chunk head. Expected size: %{public}zu", chunkHead.Size());
136             return false;
137         }
138         uint32_t chunkLength = chunkHead.ReadUInt32(0, bigEndian);
139         if (chunkLength > imgSize - imageStream_->Tell()) {
140             IMAGE_LOGE("Chunk length is larger than the remaining image size");
141             return false;
142         }
143         std::string chunkType(reinterpret_cast<const char *>(chunkHead.CData(PNG_CHUNK_LENGTH_SIZE)),
144             PNG_CHUNK_TYPE_SIZE);
145         if (chunkType == PNG_CHUNK_IEND) {
146             return false;
147         }
148         if (chunkType == PNG_CHUNK_TEXT || chunkType == PNG_CHUNK_ZTXT || chunkType == PNG_CHUNK_EXIF ||
149             chunkType == PNG_CHUNK_ITXT) {
150             if (ProcessExifData(blob, chunkType, chunkLength)) {
151                 break;
152             }
153             chunkLength = 0;
154         }
155         imageStream_->Seek(chunkLength + PNG_CHUNK_CRC_SIZE, CURRENT);
156         if (imageStream_->IsEof()) {
157             IMAGE_LOGE("Failed to read the file");
158             return false;
159         }
160     }
161     tiffOffset_ = imageStream_->Tell() - static_cast<long>(blob.Size());
162     return true;
163 }
164 
Read()165 uint32_t PngExifMetadataAccessor::Read()
166 {
167     DataBuf tiffBuf;
168     if (!ReadBlob(tiffBuf)) {
169         IMAGE_LOGD("Failed to read the blob.");
170         return ERR_IMAGE_SOURCE_DATA;
171     }
172     ExifData *exifData;
173     size_t byteOrderPos = TiffParser::FindTiffPos(tiffBuf);
174     if (byteOrderPos == std::numeric_limits<size_t>::max()) {
175         IMAGE_LOGE("Cannot find TIFF byte order in Exif metadata.");
176         return ERR_IMAGE_SOURCE_DATA;
177     }
178     TiffParser::Decode(tiffBuf.CData(), tiffBuf.Size(), &exifData);
179     if (exifData == nullptr) {
180         IMAGE_LOGE("Failed to decode TIFF buffer.");
181         return ERR_EXIF_DECODE_FAILED;
182     }
183 
184     exifMetadata_ = std::make_shared<OHOS::Media::ExifMetadata>(exifData);
185     return SUCCESS;
186 }
187 
GetExifEncodedBlob(uint8_t ** dataBlob,uint32_t & size)188 bool PngExifMetadataAccessor::GetExifEncodedBlob(uint8_t **dataBlob, uint32_t &size)
189 {
190     if (this->Get() == nullptr) {
191         IMAGE_LOGE("Exif metadata empty");
192         return false;
193     }
194 
195     ExifData *exifData = this->Get()->GetExifData();
196     TiffParser::Encode(dataBlob, size, exifData);
197 
198     if (dataBlob == nullptr || *dataBlob == nullptr) {
199         IMAGE_LOGE("Encode Jpeg data failed");
200         return false;
201     }
202     DataBuf blobBuf(*dataBlob, size);
203     size_t byteOrderPos = TiffParser::FindTiffPos(blobBuf);
204     if (byteOrderPos == std::numeric_limits<size_t>::max()) {
205         IMAGE_LOGE("Failed to Encode Exif metadata: cannot find tiff byte order");
206         return false;
207     }
208     return ((size > 0) && (size <= PNG_CHUNK_DATA_MAX));
209 }
210 
WriteData(BufferMetadataStream & bufStream,uint8_t * data,uint32_t size)211 bool PngExifMetadataAccessor::WriteData(BufferMetadataStream &bufStream, uint8_t *data, uint32_t size)
212 {
213     if (bufStream.Write(data, size) != size) {
214         IMAGE_LOGE("Write the bufStream failed");
215         return false;
216     }
217     return true;
218 }
219 
WriteExifData(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size,DataBuf & chunkBuf,std::string chunkType)220 bool PngExifMetadataAccessor::WriteExifData(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size,
221                                             DataBuf &chunkBuf, std::string chunkType)
222 {
223     if ((dataBlob == nullptr) || (size == 0)) {
224         return false;
225     }
226     if (chunkType == PNG_CHUNK_EXIF) {
227         return true;
228     }
229     if (chunkType == PNG_CHUNK_IHDR) {
230         if (!WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size())) {
231             return false;
232         }
233 
234         byte length[PNG_CHUNK_LENGTH_SIZE];
235         UL2Data(length, size, bigEndian);
236         uint8_t typeExif[] = "eXIf";
237         uint32_t tmp = crc32(0L, Z_NULL, 0);
238         tmp = crc32(tmp, typeExif, PNG_CHUNK_CRC_SIZE);
239         tmp = crc32(tmp, dataBlob, size);
240         byte crc[PNG_CHUNK_CRC_SIZE];
241         UL2Data(crc, tmp, bigEndian);
242         if (!(WriteData(bufStream, length, PNG_CHUNK_LENGTH_SIZE) &&
243             WriteData(bufStream, typeExif, PNG_CHUNK_TYPE_SIZE) &&
244             WriteData(bufStream, dataBlob, size) &&
245             WriteData(bufStream, crc, PNG_CHUNK_CRC_SIZE))) {
246             return false;
247         }
248     } else if (chunkType == PNG_CHUNK_TEXT || chunkType == PNG_CHUNK_ZTXT || chunkType == PNG_CHUNK_ITXT) {
249         if (PngImageChunkUtils::FindExifFromTxt(chunkBuf)) {
250             return true;
251         }
252 
253         return WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size());
254     }
255     return true;
256 }
257 
UpdateExifMetadata(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)258 bool PngExifMetadataAccessor::UpdateExifMetadata(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
259 {
260     const size_t imgSize = static_cast<size_t>(imageStream_->GetSize());
261     DataBuf chunkHead(PNG_CHUNK_HEAD_SIZE);
262 
263     if (!WriteData(bufStream, pngSignature, PNG_SIGN_SIZE)) {
264         return false;
265     }
266 
267     while (!imageStream_->IsEof()) {
268         if (static_cast<size_t>(ReadChunk(chunkHead)) != chunkHead.Size()) {
269             IMAGE_LOGE("Read chunk head error.");
270             return false;
271         }
272 
273         uint32_t chunkLength = chunkHead.ReadUInt32(0, bigEndian);
274         if (chunkLength > imgSize - imageStream_->Tell()) {
275             IMAGE_LOGE("Read chunk length error.");
276             return false;
277         }
278 
279         DataBuf chunkBuf(PNG_CHUNK_HEAD_SIZE + chunkLength + PNG_CHUNK_CRC_SIZE);
280         std::copy_n(chunkHead.Begin(), PNG_CHUNK_HEAD_SIZE, chunkBuf.Begin());
281 
282         ssize_t bufLength = imageStream_->Read(chunkBuf.Data(PNG_CHUNK_HEAD_SIZE), chunkLength + PNG_CHUNK_CRC_SIZE);
283         if (bufLength != chunkLength + PNG_CHUNK_CRC_SIZE) {
284             IMAGE_LOGE("Read chunk head error.");
285             return false;
286         }
287         std::string chunkType(reinterpret_cast<const char*>(chunkHead.CData(PNG_CHUNK_LENGTH_SIZE)),
288                               PNG_CHUNK_TYPE_SIZE);
289         if (chunkType == PNG_CHUNK_IEND) {
290             return WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size());
291         }
292         if (chunkType == PNG_CHUNK_EXIF || chunkType == PNG_CHUNK_IHDR || chunkType == PNG_CHUNK_TEXT ||
293             chunkType == PNG_CHUNK_ZTXT || chunkType == PNG_CHUNK_ITXT) {
294             if (!WriteExifData(bufStream, dataBlob, size, chunkBuf, chunkType)) {
295                 return false;
296             }
297         } else {
298             if (!WriteData(bufStream, chunkBuf.Data(), chunkBuf.Size())) {
299                 return false;
300             }
301         }
302     }
303     return false;
304 }
305 
UpdateData(uint8_t * dataBlob,uint32_t size)306 uint32_t PngExifMetadataAccessor::UpdateData(uint8_t *dataBlob, uint32_t size)
307 {
308     BufferMetadataStream tmpBufStream;
309     if (!tmpBufStream.Open(OpenMode::ReadWrite)) {
310         IMAGE_LOGE("Image temp stream open failed");
311         return ERR_IMAGE_SOURCE_DATA;
312     }
313 
314     if (!UpdateExifMetadata(tmpBufStream, dataBlob, size)) {
315         IMAGE_LOGE("Image temp stream write failed");
316         return ERROR;
317     }
318 
319     imageStream_->Seek(0, SeekPos::BEGIN);
320     if (!imageStream_->CopyFrom(tmpBufStream)) {
321         IMAGE_LOGE("Copy from temp stream failed");
322         return ERR_MEDIA_INVALID_OPERATION;
323     }
324     return SUCCESS;
325 }
326 
Write()327 uint32_t PngExifMetadataAccessor::Write()
328 {
329     uint8_t *dataBlob = nullptr;
330     uint32_t size = 0;
331 
332     if (!imageStream_->IsOpen()) {
333         IMAGE_LOGE("Output image stream not open");
334         return ERR_IMAGE_SOURCE_DATA;
335     }
336     imageStream_->Seek(0, SeekPos::BEGIN);
337     if (!IsPngType()) {
338         IMAGE_LOGE("Is not a PNG file.");
339         return ERR_IMAGE_SOURCE_DATA;
340     }
341 
342     if (!GetExifEncodedBlob(&dataBlob, size)) {
343         IMAGE_LOGE("Encode Metadata failed");
344         return ERR_MEDIA_VALUE_INVALID;
345     }
346 
347     uint32_t result = UpdateData(dataBlob, size);
348 
349     if (dataBlob != nullptr) {
350         free(dataBlob);
351         dataBlob = nullptr;
352     }
353 
354     return result;
355 }
356 
WriteBlob(DataBuf & blob)357 uint32_t PngExifMetadataAccessor::WriteBlob(DataBuf &blob)
358 {
359     byte *dataBlob = nullptr;
360     uint32_t size = 0;
361 
362     imageStream_->Seek(0, SeekPos::BEGIN);
363     if (!IsPngType()) {
364         IMAGE_LOGE("Is not a PNG file.");
365         return ERR_IMAGE_SOURCE_DATA;
366     }
367 
368     if (blob.Empty()) {
369         IMAGE_LOGE("Image exif blob data empty");
370         return ERR_MEDIA_VALUE_INVALID;
371     }
372 
373     size_t byteOrderPos = TiffParser::FindTiffPos(blob);
374     if (byteOrderPos == std::numeric_limits<size_t>::max()) {
375         IMAGE_LOGE("Failed to checkout Exif metadata: cannot find tiff byte order");
376         return ERR_MEDIA_VALUE_INVALID;
377     }
378 
379     dataBlob = const_cast<byte *>(blob.CData());
380     size = blob.Size();
381 
382     return UpdateData(dataBlob, size);
383 }
384 
GetFilterArea(const std::vector<std::string> & exifKeys,std::vector<std::pair<uint32_t,uint32_t>> & ranges)385 uint32_t PngExifMetadataAccessor::GetFilterArea(const std::vector<std::string> &exifKeys,
386     std::vector<std::pair<uint32_t, uint32_t>> &ranges)
387 {
388     uint32_t ret = Read();
389     if (ret != SUCCESS) {
390         IMAGE_LOGD("Failed to read the exif info.");
391         return E_NO_EXIF_TAG;
392     }
393     if (isCompressed_) {
394         IMAGE_LOGD("This png is compressed.");
395         return E_NO_EXIF_TAG;
396     }
397     exifMetadata_->GetFilterArea(exifKeys, ranges);
398     if (ranges.empty()) {
399         return E_NO_EXIF_TAG;
400     }
401     for (auto& range : ranges) {
402         range.first += static_cast<uint32_t>(GetTiffOffset());
403     }
404     return SUCCESS;
405 }
406 } // namespace Media
407 } // namespace OHOS
408