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 <array>
17 #include "jpeg_exif_metadata_accessor.h"
18 
19 #include <libexif/exif-data.h>
20 #include <array>
21 #include "file_metadata_stream.h"
22 #include "image_log.h"
23 #include "media_errors.h"
24 #include "tiff_parser.h"
25 
26 #undef LOG_DOMAIN
27 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
28 
29 #undef LOG_TAG
30 #define LOG_TAG "JpegExifMetadataAccessor"
31 
32 namespace OHOS {
33 namespace Media {
34 namespace {
35 using uint_8 = byte;
36 constexpr byte JPEG_MARKER_APP0 = 0xe0;
37 constexpr byte JPEG_MARKER_APP1 = 0xe1;
38 constexpr byte JPEG_MARKER_SOI = 0xd8;
39 constexpr byte JPEG_MARKER_EOI = 0xd9;
40 constexpr byte JPEG_MARKER_RST1 = 0xd0;
41 constexpr byte JPEG_MARKER_SOS = 0xda;
42 constexpr auto EXIF_ID = "Exif\0\0";
43 constexpr auto EXIF_BLOB_OFFSET = 2;
44 constexpr auto EXIF_ID_LENGTH = 2;
45 constexpr auto SEGMENT_LENGTH_SIZE = 2;
46 constexpr auto READ_BYTES = 2;
47 constexpr auto JPEG_HEADER_LENGTH = 2;
48 constexpr auto EXIF_ID_SIZE = 6;
49 constexpr auto APP1_EXIF_LENGTH = 8;
50 constexpr auto APP1_HEADER_LENGTH = 10;
51 constexpr auto MARKER_LENGTH_SIZE = 4;
52 constexpr auto READ_WRITE_BLOCK_SIZE = 4096;
53 constexpr auto READ_WRITE_BLOCK_SIZE_NUM = 32;
54 constexpr auto JPEG_MARKER_HEADER = 0xff;
55 constexpr auto JPEG_DATA_MAX_SIZE = 0xffff;
56 }
57 
JpegExifMetadataAccessor(std::shared_ptr<MetadataStream> & stream)58 JpegExifMetadataAccessor::JpegExifMetadataAccessor(std::shared_ptr<MetadataStream> &stream)
59     : AbstractExifMetadataAccessor(stream)
60 {}
61 
~JpegExifMetadataAccessor()62 JpegExifMetadataAccessor::~JpegExifMetadataAccessor() {}
63 
Read()64 uint32_t JpegExifMetadataAccessor::Read()
65 {
66     DataBuf dataBuf;
67     if (!ReadBlob(dataBuf)) {
68         IMAGE_LOGD("Failed to read data buffer from image stream.");
69         return ERR_IMAGE_SOURCE_DATA;
70     }
71 
72     ExifData *exifData;
73     TiffParser::DecodeJpegExif(reinterpret_cast<const unsigned char *>(dataBuf.CData()), dataBuf.Size(), &exifData);
74     if (exifData == nullptr) {
75         IMAGE_LOGE("Failed to decode EXIF data from image stream.");
76         return ERR_EXIF_DECODE_FAILED;
77     }
78 
79     exifMetadata_ = std::make_shared<OHOS::Media::ExifMetadata>(exifData);
80 
81     return SUCCESS;
82 }
83 
ReadBlob(DataBuf & blob)84 bool JpegExifMetadataAccessor::ReadBlob(DataBuf &blob)
85 {
86     if (!imageStream_->IsOpen()) {
87         IMAGE_LOGE("Output image stream is not open.");
88         return false;
89     }
90     imageStream_->Seek(0, SeekPos::BEGIN);
91 
92     int marker = FindNextMarker();
93     if (marker == EOF) {
94         IMAGE_LOGE("Failed to find marker 0xff in image stream.");
95         return false;
96     }
97 
98     while ((marker != JPEG_MARKER_SOS) && (marker != JPEG_MARKER_EOI)) {
99         const auto [sizeBuf, size] = ReadSegmentLength(marker);
100 
101         if ((marker == JPEG_MARKER_APP1) && (size >= APP1_EXIF_LENGTH)) {
102             blob.Resize(size - SEGMENT_LENGTH_SIZE);
103             if (imageStream_->Read(blob.Data(), (size - SEGMENT_LENGTH_SIZE)) == -1) {
104                 return false;
105             }
106             if (blob.CmpBytes(0, EXIF_ID, EXIF_ID_SIZE) == 0) {
107                 tiffOffset_ = imageStream_->Tell() - static_cast<long>(blob.Size()) + EXIF_ID_SIZE;
108                 return true;
109             }
110         }
111 
112         marker = FindNextMarker();
113         if (marker == EOF) {
114             IMAGE_LOGE("Failed to find marker 0xff in image stream.");
115             return false;
116         }
117     }
118     IMAGE_LOGD("Failed to find APP1 in image stream.");
119     return false;
120 }
121 
Write()122 uint32_t JpegExifMetadataAccessor::Write()
123 {
124     uint8_t *dataBlob = nullptr;
125     uint32_t size = 0;
126     if (!GetExifEncodeBlob(&dataBlob, size)) {
127         IMAGE_LOGE("Failed to encode metadata. Size: %{public}u", size);
128         return ERR_MEDIA_VALUE_INVALID;
129     }
130 
131     uint32_t result = UpdateData(dataBlob, size);
132 
133     if (dataBlob != nullptr) {
134         free(dataBlob);
135         dataBlob = nullptr;
136     }
137 
138     return result;
139 }
140 
WriteBlob(DataBuf & blob)141 uint32_t JpegExifMetadataAccessor::WriteBlob(DataBuf &blob)
142 {
143     byte *dataBlob = nullptr;
144     uint32_t size = 0;
145     if (!GetExifBlob(blob, &dataBlob, size)) {
146         IMAGE_LOGE("Blob data is empty. Size: %{public}u", size);
147         return ERR_MEDIA_VALUE_INVALID;
148     }
149 
150     return UpdateData(dataBlob, size);
151 }
152 
FindNextMarker() const153 int JpegExifMetadataAccessor::FindNextMarker() const
154 {
155     int marker = -1;
156     do {
157         marker = imageStream_->ReadByte();
158         if (marker == EOF) {
159             return marker;
160         }
161     } while (marker != JPEG_MARKER_HEADER);
162 
163     do {
164         marker = imageStream_->ReadByte();
165         if (marker == EOF) {
166             return marker;
167         }
168     } while (marker == JPEG_MARKER_HEADER);
169 
170     return marker;
171 }
172 
HasLength(byte marker)173 bool HasLength(byte marker)
174 {
175     if ((marker >= JPEG_MARKER_RST1) && (marker <= JPEG_MARKER_EOI)) {
176         return false;
177     }
178     return true;
179 }
180 
ReadSegmentLength(uint8_t marker) const181 std::pair<std::array<byte, 2>, uint16_t> JpegExifMetadataAccessor::ReadSegmentLength(uint8_t marker) const
182 {
183     std::array<byte, READ_BYTES> buf { 0, 0 };
184     uint16_t size { 0 };
185     if (HasLength(marker)) {
186         if (imageStream_->Read(buf.data(), buf.size()) == -1) {
187             IMAGE_LOGE("Failed to read from image stream. Marker: %{public}u", marker);
188             return { buf, size };
189         }
190         size = GetUShort(buf.data(), bigEndian);
191     }
192     return { buf, size };
193 }
194 
ReadNextSegment(byte marker)195 DataBuf JpegExifMetadataAccessor::ReadNextSegment(byte marker)
196 {
197     const auto [sizeBuf, size] = ReadSegmentLength(marker);
198     DataBuf buf(size);
199     if (size > SEGMENT_LENGTH_SIZE &&
200         (imageStream_->Read(buf.Data(SEGMENT_LENGTH_SIZE), (size - SEGMENT_LENGTH_SIZE)) != -1)) {
201         std::copy(sizeBuf.begin(), sizeBuf.end(), buf.Begin());
202     }
203 
204     return buf;
205 }
206 
GetExifEncodeBlob(uint8_t ** dataBlob,uint32_t & size)207 bool JpegExifMetadataAccessor::GetExifEncodeBlob(uint8_t **dataBlob, uint32_t &size)
208 {
209     if (this->Get() == nullptr) {
210         IMAGE_LOGE("EXIF metadata is empty.");
211         return false;
212     }
213 
214     ExifData *exifData = this->Get()->GetExifData();
215     TiffParser::EncodeJpegExif(dataBlob, size, exifData);
216 
217     if (dataBlob == nullptr || *dataBlob == nullptr) {
218         IMAGE_LOGE("Failed to encode JPEG data.");
219         return false;
220     }
221 
222     return (size > 0);
223 }
224 
GetExifBlob(const DataBuf & blob,uint8_t ** dataBlob,uint32_t & size)225 bool JpegExifMetadataAccessor::GetExifBlob(const DataBuf &blob, uint8_t **dataBlob, uint32_t &size)
226 {
227     if (blob.Empty()) {
228         IMAGE_LOGE("EXIF blob data is empty.");
229         return false;
230     }
231 
232     *dataBlob = const_cast<byte *>(blob.CData());
233     size = blob.Size();
234 
235     return true;
236 }
237 
WriteHeader(BufferMetadataStream & bufStream)238 bool JpegExifMetadataAccessor::WriteHeader(BufferMetadataStream &bufStream)
239 {
240     byte tmpBuf[JPEG_HEADER_LENGTH];
241     tmpBuf[0] = JPEG_MARKER_HEADER;
242     tmpBuf[1] = JPEG_MARKER_SOI;
243     if (bufStream.Write(tmpBuf, JPEG_HEADER_LENGTH) != JPEG_HEADER_LENGTH) {
244         return false;
245     }
246 
247     return true;
248 }
249 
GetInsertPosAndMarkerAPP1()250 std::tuple<size_t, size_t> JpegExifMetadataAccessor::GetInsertPosAndMarkerAPP1()
251 {
252     size_t markerCount = 0;
253     size_t skipExifSeqNum = -1;
254     size_t insertPos = 0;
255 
256     imageStream_->Seek(0, SeekPos::BEGIN);
257 
258     int ret = FindNextMarker();
259     if (ret == EOF) {
260         IMAGE_LOGE("GetInsertPosAndMarkerAPP1 failed to find marker 0xff in image stream.");
261         return std::make_tuple(insertPos, skipExifSeqNum);
262     }
263     byte marker = static_cast<byte>(ret);
264     while ((marker != JPEG_MARKER_SOS) && (marker != JPEG_MARKER_EOI)) {
265         DataBuf buf = ReadNextSegment(marker);
266         if (marker == JPEG_MARKER_APP0) {
267             insertPos = markerCount + 1;
268         } else if ((marker == JPEG_MARKER_APP1) && (buf.Size() >= APP1_EXIF_LENGTH) &&
269             (buf.CmpBytes(EXIF_BLOB_OFFSET, EXIF_ID, EXIF_ID_SIZE) == 0)) {
270             skipExifSeqNum = markerCount;
271         }
272 
273         int ret = FindNextMarker();
274         if (ret == EOF) {
275             break;
276         }
277         marker = static_cast<byte>(ret);
278         ++markerCount;
279     }
280 
281     return std::make_tuple(insertPos, skipExifSeqNum);
282 }
283 
WriteData(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)284 bool JpegExifMetadataAccessor::WriteData(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
285 {
286     std::array<byte, APP1_HEADER_LENGTH> tmpBuf;
287     tmpBuf[0] = JPEG_MARKER_HEADER;
288     tmpBuf[1] = JPEG_MARKER_APP1;
289 
290     if (size > (JPEG_DATA_MAX_SIZE - APP1_EXIF_LENGTH)) {
291         IMAGE_LOGE("JPEG EXIF size exceeds maximum limit. Size: %{public}u", size);
292         return false;
293     }
294 
295     ssize_t writeHeaderLength = MARKER_LENGTH_SIZE;
296     ssize_t exifHeaderLength = EXIF_ID_LENGTH;
297 
298     if (dataBlob == nullptr) {
299         IMAGE_LOGE("Failed to write data blob. dataBlob is nullptr");
300         return false;
301     }
302     if (size >= EXIF_ID_SIZE && memcmp(reinterpret_cast<char *>(dataBlob), EXIF_ID, EXIF_ID_SIZE) != 0) {
303         writeHeaderLength = APP1_HEADER_LENGTH;
304         exifHeaderLength = APP1_EXIF_LENGTH;
305         std::copy_n(EXIF_ID, EXIF_ID_SIZE, tmpBuf.data() + MARKER_LENGTH_SIZE);
306     }
307 
308     US2Data(tmpBuf.data() + EXIF_BLOB_OFFSET, static_cast<uint16_t>(size + exifHeaderLength), bigEndian);
309     if (bufStream.Write(tmpBuf.data(), writeHeaderLength) != writeHeaderLength) {
310         IMAGE_LOGE("Failed to write EXIF_ID to temporary stream. Expected length: %{public}zu", writeHeaderLength);
311         return false;
312     }
313 
314     if (bufStream.Write(dataBlob, size) != size) {
315         IMAGE_LOGE("Failed to write data blob to temporary stream. Expected size: %{public}u", size);
316         return false;
317     }
318 
319     return true;
320 }
321 
WriteSegment(BufferMetadataStream & bufStream,uint8_t marker,const DataBuf & buf)322 bool JpegExifMetadataAccessor::WriteSegment(BufferMetadataStream &bufStream, uint8_t marker, const DataBuf &buf)
323 {
324     std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
325     tmpBuf[0] = JPEG_MARKER_HEADER;
326     tmpBuf[1] = marker;
327     if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
328         IMAGE_LOGE("Failed to write marker and segment. Marker: %{public}u", marker);
329         return false;
330     }
331     if (bufStream.Write(const_cast<byte *>(buf.CData()), buf.Size()) != static_cast<int>(buf.Size())) {
332         IMAGE_LOGE("Failed to write buffer. Buffer size: %{public}zu", buf.Size());
333         return false;
334     }
335 
336     return true;
337 }
338 
WriteTail(BufferMetadataStream & bufStream)339 bool JpegExifMetadataAccessor::WriteTail(BufferMetadataStream &bufStream)
340 {
341     std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
342     tmpBuf[0] = JPEG_MARKER_HEADER;
343     tmpBuf[1] = JPEG_MARKER_SOS;
344     if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
345         IMAGE_LOGE("Failed to write the final marker. Expected length: %{public}d",
346                    static_cast<int>(SEGMENT_LENGTH_SIZE));
347         return false;
348     }
349 
350     return true;
351 }
352 
CopyRestData(BufferMetadataStream & bufStream)353 bool JpegExifMetadataAccessor::CopyRestData(BufferMetadataStream &bufStream)
354 {
355     DataBuf buf(READ_WRITE_BLOCK_SIZE * READ_WRITE_BLOCK_SIZE_NUM);
356     ssize_t readSize = imageStream_->Read(buf.Data(), buf.Size());
357     while (readSize > 0) {
358         if (bufStream.Write(const_cast<byte *>(buf.CData()), readSize) != readSize) {
359             IMAGE_LOGE("Failed to write block data to temporary stream. Expected size: %{public}zd", readSize);
360             return false;
361         }
362         readSize = imageStream_->Read(buf.Data(), buf.Size());
363     }
364 
365     return true;
366 }
367 
UpdateExifMetadata(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)368 bool JpegExifMetadataAccessor::UpdateExifMetadata(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
369 {
370     size_t markerCount = 0;
371     auto [insertPos, skipExifSeqNum] = GetInsertPosAndMarkerAPP1();
372 
373     if (!WriteHeader(bufStream)) {
374         IMAGE_LOGE("Failed to write header to output image stream");
375         return false;
376     }
377 
378     imageStream_->Seek(0, SeekPos::BEGIN);
379 
380     int ret = FindNextMarker();
381     if (ret == EOF) {
382         IMAGE_LOGE("UpdateExifMetadata failed to find marker 0xff in image stream.");
383         return false;
384     }
385     byte marker = static_cast<byte>(ret);
386     while (marker != JPEG_MARKER_SOS) {
387         DataBuf buf = ReadNextSegment(marker);
388         if (markerCount == insertPos) {
389             WriteData(bufStream, dataBlob, size);
390         }
391 
392         if (marker == JPEG_MARKER_EOI) {
393             break;
394         }
395 
396         if ((markerCount != skipExifSeqNum) && (marker != JPEG_MARKER_SOI)) {
397             WriteSegment(bufStream, marker, buf);
398         } else {
399             IMAGE_LOGD("Skipping existing exifApp segment number.");
400         }
401 
402         int ret = FindNextMarker();
403         if (ret == EOF) {
404             break;
405         }
406         marker = static_cast<byte>(ret);
407         ++markerCount;
408     }
409 
410     WriteTail(bufStream);
411 
412     return CopyRestData(bufStream);
413 }
414 
UpdateData(uint8_t * dataBlob,uint32_t size)415 uint32_t JpegExifMetadataAccessor::UpdateData(uint8_t *dataBlob, uint32_t size)
416 {
417     BufferMetadataStream tmpBufStream;
418     if (!tmpBufStream.Open(OpenMode::ReadWrite)) {
419         IMAGE_LOGE("Failed to open temporary image stream");
420         return ERR_IMAGE_SOURCE_DATA;
421     }
422 
423     if (!imageStream_->IsOpen()) {
424         IMAGE_LOGE("The output image stream is not open");
425         return ERR_IMAGE_SOURCE_DATA;
426     }
427 
428     if (!UpdateExifMetadata(tmpBufStream, dataBlob, size)) {
429         IMAGE_LOGE("Failed to write to temporary image stream");
430         return ERROR;
431     }
432 
433     imageStream_->Seek(0, SeekPos::BEGIN);
434     imageStream_->CopyFrom(tmpBufStream);
435 
436     return SUCCESS;
437 }
438 } // namespace Media
439 } // namespace OHOS