...

Source file src/github.com/dsoprea/go-exif/v3/ifd_enumerate.go

Documentation: github.com/dsoprea/go-exif/v3

     1  package exif
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"encoding/binary"
    13  
    14  	"github.com/dsoprea/go-logging"
    15  
    16  	"github.com/dsoprea/go-exif/v3/common"
    17  	"github.com/dsoprea/go-exif/v3/undefined"
    18  )
    19  
    20  var (
    21  	ifdEnumerateLogger = log.NewLogger("exif.ifd_enumerate")
    22  )
    23  
    24  var (
    25  	// ErrNoThumbnail means that no thumbnail was found.
    26  	ErrNoThumbnail = errors.New("no thumbnail")
    27  
    28  	// ErrNoGpsTags means that no GPS info was found.
    29  	ErrNoGpsTags = errors.New("no gps tags")
    30  
    31  	// ErrTagTypeNotValid means that the tag-type is not valid.
    32  	ErrTagTypeNotValid = errors.New("tag type invalid")
    33  
    34  	// ErrOffsetInvalid means that the file offset is not valid.
    35  	ErrOffsetInvalid = errors.New("file offset invalid")
    36  )
    37  
    38  var (
    39  	// ValidGpsVersions is the list of recognized EXIF GPS versions/signatures.
    40  	ValidGpsVersions = [][4]byte{
    41  		// 2.0.0.0 appears to have a very similar format to 2.2.0.0, so enabling
    42  		// it under that assumption.
    43  		//
    44  		// IFD-PATH=[IFD] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[114]
    45  		// IFD-PATH=[IFD/GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[02 00 00 00]
    46  		// IFD-PATH=[IFD/GPSInfo] ID=(0x0001) NAME=[GPSLatitudeRef] COUNT=(2) TYPE=[ASCII] VALUE=[S]
    47  		// IFD-PATH=[IFD/GPSInfo] ID=(0x0002) NAME=[GPSLatitude] COUNT=(3) TYPE=[RATIONAL] VALUE=[38/1...]
    48  		// IFD-PATH=[IFD/GPSInfo] ID=(0x0003) NAME=[GPSLongitudeRef] COUNT=(2) TYPE=[ASCII] VALUE=[E]
    49  		// IFD-PATH=[IFD/GPSInfo] ID=(0x0004) NAME=[GPSLongitude] COUNT=(3) TYPE=[RATIONAL] VALUE=[144/1...]
    50  		// IFD-PATH=[IFD/GPSInfo] ID=(0x0012) NAME=[GPSMapDatum] COUNT=(7) TYPE=[ASCII] VALUE=[WGS-84]
    51  		//
    52  		{2, 0, 0, 0},
    53  
    54  		{2, 2, 0, 0},
    55  
    56  		// Suddenly appeared at the default in 2.31: https://home.jeita.or.jp/tsc/std-pdf/CP-3451D.pdf
    57  		//
    58  		// Note that the presence of 2.3.0.0 doesn't seem to guarantee
    59  		// coordinates. In some cases, we seen just the following:
    60  		//
    61  		// GPS Tag Version     |2.3.0.0
    62  		// GPS Receiver Status |V
    63  		// Geodetic Survey Data|WGS-84
    64  		// GPS Differential Cor|0
    65  		//
    66  		{2, 3, 0, 0},
    67  	}
    68  )
    69  
    70  // byteParser knows how to decode an IFD and all of the tags it
    71  // describes.
    72  //
    73  // The IFDs and the actual values can float throughout the EXIF block, but the
    74  // IFD itself is just a minor header followed by a set of repeating,
    75  // statically-sized records. So, the tags (though notnecessarily their values)
    76  // are fairly simple to enumerate.
    77  type byteParser struct {
    78  	byteOrder     binary.ByteOrder
    79  	rs            io.ReadSeeker
    80  	ifdOffset     uint32
    81  	currentOffset uint32
    82  }
    83  
    84  // newByteParser returns a new byteParser struct.
    85  //
    86  // initialOffset is for arithmetic-based tracking of where we should be at in
    87  // the stream.
    88  func newByteParser(rs io.ReadSeeker, byteOrder binary.ByteOrder, initialOffset uint32) (bp *byteParser, err error) {
    89  	// TODO(dustin): Add test
    90  
    91  	bp = &byteParser{
    92  		rs:            rs,
    93  		byteOrder:     byteOrder,
    94  		currentOffset: initialOffset,
    95  	}
    96  
    97  	return bp, nil
    98  }
    99  
   100  // getUint16 reads a uint16 and advances both our current and our current
   101  // accumulator (which allows us to know how far to seek to the beginning of the
   102  // next IFD when it's time to jump).
   103  func (bp *byteParser) getUint16() (value uint16, raw []byte, err error) {
   104  	defer func() {
   105  		if state := recover(); state != nil {
   106  			err = log.Wrap(state.(error))
   107  		}
   108  	}()
   109  
   110  	// TODO(dustin): Add test
   111  
   112  	needBytes := 2
   113  
   114  	raw = make([]byte, needBytes)
   115  
   116  	_, err = io.ReadFull(bp.rs, raw)
   117  	log.PanicIf(err)
   118  
   119  	value = bp.byteOrder.Uint16(raw)
   120  
   121  	bp.currentOffset += uint32(needBytes)
   122  
   123  	return value, raw, nil
   124  }
   125  
   126  // getUint32 reads a uint32 and advances both our current and our current
   127  // accumulator (which allows us to know how far to seek to the beginning of the
   128  // next IFD when it's time to jump).
   129  func (bp *byteParser) getUint32() (value uint32, raw []byte, err error) {
   130  	defer func() {
   131  		if state := recover(); state != nil {
   132  			err = log.Wrap(state.(error))
   133  		}
   134  	}()
   135  
   136  	// TODO(dustin): Add test
   137  
   138  	needBytes := 4
   139  
   140  	raw = make([]byte, needBytes)
   141  
   142  	_, err = io.ReadFull(bp.rs, raw)
   143  	log.PanicIf(err)
   144  
   145  	value = bp.byteOrder.Uint32(raw)
   146  
   147  	bp.currentOffset += uint32(needBytes)
   148  
   149  	return value, raw, nil
   150  }
   151  
   152  // CurrentOffset returns the starting offset but the number of bytes that we
   153  // have parsed. This is arithmetic-based tracking, not a seek(0) operation.
   154  func (bp *byteParser) CurrentOffset() uint32 {
   155  	return bp.currentOffset
   156  }
   157  
   158  // IfdEnumerate is the main enumeration type. It knows how to parse the IFD
   159  // containers in the EXIF blob.
   160  type IfdEnumerate struct {
   161  	ebs            ExifBlobSeeker
   162  	byteOrder      binary.ByteOrder
   163  	tagIndex       *TagIndex
   164  	ifdMapping     *exifcommon.IfdMapping
   165  	furthestOffset uint32
   166  
   167  	visitedIfdOffsets map[uint32]struct{}
   168  }
   169  
   170  // NewIfdEnumerate returns a new instance of IfdEnumerate.
   171  func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ebs ExifBlobSeeker, byteOrder binary.ByteOrder) *IfdEnumerate {
   172  	return &IfdEnumerate{
   173  		ebs:        ebs,
   174  		byteOrder:  byteOrder,
   175  		ifdMapping: ifdMapping,
   176  		tagIndex:   tagIndex,
   177  
   178  		visitedIfdOffsets: make(map[uint32]struct{}),
   179  	}
   180  }
   181  
   182  func (ie *IfdEnumerate) getByteParser(ifdOffset uint32) (bp *byteParser, err error) {
   183  	defer func() {
   184  		if state := recover(); state != nil {
   185  			err = log.Wrap(state.(error))
   186  		}
   187  	}()
   188  
   189  	initialOffset := ExifAddressableAreaStart + ifdOffset
   190  
   191  	rs, err := ie.ebs.GetReadSeeker(int64(initialOffset))
   192  	log.PanicIf(err)
   193  
   194  	bp, err =
   195  		newByteParser(
   196  			rs,
   197  			ie.byteOrder,
   198  			initialOffset)
   199  
   200  	if err != nil {
   201  		if err == ErrOffsetInvalid {
   202  			return nil, err
   203  		}
   204  
   205  		log.Panic(err)
   206  	}
   207  
   208  	return bp, nil
   209  }
   210  
   211  func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp *byteParser) (ite *IfdTagEntry, err error) {
   212  	defer func() {
   213  		if state := recover(); state != nil {
   214  			err = log.Wrap(state.(error))
   215  		}
   216  	}()
   217  
   218  	tagId, _, err := bp.getUint16()
   219  	log.PanicIf(err)
   220  
   221  	tagTypeRaw, _, err := bp.getUint16()
   222  	log.PanicIf(err)
   223  
   224  	tagType := exifcommon.TagTypePrimitive(tagTypeRaw)
   225  
   226  	unitCount, _, err := bp.getUint32()
   227  	log.PanicIf(err)
   228  
   229  	valueOffset, rawValueOffset, err := bp.getUint32()
   230  	log.PanicIf(err)
   231  
   232  	// Check whether the embedded type indicator is valid.
   233  
   234  	if tagType.IsValid() == false {
   235  		// Technically, we have the type on-file in the tags-index, but
   236  		// if the type stored alongside the data disagrees with it,
   237  		// which it apparently does, all bets are off.
   238  		ifdEnumerateLogger.Warningf(nil,
   239  			"Tag (0x%04x) in IFD [%s] at position (%d) has invalid type (0x%04x) and will be skipped.",
   240  			tagId, ii, tagPosition, int(tagType))
   241  
   242  		ite = &IfdTagEntry{
   243  			tagId:   tagId,
   244  			tagType: tagType,
   245  		}
   246  
   247  		return ite, ErrTagTypeNotValid
   248  	}
   249  
   250  	// Check whether the embedded type is listed among the supported types for
   251  	// the registered tag. If not, skip processing the tag.
   252  
   253  	it, err := ie.tagIndex.Get(ii, tagId)
   254  	if err != nil {
   255  		if log.Is(err, ErrTagNotFound) == true {
   256  			ifdEnumerateLogger.Warningf(nil, "Tag (0x%04x) is not known and will be skipped.", tagId)
   257  
   258  			ite = &IfdTagEntry{
   259  				tagId: tagId,
   260  			}
   261  
   262  			return ite, ErrTagNotFound
   263  		}
   264  
   265  		log.Panic(err)
   266  	}
   267  
   268  	// If we're trying to be as forgiving as possible then use whatever type was
   269  	// reported in the format. Otherwise, only accept a type that's expected for
   270  	// this tag.
   271  	if ie.tagIndex.UniversalSearch() == false && it.DoesSupportType(tagType) == false {
   272  		// The type in the stream disagrees with the type that this tag is
   273  		// expected to have. This can present issues with how we handle the
   274  		// special-case tags (e.g. thumbnails, GPS, etc..) when those tags
   275  		// suddenly have data that we no longer manipulate correctly/
   276  		// accurately.
   277  		ifdEnumerateLogger.Warningf(nil,
   278  			"Tag (0x%04x) in IFD [%s] at position (%d) has unsupported type (0x%02x) and will be skipped.",
   279  			tagId, ii, tagPosition, int(tagType))
   280  
   281  		return nil, ErrTagTypeNotValid
   282  	}
   283  
   284  	// Construct tag struct.
   285  
   286  	rs, err := ie.ebs.GetReadSeeker(0)
   287  	log.PanicIf(err)
   288  
   289  	ite = newIfdTagEntry(
   290  		ii,
   291  		tagId,
   292  		tagPosition,
   293  		tagType,
   294  		unitCount,
   295  		valueOffset,
   296  		rawValueOffset,
   297  		rs,
   298  		ie.byteOrder)
   299  
   300  	ifdPath := ii.UnindexedString()
   301  
   302  	// If it's an IFD but not a standard one, it'll just be seen as a LONG
   303  	// (the standard IFD tag type), later, unless we skip it because it's
   304  	// [likely] not even in the standard list of known tags.
   305  	mi, err := ie.ifdMapping.GetChild(ifdPath, tagId)
   306  	if err == nil {
   307  		currentIfdTag := ii.IfdTag()
   308  
   309  		childIt := exifcommon.NewIfdTag(&currentIfdTag, tagId, mi.Name)
   310  		iiChild := ii.NewChild(childIt, 0)
   311  		ite.SetChildIfd(iiChild)
   312  
   313  		// We also need to set `tag.ChildFqIfdPath` but can't do it here
   314  		// because we don't have the IFD index.
   315  	} else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false {
   316  		log.Panic(err)
   317  	}
   318  
   319  	return ite, nil
   320  }
   321  
   322  // TagVisitorFn is called for each tag when enumerating through the EXIF.
   323  type TagVisitorFn func(ite *IfdTagEntry) (err error)
   324  
   325  // tagPostParse do some tag-level processing here following the parse of each.
   326  func (ie *IfdEnumerate) tagPostParse(ite *IfdTagEntry, med *MiscellaneousExifData) (err error) {
   327  	defer func() {
   328  		if state := recover(); state != nil {
   329  			err = log.Wrap(state.(error))
   330  		}
   331  	}()
   332  
   333  	// TODO(dustin): Add test
   334  
   335  	ii := ite.IfdIdentity()
   336  
   337  	tagId := ite.TagId()
   338  	tagType := ite.TagType()
   339  
   340  	it, err := ie.tagIndex.Get(ii, tagId)
   341  	if err == nil {
   342  		ite.setTagName(it.Name)
   343  	} else {
   344  		if err != ErrTagNotFound {
   345  			log.Panic(err)
   346  		}
   347  
   348  		// This is an unknown tag.
   349  
   350  		originalBt := exifcommon.BasicTag{
   351  			FqIfdPath: ii.String(),
   352  			IfdPath:   ii.UnindexedString(),
   353  			TagId:     tagId,
   354  		}
   355  
   356  		if med != nil {
   357  			med.unknownTags[originalBt] = exifcommon.BasicTag{}
   358  		}
   359  
   360  		utilityLogger.Debugf(nil,
   361  			"Tag (0x%04x) is not valid for IFD [%s]. Attempting secondary "+
   362  				"lookup.", tagId, ii.String())
   363  
   364  		// This will overwrite the existing `it` and `err`. Since `FindFirst()`
   365  		// might generate different Errors than `Get()`, the log message above
   366  		// is import to try and mitigate confusion in that case.
   367  		it, err = ie.tagIndex.FindFirst(tagId, tagType, nil)
   368  		if err != nil {
   369  			if err != ErrTagNotFound {
   370  				log.Panic(err)
   371  			}
   372  
   373  			// This is supposed to be a convenience function and if we were
   374  			// to keep the name empty or set it to some placeholder, it
   375  			// might be mismanaged by the package that is calling us. If
   376  			// they want to specifically manage these types of tags, they
   377  			// can use more advanced functionality to specifically -handle
   378  			// unknown tags.
   379  			utilityLogger.Warningf(nil,
   380  				"Tag with ID (0x%04x) in IFD [%s] is not recognized and "+
   381  					"will be ignored.", tagId, ii.String())
   382  
   383  			return ErrTagNotFound
   384  		}
   385  
   386  		ite.setTagName(it.Name)
   387  
   388  		utilityLogger.Warningf(nil,
   389  			"Tag with ID (0x%04x) is not valid for IFD [%s], but it *is* "+
   390  				"valid as tag [%s] under IFD [%s] and has the same type "+
   391  				"[%s], so we will use that. This EXIF blob was probably "+
   392  				"written by a buggy implementation.",
   393  			tagId, ii.UnindexedString(), it.Name, it.IfdPath,
   394  			tagType)
   395  
   396  		if med != nil {
   397  			med.unknownTags[originalBt] = exifcommon.BasicTag{
   398  				IfdPath: it.IfdPath,
   399  				TagId:   tagId,
   400  			}
   401  		}
   402  	}
   403  
   404  	// This is a known tag (from the standard, unless the user did
   405  	// something different).
   406  
   407  	// Skip any tags that have a type that doesn't match the type in the
   408  	// index (which is loaded with the standard and accept tag
   409  	// information unless configured otherwise).
   410  	//
   411  	// We've run into multiple instances of the same tag, where a) no
   412  	// tag should ever be repeated, and b) all but one had an incorrect
   413  	// type and caused parsing/conversion woes. So, this is a quick fix
   414  	// for those scenarios.
   415  	if ie.tagIndex.UniversalSearch() == false && it.DoesSupportType(tagType) == false {
   416  		ifdEnumerateLogger.Warningf(nil,
   417  			"Skipping tag [%s] (0x%04x) [%s] with an unexpected type: %v ∉ %v",
   418  			ii.UnindexedString(), tagId, it.Name,
   419  			tagType, it.SupportedTypes)
   420  
   421  		return ErrTagNotFound
   422  	}
   423  
   424  	return nil
   425  }
   426  
   427  // parseIfd decodes the IFD block that we're currently sitting on the first
   428  // byte of.
   429  func (ie *IfdEnumerate) parseIfd(ii *exifcommon.IfdIdentity, bp *byteParser, visitor TagVisitorFn, doDescend bool, med *MiscellaneousExifData) (nextIfdOffset uint32, entries []*IfdTagEntry, thumbnailData []byte, err error) {
   430  	defer func() {
   431  		if state := recover(); state != nil {
   432  			err = log.Wrap(state.(error))
   433  		}
   434  	}()
   435  
   436  	tagCount, _, err := bp.getUint16()
   437  	log.PanicIf(err)
   438  
   439  	ifdEnumerateLogger.Debugf(nil, "IFD [%s] tag-count: (%d)", ii.String(), tagCount)
   440  
   441  	entries = make([]*IfdTagEntry, 0)
   442  
   443  	var enumeratorThumbnailOffset *IfdTagEntry
   444  	var enumeratorThumbnailSize *IfdTagEntry
   445  
   446  	for i := 0; i < int(tagCount); i++ {
   447  		ite, err := ie.parseTag(ii, i, bp)
   448  		if err != nil {
   449  			if log.Is(err, ErrTagNotFound) == true || log.Is(err, ErrTagTypeNotValid) == true {
   450  				// These tags should've been fully logged in parseTag(). The
   451  				// ITE returned is nil so we can't print anything about them, now.
   452  				continue
   453  			}
   454  
   455  			log.Panic(err)
   456  		}
   457  
   458  		err = ie.tagPostParse(ite, med)
   459  		if err == nil {
   460  			if err == ErrTagNotFound {
   461  				continue
   462  			}
   463  
   464  			log.PanicIf(err)
   465  		}
   466  
   467  		tagId := ite.TagId()
   468  
   469  		if visitor != nil {
   470  			err := visitor(ite)
   471  			log.PanicIf(err)
   472  		}
   473  
   474  		if ite.IsThumbnailOffset() == true {
   475  			ifdEnumerateLogger.Debugf(nil, "Skipping the thumbnail offset tag (0x%04x). Use accessors to get it or set it.", tagId)
   476  
   477  			enumeratorThumbnailOffset = ite
   478  			entries = append(entries, ite)
   479  
   480  			continue
   481  		} else if ite.IsThumbnailSize() == true {
   482  			ifdEnumerateLogger.Debugf(nil, "Skipping the thumbnail size tag (0x%04x). Use accessors to get it or set it.", tagId)
   483  
   484  			enumeratorThumbnailSize = ite
   485  			entries = append(entries, ite)
   486  
   487  			continue
   488  		}
   489  
   490  		if ite.TagType() != exifcommon.TypeUndefined {
   491  			// If this tag's value is an offset, bump our max-offset value to
   492  			// what that offset is plus however large that value is.
   493  
   494  			vc := ite.getValueContext()
   495  
   496  			farOffset, err := vc.GetFarOffset()
   497  			if err == nil {
   498  				candidateOffset := farOffset + uint32(vc.SizeInBytes())
   499  				if candidateOffset > ie.furthestOffset {
   500  					ie.furthestOffset = candidateOffset
   501  				}
   502  			} else if err != exifcommon.ErrNotFarValue {
   503  				log.PanicIf(err)
   504  			}
   505  		}
   506  
   507  		// If it's an IFD but not a standard one, it'll just be seen as a LONG
   508  		// (the standard IFD tag type), later, unless we skip it because it's
   509  		// [likely] not even in the standard list of known tags.
   510  		if ite.ChildIfdPath() != "" {
   511  			if doDescend == true {
   512  				ifdEnumerateLogger.Debugf(nil, "Descending from IFD [%s] to IFD [%s].", ii, ite.ChildIfdPath())
   513  
   514  				currentIfdTag := ii.IfdTag()
   515  
   516  				childIfdTag :=
   517  					exifcommon.NewIfdTag(
   518  						&currentIfdTag,
   519  						ite.TagId(),
   520  						ite.ChildIfdName())
   521  
   522  				iiChild := ii.NewChild(childIfdTag, 0)
   523  
   524  				err := ie.scan(iiChild, ite.getValueOffset(), visitor, med)
   525  				log.PanicIf(err)
   526  
   527  				ifdEnumerateLogger.Debugf(nil, "Ascending from IFD [%s] to IFD [%s].", ite.ChildIfdPath(), ii)
   528  			}
   529  		}
   530  
   531  		entries = append(entries, ite)
   532  	}
   533  
   534  	if enumeratorThumbnailOffset != nil && enumeratorThumbnailSize != nil {
   535  		thumbnailData, err = ie.parseThumbnail(enumeratorThumbnailOffset, enumeratorThumbnailSize)
   536  		if err != nil {
   537  			ifdEnumerateLogger.Errorf(
   538  				nil, err,
   539  				"We tried to bump our furthest-offset counter but there was an issue first seeking past the thumbnail.")
   540  		} else {
   541  			// In this case, the value is always an offset.
   542  			offset := enumeratorThumbnailOffset.getValueOffset()
   543  
   544  			// This this case, the value is always a length.
   545  			length := enumeratorThumbnailSize.getValueOffset()
   546  
   547  			ifdEnumerateLogger.Debugf(nil, "Found thumbnail in IFD [%s]. Its offset is (%d) and is (%d) bytes.", ii, offset, length)
   548  
   549  			furthestOffset := offset + length
   550  
   551  			if furthestOffset > ie.furthestOffset {
   552  				ie.furthestOffset = furthestOffset
   553  			}
   554  		}
   555  	}
   556  
   557  	nextIfdOffset, _, err = bp.getUint32()
   558  	log.PanicIf(err)
   559  
   560  	_, alreadyVisited := ie.visitedIfdOffsets[nextIfdOffset]
   561  
   562  	if alreadyVisited == true {
   563  		ifdEnumerateLogger.Warningf(nil, "IFD at offset (0x%08x) has been linked-to more than once. There might be a cycle in the IFD chain. Not reparsing.", nextIfdOffset)
   564  		nextIfdOffset = 0
   565  	}
   566  
   567  	if nextIfdOffset != 0 {
   568  		ie.visitedIfdOffsets[nextIfdOffset] = struct{}{}
   569  		ifdEnumerateLogger.Debugf(nil, "[%s] Next IFD at offset: (0x%08x)", ii.String(), nextIfdOffset)
   570  	} else {
   571  		ifdEnumerateLogger.Debugf(nil, "[%s] IFD chain has terminated.", ii.String())
   572  	}
   573  
   574  	return nextIfdOffset, entries, thumbnailData, nil
   575  }
   576  
   577  func (ie *IfdEnumerate) parseThumbnail(offsetIte, lengthIte *IfdTagEntry) (thumbnailData []byte, err error) {
   578  	defer func() {
   579  		if state := recover(); state != nil {
   580  			err = log.Wrap(state.(error))
   581  		}
   582  	}()
   583  
   584  	vRaw, err := lengthIte.Value()
   585  	log.PanicIf(err)
   586  
   587  	vList := vRaw.([]uint32)
   588  	if len(vList) != 1 {
   589  		log.Panicf("not exactly one long: (%d)", len(vList))
   590  	}
   591  
   592  	length := vList[0]
   593  
   594  	// The tag is official a LONG type, but it's actually an offset to a blob of bytes.
   595  	offsetIte.updateTagType(exifcommon.TypeByte)
   596  	offsetIte.updateUnitCount(length)
   597  
   598  	thumbnailData, err = offsetIte.GetRawBytes()
   599  	log.PanicIf(err)
   600  
   601  	return thumbnailData, nil
   602  }
   603  
   604  // scan parses and enumerates the different IFD blocks and invokes a visitor
   605  // callback for each tag. No information is kept or returned.
   606  func (ie *IfdEnumerate) scan(iiGeneral *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn, med *MiscellaneousExifData) (err error) {
   607  	defer func() {
   608  		if state := recover(); state != nil {
   609  			err = log.Wrap(state.(error))
   610  		}
   611  	}()
   612  
   613  	// TODO(dustin): Add test
   614  
   615  	for ifdIndex := 0; ; ifdIndex++ {
   616  		iiSibling := iiGeneral.NewSibling(ifdIndex)
   617  
   618  		ifdEnumerateLogger.Debugf(nil, "Parsing IFD [%s] at offset (0x%04x) (scan).", iiSibling.String(), ifdOffset)
   619  
   620  		bp, err := ie.getByteParser(ifdOffset)
   621  		if err != nil {
   622  			if err == ErrOffsetInvalid {
   623  				ifdEnumerateLogger.Errorf(nil, nil, "IFD [%s] at offset (0x%04x) is unreachable. Terminating scan.", iiSibling.String(), ifdOffset)
   624  				break
   625  			}
   626  
   627  			log.Panic(err)
   628  		}
   629  
   630  		nextIfdOffset, _, _, err := ie.parseIfd(iiSibling, bp, visitor, true, med)
   631  		log.PanicIf(err)
   632  
   633  		currentOffset := bp.CurrentOffset()
   634  		if currentOffset > ie.furthestOffset {
   635  			ie.furthestOffset = currentOffset
   636  		}
   637  
   638  		if nextIfdOffset == 0 {
   639  			break
   640  		}
   641  
   642  		ifdOffset = nextIfdOffset
   643  	}
   644  
   645  	return nil
   646  }
   647  
   648  // MiscellaneousExifData is reports additional data collected during the parse.
   649  type MiscellaneousExifData struct {
   650  	// UnknownTags contains all tags that were invalid for their containing
   651  	// IFDs. The values represent alternative IFDs that were correctly matched
   652  	// to those tags and used instead.
   653  	unknownTags map[exifcommon.BasicTag]exifcommon.BasicTag
   654  }
   655  
   656  // UnknownTags returns the unknown tags encountered during the scan.
   657  func (med *MiscellaneousExifData) UnknownTags() map[exifcommon.BasicTag]exifcommon.BasicTag {
   658  	return med.unknownTags
   659  }
   660  
   661  // ScanOptions tweaks parser behavior/choices.
   662  type ScanOptions struct {
   663  	// NOTE(dustin): Reserved for future usage.
   664  }
   665  
   666  // Scan enumerates the different EXIF blocks (called IFDs). `rootIfdName` will
   667  // be "IFD" in the TIFF standard.
   668  func (ie *IfdEnumerate) Scan(iiRoot *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn, so *ScanOptions) (med *MiscellaneousExifData, err error) {
   669  	defer func() {
   670  		if state := recover(); state != nil {
   671  			err = log.Wrap(state.(error))
   672  		}
   673  	}()
   674  
   675  	// TODO(dustin): Add test
   676  
   677  	med = &MiscellaneousExifData{
   678  		unknownTags: make(map[exifcommon.BasicTag]exifcommon.BasicTag),
   679  	}
   680  
   681  	err = ie.scan(iiRoot, ifdOffset, visitor, med)
   682  	log.PanicIf(err)
   683  
   684  	ifdEnumerateLogger.Debugf(nil, "Scan: It looks like the furthest offset that contained EXIF data in the EXIF blob was (%d) (Scan).", ie.FurthestOffset())
   685  
   686  	return med, nil
   687  }
   688  
   689  // Ifd represents a single, parsed IFD.
   690  type Ifd struct {
   691  	ifdIdentity *exifcommon.IfdIdentity
   692  
   693  	ifdMapping *exifcommon.IfdMapping
   694  	tagIndex   *TagIndex
   695  
   696  	offset    uint32
   697  	byteOrder binary.ByteOrder
   698  	id        int
   699  
   700  	parentIfd *Ifd
   701  
   702  	// ParentTagIndex is our tag position in the parent IFD, if we had a parent
   703  	// (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
   704  	// instead of as a child).
   705  	parentTagIndex int
   706  
   707  	entries        []*IfdTagEntry
   708  	entriesByTagId map[uint16][]*IfdTagEntry
   709  
   710  	children      []*Ifd
   711  	childIfdIndex map[string]*Ifd
   712  
   713  	thumbnailData []byte
   714  
   715  	nextIfdOffset uint32
   716  	nextIfd       *Ifd
   717  }
   718  
   719  // IfdIdentity returns IFD identity that this struct represents.
   720  func (ifd *Ifd) IfdIdentity() *exifcommon.IfdIdentity {
   721  	return ifd.ifdIdentity
   722  }
   723  
   724  // Entries returns a flat list of all tags for this IFD.
   725  func (ifd *Ifd) Entries() []*IfdTagEntry {
   726  
   727  	// TODO(dustin): Add test
   728  
   729  	return ifd.entries
   730  }
   731  
   732  // EntriesByTagId returns a map of all tags for this IFD.
   733  func (ifd *Ifd) EntriesByTagId() map[uint16][]*IfdTagEntry {
   734  
   735  	// TODO(dustin): Add test
   736  
   737  	return ifd.entriesByTagId
   738  }
   739  
   740  // Children returns a flat list of all child IFDs of this IFD.
   741  func (ifd *Ifd) Children() []*Ifd {
   742  
   743  	// TODO(dustin): Add test
   744  
   745  	return ifd.children
   746  }
   747  
   748  // ChildWithIfdPath returns a map of all child IFDs of this IFD.
   749  func (ifd *Ifd) ChildIfdIndex() map[string]*Ifd {
   750  
   751  	// TODO(dustin): Add test
   752  
   753  	return ifd.childIfdIndex
   754  }
   755  
   756  // ParentTagIndex returns the position of this IFD's tag in its parent IFD (*if*
   757  // there is a parent).
   758  func (ifd *Ifd) ParentTagIndex() int {
   759  
   760  	// TODO(dustin): Add test
   761  
   762  	return ifd.parentTagIndex
   763  }
   764  
   765  // Offset returns the offset of the IFD in the stream.
   766  func (ifd *Ifd) Offset() uint32 {
   767  
   768  	// TODO(dustin): Add test
   769  
   770  	return ifd.offset
   771  }
   772  
   773  // Offset returns the offset of the IFD in the stream.
   774  func (ifd *Ifd) ByteOrder() binary.ByteOrder {
   775  
   776  	// TODO(dustin): Add test
   777  
   778  	return ifd.byteOrder
   779  }
   780  
   781  // NextIfd returns the Ifd struct for the next IFD in the chain.
   782  func (ifd *Ifd) NextIfd() *Ifd {
   783  
   784  	// TODO(dustin): Add test
   785  
   786  	return ifd.nextIfd
   787  }
   788  
   789  // ChildWithIfdPath returns an `Ifd` struct for the given child of the current
   790  // IFD.
   791  func (ifd *Ifd) ChildWithIfdPath(iiChild *exifcommon.IfdIdentity) (childIfd *Ifd, err error) {
   792  	defer func() {
   793  		if state := recover(); state != nil {
   794  			err = log.Wrap(state.(error))
   795  		}
   796  	}()
   797  
   798  	// TODO(dustin): This is a bridge while we're introducing the IFD type-system. We should be able to use the (IfdIdentity).Equals() method for this.
   799  	ifdPath := iiChild.UnindexedString()
   800  
   801  	for _, childIfd := range ifd.children {
   802  		if childIfd.ifdIdentity.UnindexedString() == ifdPath {
   803  			return childIfd, nil
   804  		}
   805  	}
   806  
   807  	log.Panic(ErrTagNotFound)
   808  	return nil, nil
   809  }
   810  
   811  // FindTagWithId returns a list of tags (usually just zero or one) that match
   812  // the given tag ID. This is efficient.
   813  func (ifd *Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) {
   814  	defer func() {
   815  		if state := recover(); state != nil {
   816  			err = log.Wrap(state.(error))
   817  		}
   818  	}()
   819  
   820  	results, found := ifd.entriesByTagId[tagId]
   821  	if found != true {
   822  		log.Panic(ErrTagNotFound)
   823  	}
   824  
   825  	return results, nil
   826  }
   827  
   828  // FindTagWithName returns a list of tags (usually just zero or one) that match
   829  // the given tag name. This is not efficient (though the labor is trivial).
   830  func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err error) {
   831  	defer func() {
   832  		if state := recover(); state != nil {
   833  			err = log.Wrap(state.(error))
   834  		}
   835  	}()
   836  
   837  	it, err := ifd.tagIndex.GetWithName(ifd.ifdIdentity, tagName)
   838  	if log.Is(err, ErrTagNotFound) == true {
   839  		log.Panic(ErrTagNotKnown)
   840  	} else if err != nil {
   841  		log.Panic(err)
   842  	}
   843  
   844  	results = make([]*IfdTagEntry, 0)
   845  	for _, ite := range ifd.entries {
   846  		if ite.TagId() == it.Id {
   847  			results = append(results, ite)
   848  		}
   849  	}
   850  
   851  	if len(results) == 0 {
   852  		log.Panic(ErrTagNotFound)
   853  	}
   854  
   855  	return results, nil
   856  }
   857  
   858  // String returns a description string.
   859  func (ifd *Ifd) String() string {
   860  	parentOffset := uint32(0)
   861  	if ifd.parentIfd != nil {
   862  		parentOffset = ifd.parentIfd.offset
   863  	}
   864  
   865  	return fmt.Sprintf("Ifd<ID=(%d) IFD-PATH=[%s] INDEX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)>", ifd.id, ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index(), len(ifd.entries), ifd.offset, len(ifd.children), parentOffset, ifd.nextIfdOffset)
   866  }
   867  
   868  // Thumbnail returns the raw thumbnail bytes. This is typically directly
   869  // readable by any standard image viewer.
   870  func (ifd *Ifd) Thumbnail() (data []byte, err error) {
   871  
   872  	if ifd.thumbnailData == nil {
   873  		return nil, ErrNoThumbnail
   874  	}
   875  
   876  	return ifd.thumbnailData, nil
   877  }
   878  
   879  // dumpTags recursively builds a list of tags from an IFD.
   880  func (ifd *Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
   881  	if tags == nil {
   882  		tags = make([]*IfdTagEntry, 0)
   883  	}
   884  
   885  	// Now, print the tags while also descending to child-IFDS as we encounter them.
   886  
   887  	ifdsFoundCount := 0
   888  
   889  	for _, ite := range ifd.entries {
   890  		tags = append(tags, ite)
   891  
   892  		childIfdPath := ite.ChildIfdPath()
   893  		if childIfdPath != "" {
   894  			ifdsFoundCount++
   895  
   896  			childIfd, found := ifd.childIfdIndex[childIfdPath]
   897  			if found != true {
   898  				log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
   899  			}
   900  
   901  			tags = childIfd.dumpTags(tags)
   902  		}
   903  	}
   904  
   905  	if len(ifd.children) != ifdsFoundCount {
   906  		log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
   907  	}
   908  
   909  	if ifd.nextIfd != nil {
   910  		tags = ifd.nextIfd.dumpTags(tags)
   911  	}
   912  
   913  	return tags
   914  }
   915  
   916  // DumpTags prints the IFD hierarchy.
   917  func (ifd *Ifd) DumpTags() []*IfdTagEntry {
   918  	return ifd.dumpTags(nil)
   919  }
   920  
   921  func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink bool) {
   922  	indent := strings.Repeat(" ", level*2)
   923  
   924  	prefix := " "
   925  	if nextLink {
   926  		prefix = ">"
   927  	}
   928  
   929  	fmt.Printf("%s%sIFD: %s\n", indent, prefix, ifd)
   930  
   931  	// Now, print the tags while also descending to child-IFDS as we encounter them.
   932  
   933  	ifdsFoundCount := 0
   934  
   935  	for _, ite := range ifd.entries {
   936  		if ite.ChildIfdPath() != "" {
   937  			fmt.Printf("%s - TAG: %s\n", indent, ite)
   938  		} else {
   939  			// This will just add noise to the output (byte-tags are fully
   940  			// dumped).
   941  			if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() == true {
   942  				continue
   943  			}
   944  
   945  			it, err := ifd.tagIndex.Get(ifd.ifdIdentity, ite.TagId())
   946  
   947  			tagName := ""
   948  			if err == nil {
   949  				tagName = it.Name
   950  			}
   951  
   952  			var valuePhrase string
   953  			if populateValues == true {
   954  				var err error
   955  
   956  				valuePhrase, err = ite.Format()
   957  				if err != nil {
   958  					if log.Is(err, exifcommon.ErrUnhandledUndefinedTypedTag) == true {
   959  						ifdEnumerateLogger.Warningf(nil, "Skipping non-standard undefined tag: [%s] (%04x)", ifd.ifdIdentity.UnindexedString(), ite.TagId())
   960  						continue
   961  					} else if err == exifundefined.ErrUnparseableValue {
   962  						ifdEnumerateLogger.Warningf(nil, "Skipping unparseable undefined tag: [%s] (%04x) [%s]", ifd.ifdIdentity.UnindexedString(), ite.TagId(), it.Name)
   963  						continue
   964  					}
   965  
   966  					log.Panic(err)
   967  				}
   968  			} else {
   969  				valuePhrase = "!UNRESOLVED"
   970  			}
   971  
   972  			fmt.Printf("%s - TAG: %s NAME=[%s] VALUE=[%v]\n", indent, ite, tagName, valuePhrase)
   973  		}
   974  
   975  		childIfdPath := ite.ChildIfdPath()
   976  		if childIfdPath != "" {
   977  			ifdsFoundCount++
   978  
   979  			childIfd, found := ifd.childIfdIndex[childIfdPath]
   980  			if found != true {
   981  				log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
   982  			}
   983  
   984  			childIfd.printTagTree(populateValues, 0, level+1, false)
   985  		}
   986  	}
   987  
   988  	if len(ifd.children) != ifdsFoundCount {
   989  		log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
   990  	}
   991  
   992  	if ifd.nextIfd != nil {
   993  		ifd.nextIfd.printTagTree(populateValues, index+1, level, true)
   994  	}
   995  }
   996  
   997  // PrintTagTree prints the IFD hierarchy.
   998  func (ifd *Ifd) PrintTagTree(populateValues bool) {
   999  	ifd.printTagTree(populateValues, 0, 0, false)
  1000  }
  1001  
  1002  func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
  1003  	indent := strings.Repeat(" ", level*2)
  1004  
  1005  	prefix := " "
  1006  	if nextLink {
  1007  		prefix = ">"
  1008  	}
  1009  
  1010  	fmt.Printf("%s%s%s\n", indent, prefix, ifd)
  1011  
  1012  	// Now, print the tags while also descending to child-IFDS as we encounter them.
  1013  
  1014  	ifdsFoundCount := 0
  1015  
  1016  	for _, ite := range ifd.entries {
  1017  		childIfdPath := ite.ChildIfdPath()
  1018  		if childIfdPath != "" {
  1019  			ifdsFoundCount++
  1020  
  1021  			childIfd, found := ifd.childIfdIndex[childIfdPath]
  1022  			if found != true {
  1023  				log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
  1024  			}
  1025  
  1026  			childIfd.printIfdTree(level+1, false)
  1027  		}
  1028  	}
  1029  
  1030  	if len(ifd.children) != ifdsFoundCount {
  1031  		log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
  1032  	}
  1033  
  1034  	if ifd.nextIfd != nil {
  1035  		ifd.nextIfd.printIfdTree(level, true)
  1036  	}
  1037  }
  1038  
  1039  // PrintIfdTree prints the IFD hierarchy.
  1040  func (ifd *Ifd) PrintIfdTree() {
  1041  	ifd.printIfdTree(0, false)
  1042  }
  1043  
  1044  func (ifd *Ifd) dumpTree(tagsDump []string, level int) []string {
  1045  	if tagsDump == nil {
  1046  		tagsDump = make([]string, 0)
  1047  	}
  1048  
  1049  	indent := strings.Repeat(" ", level*2)
  1050  
  1051  	var ifdPhrase string
  1052  	if ifd.parentIfd != nil {
  1053  		ifdPhrase = fmt.Sprintf("[%s]->[%s]:(%d)", ifd.parentIfd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index())
  1054  	} else {
  1055  		ifdPhrase = fmt.Sprintf("[ROOT]->[%s]:(%d)", ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index())
  1056  	}
  1057  
  1058  	startBlurb := fmt.Sprintf("%s> IFD %s TOP", indent, ifdPhrase)
  1059  	tagsDump = append(tagsDump, startBlurb)
  1060  
  1061  	ifdsFoundCount := 0
  1062  	for _, ite := range ifd.entries {
  1063  		tagsDump = append(tagsDump, fmt.Sprintf("%s  - (0x%04x)", indent, ite.TagId()))
  1064  
  1065  		childIfdPath := ite.ChildIfdPath()
  1066  		if childIfdPath != "" {
  1067  			ifdsFoundCount++
  1068  
  1069  			childIfd, found := ifd.childIfdIndex[childIfdPath]
  1070  			if found != true {
  1071  				log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
  1072  			}
  1073  
  1074  			tagsDump = childIfd.dumpTree(tagsDump, level+1)
  1075  		}
  1076  	}
  1077  
  1078  	if len(ifd.children) != ifdsFoundCount {
  1079  		log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
  1080  	}
  1081  
  1082  	finishBlurb := fmt.Sprintf("%s< IFD %s BOTTOM", indent, ifdPhrase)
  1083  	tagsDump = append(tagsDump, finishBlurb)
  1084  
  1085  	if ifd.nextIfd != nil {
  1086  		siblingBlurb := fmt.Sprintf("%s* LINKING TO SIBLING IFD [%s]:(%d)", indent, ifd.nextIfd.ifdIdentity.UnindexedString(), ifd.nextIfd.ifdIdentity.Index())
  1087  		tagsDump = append(tagsDump, siblingBlurb)
  1088  
  1089  		tagsDump = ifd.nextIfd.dumpTree(tagsDump, level)
  1090  	}
  1091  
  1092  	return tagsDump
  1093  }
  1094  
  1095  // DumpTree returns a list of strings describing the IFD hierarchy.
  1096  func (ifd *Ifd) DumpTree() []string {
  1097  	return ifd.dumpTree(nil, 0)
  1098  }
  1099  
  1100  // GpsInfo parses and consolidates the GPS info. This can only be called on the
  1101  // GPS IFD.
  1102  func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
  1103  	defer func() {
  1104  		if state := recover(); state != nil {
  1105  			err = log.Wrap(state.(error))
  1106  		}
  1107  	}()
  1108  
  1109  	gi = new(GpsInfo)
  1110  
  1111  	if ifd.ifdIdentity.Equals(exifcommon.IfdGpsInfoStandardIfdIdentity) == false {
  1112  		log.Panicf("GPS can only be read on GPS IFD: [%s]", ifd.ifdIdentity.UnindexedString())
  1113  	}
  1114  
  1115  	if tags, found := ifd.entriesByTagId[TagGpsVersionId]; found == false {
  1116  		// We've seen this. We'll just have to default to assuming we're in a
  1117  		// 2.2.0.0 format.
  1118  		ifdEnumerateLogger.Warningf(nil, "No GPS version tag (0x%04x) found.", TagGpsVersionId)
  1119  	} else {
  1120  		versionBytes, err := tags[0].GetRawBytes()
  1121  		log.PanicIf(err)
  1122  
  1123  		hit := false
  1124  		for _, acceptedGpsVersion := range ValidGpsVersions {
  1125  			if bytes.Compare(versionBytes, acceptedGpsVersion[:]) == 0 {
  1126  				hit = true
  1127  				break
  1128  			}
  1129  		}
  1130  
  1131  		if hit != true {
  1132  			ifdEnumerateLogger.Warningf(nil, "GPS version not supported: %v", versionBytes)
  1133  			log.Panic(ErrNoGpsTags)
  1134  		}
  1135  	}
  1136  
  1137  	tags, found := ifd.entriesByTagId[TagLatitudeId]
  1138  	if found == false {
  1139  		ifdEnumerateLogger.Warningf(nil, "latitude not found")
  1140  		log.Panic(ErrNoGpsTags)
  1141  	}
  1142  
  1143  	latitudeValue, err := tags[0].Value()
  1144  	log.PanicIf(err)
  1145  
  1146  	// Look for whether North or South.
  1147  	tags, found = ifd.entriesByTagId[TagLatitudeRefId]
  1148  	if found == false {
  1149  		ifdEnumerateLogger.Warningf(nil, "latitude-ref not found")
  1150  		log.Panic(ErrNoGpsTags)
  1151  	}
  1152  
  1153  	latitudeRefValue, err := tags[0].Value()
  1154  	log.PanicIf(err)
  1155  
  1156  	tags, found = ifd.entriesByTagId[TagLongitudeId]
  1157  	if found == false {
  1158  		ifdEnumerateLogger.Warningf(nil, "longitude not found")
  1159  		log.Panic(ErrNoGpsTags)
  1160  	}
  1161  
  1162  	longitudeValue, err := tags[0].Value()
  1163  	log.PanicIf(err)
  1164  
  1165  	// Look for whether West or East.
  1166  	tags, found = ifd.entriesByTagId[TagLongitudeRefId]
  1167  	if found == false {
  1168  		ifdEnumerateLogger.Warningf(nil, "longitude-ref not found")
  1169  		log.Panic(ErrNoGpsTags)
  1170  	}
  1171  
  1172  	longitudeRefValue, err := tags[0].Value()
  1173  	log.PanicIf(err)
  1174  
  1175  	// Parse location.
  1176  
  1177  	latitudeRaw := latitudeValue.([]exifcommon.Rational)
  1178  
  1179  	gi.Latitude, err = NewGpsDegreesFromRationals(latitudeRefValue.(string), latitudeRaw)
  1180  	log.PanicIf(err)
  1181  
  1182  	longitudeRaw := longitudeValue.([]exifcommon.Rational)
  1183  
  1184  	gi.Longitude, err = NewGpsDegreesFromRationals(longitudeRefValue.(string), longitudeRaw)
  1185  	log.PanicIf(err)
  1186  
  1187  	// Parse altitude.
  1188  
  1189  	altitudeTags, foundAltitude := ifd.entriesByTagId[TagAltitudeId]
  1190  	altitudeRefTags, foundAltitudeRef := ifd.entriesByTagId[TagAltitudeRefId]
  1191  
  1192  	if foundAltitude == true && foundAltitudeRef == true {
  1193  		altitudePhrase, err := altitudeTags[0].Format()
  1194  		log.PanicIf(err)
  1195  
  1196  		ifdEnumerateLogger.Debugf(nil, "Altitude is [%s].", altitudePhrase)
  1197  
  1198  		altitudeValue, err := altitudeTags[0].Value()
  1199  		log.PanicIf(err)
  1200  
  1201  		altitudeRefPhrase, err := altitudeRefTags[0].Format()
  1202  		log.PanicIf(err)
  1203  
  1204  		ifdEnumerateLogger.Debugf(nil, "Altitude-reference is [%s].", altitudeRefPhrase)
  1205  
  1206  		altitudeRefValue, err := altitudeRefTags[0].Value()
  1207  		log.PanicIf(err)
  1208  
  1209  		altitudeRaw := altitudeValue.([]exifcommon.Rational)
  1210  		if altitudeRaw[0].Denominator > 0 {
  1211  			altitude := int(altitudeRaw[0].Numerator / altitudeRaw[0].Denominator)
  1212  
  1213  			if altitudeRefValue.([]byte)[0] == 1 {
  1214  				altitude *= -1
  1215  			}
  1216  
  1217  			gi.Altitude = altitude
  1218  		}
  1219  	}
  1220  
  1221  	// Parse timestamp from separate date and time tags.
  1222  
  1223  	timestampTags, foundTimestamp := ifd.entriesByTagId[TagTimestampId]
  1224  	datestampTags, foundDatestamp := ifd.entriesByTagId[TagDatestampId]
  1225  
  1226  	if foundTimestamp == true && foundDatestamp == true {
  1227  		datestampValue, err := datestampTags[0].Value()
  1228  		log.PanicIf(err)
  1229  
  1230  		datePhrase := datestampValue.(string)
  1231  		ifdEnumerateLogger.Debugf(nil, "Date tag value is [%s].", datePhrase)
  1232  
  1233  		// Normalize the separators.
  1234  		datePhrase = strings.ReplaceAll(datePhrase, "-", ":")
  1235  
  1236  		dateParts := strings.Split(datePhrase, ":")
  1237  
  1238  		year, err1 := strconv.ParseUint(dateParts[0], 10, 16)
  1239  		month, err2 := strconv.ParseUint(dateParts[1], 10, 8)
  1240  		day, err3 := strconv.ParseUint(dateParts[2], 10, 8)
  1241  
  1242  		if err1 == nil && err2 == nil && err3 == nil {
  1243  			timestampValue, err := timestampTags[0].Value()
  1244  			log.PanicIf(err)
  1245  
  1246  			timePhrase, err := timestampTags[0].Format()
  1247  			log.PanicIf(err)
  1248  
  1249  			ifdEnumerateLogger.Debugf(nil, "Time tag value is [%s].", timePhrase)
  1250  
  1251  			timestampRaw := timestampValue.([]exifcommon.Rational)
  1252  
  1253  			hour := int(timestampRaw[0].Numerator / timestampRaw[0].Denominator)
  1254  			minute := int(timestampRaw[1].Numerator / timestampRaw[1].Denominator)
  1255  			second := int(timestampRaw[2].Numerator / timestampRaw[2].Denominator)
  1256  
  1257  			gi.Timestamp = time.Date(int(year), time.Month(month), int(day), hour, minute, second, 0, time.UTC)
  1258  		}
  1259  	}
  1260  
  1261  	return gi, nil
  1262  }
  1263  
  1264  // ParsedTagVisitor is a callback used if wanting to visit through all tags and
  1265  // child IFDs from the current IFD and going down.
  1266  type ParsedTagVisitor func(*Ifd, *IfdTagEntry) error
  1267  
  1268  // EnumerateTagsRecursively calls the given visitor function for every tag and
  1269  // IFD in the current IFD, recursively.
  1270  func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error) {
  1271  	defer func() {
  1272  		if state := recover(); state != nil {
  1273  			err = log.Wrap(state.(error))
  1274  		}
  1275  	}()
  1276  
  1277  	for ptr := ifd; ptr != nil; ptr = ptr.nextIfd {
  1278  		for _, ite := range ifd.entries {
  1279  			childIfdPath := ite.ChildIfdPath()
  1280  			if childIfdPath != "" {
  1281  				childIfd := ifd.childIfdIndex[childIfdPath]
  1282  
  1283  				err := childIfd.EnumerateTagsRecursively(visitor)
  1284  				log.PanicIf(err)
  1285  			} else {
  1286  				err := visitor(ifd, ite)
  1287  				log.PanicIf(err)
  1288  			}
  1289  		}
  1290  	}
  1291  
  1292  	return nil
  1293  }
  1294  
  1295  // QueuedIfd is one IFD that has been identified but yet to be processed.
  1296  type QueuedIfd struct {
  1297  	IfdIdentity *exifcommon.IfdIdentity
  1298  
  1299  	Offset uint32
  1300  	Parent *Ifd
  1301  
  1302  	// ParentTagIndex is our tag position in the parent IFD, if we had a parent
  1303  	// (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
  1304  	// instead of as a child).
  1305  	ParentTagIndex int
  1306  }
  1307  
  1308  // IfdIndex collects a bunch of IFD and tag information stored in several
  1309  // different ways in order to provide convenient lookups.
  1310  type IfdIndex struct {
  1311  	RootIfd *Ifd
  1312  	Ifds    []*Ifd
  1313  	Tree    map[int]*Ifd
  1314  	Lookup  map[string]*Ifd
  1315  }
  1316  
  1317  // Collect enumerates the different EXIF blocks (called IFDs) and builds out an
  1318  // index struct for referencing all of the parsed data.
  1319  func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error) {
  1320  	defer func() {
  1321  		if state := recover(); state != nil {
  1322  			err = log.Wrap(state.(error))
  1323  		}
  1324  	}()
  1325  
  1326  	// TODO(dustin): Add MiscellaneousExifData to IfdIndex
  1327  
  1328  	tree := make(map[int]*Ifd)
  1329  	ifds := make([]*Ifd, 0)
  1330  	lookup := make(map[string]*Ifd)
  1331  
  1332  	queue := []QueuedIfd{
  1333  		{
  1334  			IfdIdentity: exifcommon.IfdStandardIfdIdentity,
  1335  			Offset:      rootIfdOffset,
  1336  		},
  1337  	}
  1338  
  1339  	edges := make(map[uint32]*Ifd)
  1340  
  1341  	for {
  1342  		if len(queue) == 0 {
  1343  			break
  1344  		}
  1345  
  1346  		qi := queue[0]
  1347  		ii := qi.IfdIdentity
  1348  
  1349  		offset := qi.Offset
  1350  		parentIfd := qi.Parent
  1351  
  1352  		queue = queue[1:]
  1353  
  1354  		ifdEnumerateLogger.Debugf(nil, "Parsing IFD [%s] (%d) at offset (0x%04x) (Collect).", ii.String(), ii.Index(), offset)
  1355  
  1356  		bp, err := ie.getByteParser(offset)
  1357  		if err != nil {
  1358  			if err == ErrOffsetInvalid {
  1359  				return index, err
  1360  			}
  1361  
  1362  			log.Panic(err)
  1363  		}
  1364  
  1365  		// TODO(dustin): We don't need to pass the index in as a separate argument. Get from the II.
  1366  
  1367  		nextIfdOffset, entries, thumbnailData, err := ie.parseIfd(ii, bp, nil, false, nil)
  1368  		log.PanicIf(err)
  1369  
  1370  		currentOffset := bp.CurrentOffset()
  1371  		if currentOffset > ie.furthestOffset {
  1372  			ie.furthestOffset = currentOffset
  1373  		}
  1374  
  1375  		id := len(ifds)
  1376  
  1377  		entriesByTagId := make(map[uint16][]*IfdTagEntry)
  1378  		for _, ite := range entries {
  1379  			tagId := ite.TagId()
  1380  
  1381  			tags, found := entriesByTagId[tagId]
  1382  			if found == false {
  1383  				tags = make([]*IfdTagEntry, 0)
  1384  			}
  1385  
  1386  			entriesByTagId[tagId] = append(tags, ite)
  1387  		}
  1388  
  1389  		ifd := &Ifd{
  1390  			ifdIdentity: ii,
  1391  
  1392  			byteOrder: ie.byteOrder,
  1393  
  1394  			id: id,
  1395  
  1396  			parentIfd:      parentIfd,
  1397  			parentTagIndex: qi.ParentTagIndex,
  1398  
  1399  			offset:         offset,
  1400  			entries:        entries,
  1401  			entriesByTagId: entriesByTagId,
  1402  
  1403  			// This is populated as each child is processed.
  1404  			children: make([]*Ifd, 0),
  1405  
  1406  			nextIfdOffset: nextIfdOffset,
  1407  			thumbnailData: thumbnailData,
  1408  
  1409  			ifdMapping: ie.ifdMapping,
  1410  			tagIndex:   ie.tagIndex,
  1411  		}
  1412  
  1413  		// Add ourselves to a big list of IFDs.
  1414  		ifds = append(ifds, ifd)
  1415  
  1416  		// Install ourselves into a by-id lookup table (keys are unique).
  1417  		tree[id] = ifd
  1418  
  1419  		// Install into by-name buckets.
  1420  		lookup[ii.String()] = ifd
  1421  
  1422  		// Add a link from the previous IFD in the chain to us.
  1423  		if previousIfd, found := edges[offset]; found == true {
  1424  			previousIfd.nextIfd = ifd
  1425  		}
  1426  
  1427  		// Attach as a child to our parent (where we appeared as a tag in
  1428  		// that IFD).
  1429  		if parentIfd != nil {
  1430  			parentIfd.children = append(parentIfd.children, ifd)
  1431  		}
  1432  
  1433  		// Determine if any of our entries is a child IFD and queue it.
  1434  		for i, ite := range entries {
  1435  			if ite.ChildIfdPath() == "" {
  1436  				continue
  1437  			}
  1438  
  1439  			tagId := ite.TagId()
  1440  			childIfdName := ite.ChildIfdName()
  1441  
  1442  			currentIfdTag := ii.IfdTag()
  1443  
  1444  			childIfdTag :=
  1445  				exifcommon.NewIfdTag(
  1446  					&currentIfdTag,
  1447  					tagId,
  1448  					childIfdName)
  1449  
  1450  			iiChild := ii.NewChild(childIfdTag, 0)
  1451  
  1452  			qi := QueuedIfd{
  1453  				IfdIdentity: iiChild,
  1454  
  1455  				Offset:         ite.getValueOffset(),
  1456  				Parent:         ifd,
  1457  				ParentTagIndex: i,
  1458  			}
  1459  
  1460  			queue = append(queue, qi)
  1461  		}
  1462  
  1463  		// If there's another IFD in the chain.
  1464  		if nextIfdOffset != 0 {
  1465  			iiSibling := ii.NewSibling(ii.Index() + 1)
  1466  
  1467  			// Allow the next link to know what the previous link was.
  1468  			edges[nextIfdOffset] = ifd
  1469  
  1470  			qi := QueuedIfd{
  1471  				IfdIdentity: iiSibling,
  1472  				Offset:      nextIfdOffset,
  1473  			}
  1474  
  1475  			queue = append(queue, qi)
  1476  		}
  1477  	}
  1478  
  1479  	index.RootIfd = tree[0]
  1480  	index.Ifds = ifds
  1481  	index.Tree = tree
  1482  	index.Lookup = lookup
  1483  
  1484  	err = ie.setChildrenIndex(index.RootIfd)
  1485  	log.PanicIf(err)
  1486  
  1487  	ifdEnumerateLogger.Debugf(nil, "Collect: It looks like the furthest offset that contained EXIF data in the EXIF blob was (%d).", ie.FurthestOffset())
  1488  
  1489  	return index, nil
  1490  }
  1491  
  1492  func (ie *IfdEnumerate) setChildrenIndex(ifd *Ifd) (err error) {
  1493  	defer func() {
  1494  		if state := recover(); state != nil {
  1495  			err = log.Wrap(state.(error))
  1496  		}
  1497  	}()
  1498  
  1499  	childIfdIndex := make(map[string]*Ifd)
  1500  	for _, childIfd := range ifd.children {
  1501  		childIfdIndex[childIfd.ifdIdentity.UnindexedString()] = childIfd
  1502  	}
  1503  
  1504  	ifd.childIfdIndex = childIfdIndex
  1505  
  1506  	for _, childIfd := range ifd.children {
  1507  		err := ie.setChildrenIndex(childIfd)
  1508  		log.PanicIf(err)
  1509  	}
  1510  
  1511  	return nil
  1512  }
  1513  
  1514  // FurthestOffset returns the furthest offset visited in the EXIF blob. This
  1515  // *does not* account for the locations of any undefined tags since we always
  1516  // evaluate the furthest offset, whether or not the user wants to know it.
  1517  //
  1518  // We are not willing to incur the cost of actually parsing those tags just to
  1519  // know their length when there are still undefined tags that are out there
  1520  // that we still won't have any idea how to parse, thus making this an
  1521  // approximation regardless of how clever we get.
  1522  func (ie *IfdEnumerate) FurthestOffset() uint32 {
  1523  
  1524  	// TODO(dustin): Add test
  1525  
  1526  	return ie.furthestOffset
  1527  }
  1528  
  1529  // parseOneIfd is a hack to use an IE to parse a raw IFD block. Can be used for
  1530  // testing. The fqIfdPath ("fully-qualified IFD path") will be less qualified
  1531  // in that the numeric index will always be zero (the zeroth child) rather than
  1532  // the proper number (if its actually a sibling to the first child, for
  1533  // instance).
  1534  func parseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor TagVisitorFn) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) {
  1535  	defer func() {
  1536  		if state := recover(); state != nil {
  1537  			err = log.Wrap(state.(error))
  1538  		}
  1539  	}()
  1540  
  1541  	// TODO(dustin): Add test
  1542  
  1543  	ebs := NewExifReadSeekerWithBytes(ifdBlock)
  1544  
  1545  	rs, err := ebs.GetReadSeeker(0)
  1546  	log.PanicIf(err)
  1547  
  1548  	bp, err := newByteParser(rs, byteOrder, 0)
  1549  	if err != nil {
  1550  		if err == ErrOffsetInvalid {
  1551  			return 0, nil, err
  1552  		}
  1553  
  1554  		log.Panic(err)
  1555  	}
  1556  
  1557  	dummyEbs := NewExifReadSeekerWithBytes([]byte{})
  1558  	ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder)
  1559  
  1560  	nextIfdOffset, entries, _, err = ie.parseIfd(ii, bp, visitor, true, nil)
  1561  	log.PanicIf(err)
  1562  
  1563  	return nextIfdOffset, entries, nil
  1564  }
  1565  
  1566  // parseOneTag is a hack to use an IE to parse a raw tag block.
  1567  func parseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, tagBlock []byte) (ite *IfdTagEntry, err error) {
  1568  	defer func() {
  1569  		if state := recover(); state != nil {
  1570  			err = log.Wrap(state.(error))
  1571  		}
  1572  	}()
  1573  
  1574  	// TODO(dustin): Add test
  1575  
  1576  	ebs := NewExifReadSeekerWithBytes(tagBlock)
  1577  
  1578  	rs, err := ebs.GetReadSeeker(0)
  1579  	log.PanicIf(err)
  1580  
  1581  	bp, err := newByteParser(rs, byteOrder, 0)
  1582  	if err != nil {
  1583  		if err == ErrOffsetInvalid {
  1584  			return nil, err
  1585  		}
  1586  
  1587  		log.Panic(err)
  1588  	}
  1589  
  1590  	dummyEbs := NewExifReadSeekerWithBytes([]byte{})
  1591  	ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder)
  1592  
  1593  	ite, err = ie.parseTag(ii, 0, bp)
  1594  	log.PanicIf(err)
  1595  
  1596  	err = ie.tagPostParse(ite, nil)
  1597  	if err != nil {
  1598  		if err == ErrTagNotFound {
  1599  			return nil, err
  1600  		}
  1601  
  1602  		log.Panic(err)
  1603  	}
  1604  
  1605  	return ite, nil
  1606  }
  1607  
  1608  // FindIfdFromRootIfd returns the given `Ifd` given the root-IFD and path of the
  1609  // desired IFD.
  1610  func FindIfdFromRootIfd(rootIfd *Ifd, ifdPath string) (ifd *Ifd, err error) {
  1611  	defer func() {
  1612  		if state := recover(); state != nil {
  1613  			err = log.Wrap(state.(error))
  1614  		}
  1615  	}()
  1616  
  1617  	// TODO(dustin): !! Add test.
  1618  
  1619  	lineage, err := rootIfd.ifdMapping.ResolvePath(ifdPath)
  1620  	log.PanicIf(err)
  1621  
  1622  	// Confirm the first IFD is our root IFD type, and then prune it because
  1623  	// from then on we'll be searching down through our children.
  1624  
  1625  	if len(lineage) == 0 {
  1626  		log.Panicf("IFD path must be non-empty.")
  1627  	} else if lineage[0].Name != exifcommon.IfdStandardIfdIdentity.Name() {
  1628  		log.Panicf("First IFD path item must be [%s].", exifcommon.IfdStandardIfdIdentity.Name())
  1629  	}
  1630  
  1631  	desiredRootIndex := lineage[0].Index
  1632  	lineage = lineage[1:]
  1633  
  1634  	// TODO(dustin): !! This is a poorly conceived fix that just doubles the work we already have to do below, which then interacts badly with the indices not being properly represented in the IFD-phrase.
  1635  	// TODO(dustin): !! <-- However, we're not sure whether we shouldn't store a secondary IFD-path with the indices. Some IFDs may not necessarily restrict which IFD indices they can be a child of (only the IFD itself matters). Validation should be delegated to the caller.
  1636  	thisIfd := rootIfd
  1637  	for currentRootIndex := 0; currentRootIndex < desiredRootIndex; currentRootIndex++ {
  1638  		if thisIfd.nextIfd == nil {
  1639  			log.Panicf("Root-IFD index (%d) does not exist in the data.", currentRootIndex)
  1640  		}
  1641  
  1642  		thisIfd = thisIfd.nextIfd
  1643  	}
  1644  
  1645  	for _, itii := range lineage {
  1646  		var hit *Ifd
  1647  		for _, childIfd := range thisIfd.children {
  1648  			if childIfd.ifdIdentity.TagId() == itii.TagId {
  1649  				hit = childIfd
  1650  				break
  1651  			}
  1652  		}
  1653  
  1654  		// If we didn't find the child, add it.
  1655  		if hit == nil {
  1656  			log.Panicf("IFD [%s] in [%s] not found: %s", itii.Name, ifdPath, thisIfd.children)
  1657  		}
  1658  
  1659  		thisIfd = hit
  1660  
  1661  		// If we didn't find the sibling, add it.
  1662  		for i := 0; i < itii.Index; i++ {
  1663  			if thisIfd.nextIfd == nil {
  1664  				log.Panicf("IFD [%s] does not have (%d) occurrences/siblings", thisIfd.ifdIdentity.UnindexedString(), itii.Index)
  1665  			}
  1666  
  1667  			thisIfd = thisIfd.nextIfd
  1668  		}
  1669  	}
  1670  
  1671  	return thisIfd, nil
  1672  }
  1673  

View as plain text