/* 'checkit_tiff' is a conformance checker for baseline TIFFs
 *
 * author: Andreas Romeyke, 2015-2022
 * licensed under conditions of libtiff
 * (see http://libtiff.maptools.org/misc.html)
 *
 */

#include "check.h"
#include "check_helper.h"
#include "ctstring.h"

static ret_t check_specific_offsets_within_filesize(ctiff_t * ctif, cifd_t * ifd) {
    GET_EMPTY_RET(ret);
    TIFP_CHECK( ctif, ret);
    size_t filesize = ctif->streamlen;
    uint16 count = get_ifd_count( ifd);
    for (int tagidx = 0; tagidx< count; tagidx++) {
        ifd_entry_t ifd_entry = TIFFGetRawTagIFDListEntry(ctif, ifd, tagidx);
        uint32 tag = TIFFGetRawTagListEntry(ctif, ifd, tagidx);
        if (ifd_entry.value_or_offset == is_offset || tag == TIFFTAG_EXIFIFD) {
            uint32 offset = ifd_entry.data32offset;
            uint32 streamlen = 0;
            if (tag_is_an_IFD(&ifd_entry) || tag == TIFFTAG_EXIFIFD) {
                cifd_t tmpifd = {0};
                get_IFD_at_pos(ctif, offset, &tmpifd);
                streamlen = 12*get_ifd_count(&tmpifd) + 2 + 4;
            } else {
                streamlen = get_streamlen_at_offset(&ifd_entry);
            }
            /*  end else if tag == TIFFTAG_EXIFIFD */
            char msg_if_subifd[8192] = {0};
            if (ifd->tag_if_subifd < 0) {
                sprintf( msg_if_subifd, "of sub IFD (tag=%u)", - ifd->tag_if_subifd);
            }
            if ((size_t) offset > filesize) {
                ret = set_value_found_ret_formatted_with_returncode(&ret, ifderror_offset_out_of_filesize, "tag %u %s pointing to 0x%08x (%lu) exceeding filesize %lu bytes", tag, msg_if_subifd, offset, offset, filesize);
                return ret;
            }
            if (((size_t) offset + streamlen) > filesize) {
                ret = set_value_found_ret_formatted_with_returncode(&ret, ifderror_offset_out_of_filesize, "tag %u %s pointing to 0x%08x (%lu) with stream of len %u, exceeding filesize %lu bytes", tag, msg_if_subifd, offset, offset, streamlen, filesize);
                return ret;
            }
        }
    }
    ret.returncode=is_valid;
    return ret;
}

/* check if offsets are within filesize */
ret_t check_all_offsets_within_filesize(ctiff_t * ctif) {
  GET_EMPTY_RET(ret);
  TIFP_CHECK( ctif, ret);
  ret = check_specific_offsets_within_filesize(ctif, ctif->ifd0);
  if (ret.returncode != is_valid) {
      return ret;
  }
  uint16 count = get_ifd0_count( ctif);
  for (int tagidx = 0; tagidx< count; tagidx++) {
      ifd_entry_t ifd_entry = TIFFGetRawTagIFDListEntry(ctif, ctif->ifd0, tagidx);
      uint32 tag = TIFFGetRawTagListEntry(ctif, ctif->ifd0, tagidx);
      if (
          tag_is_an_IFD(&ifd_entry)
            || (tag == TIFFTAG_EXIFIFD)
      ) {

          //printf("DEBUG: found IFD type for tag %u\n", tag);
          uint32 offset = ifd_entry.data32offset;
          cifd_t ifd = {0};
          ifd.tag_if_subifd = -tag;
          get_IFD_at_pos(ctif, offset, &ifd);
          ret = check_specific_offsets_within_filesize( ctif, &ifd);
          if (ret.returncode!=is_valid) { return ret; }
      }
  }
  ret.returncode=is_valid;
  return ret;
}


/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 smarttab expandtab :*/
