Ejemplo n.º 1
0
// FileTime returns the best guess of the file's creation time (or modtime).
// If the file doesn't have its own metadata indication the creation time (such as in EXIF),
// FileTime uses the modification time from the file system.
// It there was a valid EXIF but an error while trying to get a date from it,
// it logs the error and tries the other methods.
func FileTime(f io.ReaderAt) (time.Time, error) {
	var ct time.Time
	defaultTime := func() (time.Time, error) {
		if osf, ok := f.(*os.File); ok {
			fi, err := osf.Stat()
			if err != nil {
				return ct, fmt.Errorf("Failed to find a modtime: stat: %v", err)
			}
			return fi.ModTime(), nil
		}
		return ct, errors.New("All methods failed to find a creation time or modtime.")
	}

	size, ok := findSize(f)
	if !ok {
		size = 256 << 10 // enough to get the EXIF
	}
	r := io.NewSectionReader(f, 0, size)
	var tiffErr error
	ex, err := exif.Decode(r)
	if err != nil {
		tiffErr = err
		if exif.IsShortReadTagValueError(err) {
			return ct, io.ErrUnexpectedEOF
		}
		if exif.IsCriticalError(err) || exif.IsExifError(err) {
			return defaultTime()
		}
	}
	ct, err = ex.DateTime()
	if err != nil {
		return defaultTime()
	}
	// If the EXIF file only had local timezone, but it did have
	// GPS, then lookup the timezone and correct the time.
	if ct.Location() == time.Local {
		if exif.IsGPSError(tiffErr) {
			log.Printf("Invalid EXIF GPS data: %v", tiffErr)
			return ct, nil
		}
		if lat, long, err := ex.LatLong(); err == nil {
			if loc := lookupLocation(latlong.LookupZoneName(lat, long)); loc != nil {
				if t, err := exifDateTimeInLocation(ex, loc); err == nil {
					return t, nil
				}
			}
		} else if !exif.IsTagNotPresentError(err) {
			log.Printf("Invalid EXIF GPS data: %v", err)
		}
	}
	return ct, nil
}
Ejemplo n.º 2
0
func indexEXIF(wholeRef blob.Ref, reader io.Reader, mm *mutationMap) (err error) {
	var tiffErr error
	ex, err := exif.Decode(reader)
	if err != nil {
		tiffErr = err
		if exif.IsCriticalError(err) {
			return
		}
		log.Printf("Non critical TIFF decoding error: %v", err)
	}
	defer func() {
		// The EXIF library panics if you access a field past
		// what the file contains.  Be paranoid and just
		// recover here, instead of crashing on an invalid
		// EXIF file.
		if e := recover(); e != nil {
			err = errEXIFPanic
		}
	}()

	err = ex.Walk(exifWalkFunc(func(name exif.FieldName, tag *tiff.Tag) error {
		tagFmt := tagFormatString(tag)
		if tagFmt == "" {
			return nil
		}
		key := keyEXIFTag.Key(wholeRef, fmt.Sprintf("%04x", tag.Id))
		numComp := int(tag.Count)
		if tag.Format() == tiff.StringVal {
			numComp = 1
		}
		var val bytes.Buffer
		val.WriteString(keyEXIFTag.Val(tagFmt, numComp, ""))
		if tag.Format() == tiff.StringVal {
			str, err := tag.StringVal()
			if err != nil {
				log.Printf("Invalid EXIF string data: %v", err)
				return nil
			}
			if containsUnsafeRawStrByte(str) {
				val.WriteString(urle(str))
			} else {
				val.WriteString(str)
			}
		} else {
			for i := 0; i < int(tag.Count); i++ {
				if i > 0 {
					val.WriteByte('|')
				}
				switch tagFmt {
				case "int":
					v, err := tag.Int(i)
					if err != nil {
						log.Printf("Invalid EXIF int data: %v", err)
						return nil
					}
					fmt.Fprintf(&val, "%d", v)
				case "rat":
					n, d, err := tag.Rat2(i)
					if err != nil {
						log.Printf("Invalid EXIF rat data: %v", err)
						return nil
					}
					fmt.Fprintf(&val, "%d/%d", n, d)
				case "float":
					v, err := tag.Float(i)
					if err != nil {
						log.Printf("Invalid EXIF float data: %v", err)
						return nil
					}
					fmt.Fprintf(&val, "%v", v)
				default:
					panic("shouldn't get here")
				}
			}
		}
		valStr := val.String()
		mm.Set(key, valStr)
		return nil
	}))
	if err != nil {
		return
	}

	if exif.IsGPSError(tiffErr) {
		log.Printf("Invalid EXIF GPS data: %v", tiffErr)
		return nil
	}
	if lat, long, err := ex.LatLong(); err == nil {
		mm.Set(keyEXIFGPS.Key(wholeRef), keyEXIFGPS.Val(fmt.Sprint(lat), fmt.Sprint(long)))
	} else if !exif.IsTagNotPresentError(err) {
		log.Printf("Invalid EXIF GPS data: %v", err)
	}
	return nil
}