    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 * http://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.archivers.zip;
    020
    021import java.io.Serializable;
    022import java.util.Arrays;
    023import java.util.Date;
    024import java.util.zip.ZipException;
    025
    026/**
    027 * <p>An extra field that stores additional file and directory timestamp data
    028 * for zip entries.   Each zip entry can include up to three timestamps
    029 * (modify, access, create*).  The timestamps are stored as 32 bit signed
    030 * integers representing seconds since UNIX epoch (Jan 1st, 1970, UTC).
    031 * This field improves on zip's default timestamp granularity, since it
    032 * allows one to store additional timestamps, and, in addition, the timestamps
    033 * are stored using per-second granularity (zip's default behavior can only store
    034 * timestamps to the nearest <em>even</em> second).
    035 * </p><p>
    036 * Unfortunately, 32 (signed) bits can only store dates up to the year 2037,
    037 * and so this extra field will eventually be obsolete.  Enjoy it while it lasts!
    038 * </p>
    039 * <ul>
    040 * <li><b>modifyTime:</b>
    041 * most recent time of file/directory modification
    042 * (or file/dir creation if the entry has not been
    043 * modified since it was created).
    044 * </li>
    045 * <li><b>accessTime:</b>
    046 * most recent time file/directory was opened
    047 * (e.g., read from disk).  Many people disable
    048 * their operating systems from updating this value
    049 * using the NOATIME mount option to optimize disk behavior,
    050 * and thus it's not always reliable.  In those cases
    051 * it's always equal to modifyTime.
    052 * </li>
    053 * <li><b>*createTime:</b>
    054 * modern linux file systems (e.g., ext2 and newer)
    055 * do not appear to store a value like this, and so
    056 * it's usually omitted altogether in the zip extra
    057 * field.  Perhaps other unix systems track this.
    058 * </li></ul>
    059 * <p>
    060 * We're using the field definition given in Info-Zip's source archive:
    061 * zip-3.0.tar.gz/proginfo/extrafld.txt
    062 * </p>
    063 * <pre>
    064 * Value         Size        Description
    065 * -----         ----        -----------
    066 * 0x5455        Short       tag for this extra block type ("UT")
    067 * TSize         Short       total data size for this block
    068 * Flags         Byte        info bits
    069 * (ModTime)     Long        time of last modification (UTC/GMT)
    070 * (AcTime)      Long        time of last access (UTC/GMT)
    071 * (CrTime)      Long        time of original creation (UTC/GMT)
    072 *
    073 * Central-header version:
    074 *
    075 * Value         Size        Description
    076 * -----         ----        -----------
    077 * 0x5455        Short       tag for this extra block type ("UT")
    078 * TSize         Short       total data size for this block
    079 * Flags         Byte        info bits (refers to local header!)
    080 * (ModTime)     Long        time of last modification (UTC/GMT)
    081 * </pre>
    082 * @since 1.5
    083 */
    084public class X5455_ExtendedTimestamp implements ZipExtraField, Cloneable, Serializable {
    085    private static final ZipShort HEADER_ID = new ZipShort(0x5455);
    086    private static final long serialVersionUID = 1L;
    087
    088    /**
    089     * The bit set inside the flags by when the last modification time
    090     * is present in this extra field.
    091     */
    092    public static final byte MODIFY_TIME_BIT = 1;
    093    /**
    094     * The bit set inside the flags by when the lasr access time is
    095     * present in this extra field.
    096     */
    097    public static final byte ACCESS_TIME_BIT = 2;
    098    /**
    099     * The bit set inside the flags by when the original creation time
    100     * is present in this extra field.
    101     */
    102    public static final byte CREATE_TIME_BIT = 4;
    103
    104    // The 3 boolean fields (below) come from this flags byte.  The remaining 5 bits
    105    // are ignored according to the current version of the spec (December 2012).
    106    private byte flags;
    107
    108    // Note: even if bit1 and bit2 are set, the Central data will still not contain
    109    // access/create fields:  only local data ever holds those!  This causes
    110    // some of our implementation to look a little odd, with seemingly spurious
    111    // != null and length checks.
    112    private boolean bit0_modifyTimePresent;
    113    private boolean bit1_accessTimePresent;
    114    private boolean bit2_createTimePresent;
    115
    116    private ZipLong modifyTime;
    117    private ZipLong accessTime;
    118    private ZipLong createTime;
    119
    120    /**
    121     * Constructor for X5455_ExtendedTimestamp.
    122     */
    123    public X5455_ExtendedTimestamp() {}
    124
    125    /**
    126     * The Header-ID.
    127     *
    128     * @return the value for the header id for this extrafield
    129     */
    130    @Override
    131    public ZipShort getHeaderId() {
    132        return HEADER_ID;
    133    }
    134
    135    /**
    136     * Length of the extra field in the local file data - without
    137     * Header-ID or length specifier.
    138     *
    139     * @return a <code>ZipShort</code> for the length of the data of this extra field
    140     */
    141    @Override
    142    public ZipShort getLocalFileDataLength() {
    143        return new ZipShort(1 +
    144                (bit0_modifyTimePresent ? 4 : 0) +
    145                (bit1_accessTimePresent && accessTime != null ? 4 : 0) +
    146                (bit2_createTimePresent && createTime != null ? 4 : 0)
    147        );
    148    }
    149
    150    /**
    151     * Length of the extra field in the local file data - without
    152     * Header-ID or length specifier.
    153     *
    154     * <p>For X5455 the central length is often smaller than the
    155     * local length, because central cannot contain access or create
    156     * timestamps.</p>
    157     *
    158     * @return a <code>ZipShort</code> for the length of the data of this extra field
    159     */
    160    @Override
    161    public ZipShort getCentralDirectoryLength() {
    162        return new ZipShort(1 +
    163                (bit0_modifyTimePresent ? 4 : 0)
    164        );
    165    }
    166
    167    /**
    168     * The actual data to put into local file data - without Header-ID
    169     * or length specifier.
    170     *
    171     * @return get the data
    172     */
    173    @Override
    174    public byte[] getLocalFileDataData() {
    175        final byte[] data = new byte[getLocalFileDataLength().getValue()];
    176        int pos = 0;
    177        data[pos++] = 0;
    178        if (bit0_modifyTimePresent) {
    179            data[0] |= MODIFY_TIME_BIT;
    180            System.arraycopy(modifyTime.getBytes(), 0, data, pos, 4);
    181            pos += 4;
    182        }
    183        if (bit1_accessTimePresent && accessTime != null) {
    184            data[0] |= ACCESS_TIME_BIT;
    185            System.arraycopy(accessTime.getBytes(), 0, data, pos, 4);
    186            pos += 4;
    187        }
    188        if (bit2_createTimePresent && createTime != null) {
    189            data[0] |= CREATE_TIME_BIT;
    190            System.arraycopy(createTime.getBytes(), 0, data, pos, 4);
    191            pos += 4; // NOSONAR - assignment as documentation
    192        }
    193        return data;
    194    }
    195
    196    /**
    197     * The actual data to put into central directory data - without Header-ID
    198     * or length specifier.
    199     *
    200     * @return the central directory data
    201     */
    202    @Override
    203    public byte[] getCentralDirectoryData() {
    204        // Truncate out create & access time (last 8 bytes) from
    205        // the copy of the local data we obtained:
    206        return Arrays.copyOf(getLocalFileDataData(), getCentralDirectoryLength().getValue());
    207    }
    208
    209    /**
    210     * Populate data from this array as if it was in local file data.
    211     *
    212     * @param data   an array of bytes
    213     * @param offset the start offset
    214     * @param length the number of bytes in the array from offset
    215     * @throws java.util.zip.ZipException on error
    216     */
    217    @Override
    218    public void parseFromLocalFileData(
    219            final byte[] data, int offset, final int length
    220    ) throws ZipException {
    221        reset();
    222        if (length < 1) {
    223            throw new ZipException("X5455_ExtendedTimestamp too short, only " + length + " bytes");
    224        }
    225        final int len = offset + length;
    226        setFlags(data[offset++]);
    227        if (bit0_modifyTimePresent && offset + 4 <= len) {
    228            modifyTime = new ZipLong(data, offset);
    229            offset += 4;
    230        } else {
    231            bit0_modifyTimePresent = false;
    232        }
    233        if (bit1_accessTimePresent && offset + 4 <= len) {
    234            accessTime = new ZipLong(data, offset);
    235            offset += 4;
    236        } else {
    237            bit1_accessTimePresent = false;
    238        }
    239        if (bit2_createTimePresent && offset + 4 <= len) {
    240            createTime = new ZipLong(data, offset);
    241            offset += 4; // NOSONAR - assignment as documentation
    242        } else {
    243            bit2_createTimePresent = false;
    244        }
    245    }
    246
    247    /**
    248     * Doesn't do anything special since this class always uses the
    249     * same parsing logic for both central directory and local file data.
    250     */
    251    @Override
    252    public void parseFromCentralDirectoryData(
    253            final byte[] buffer, final int offset, final int length
    254    ) throws ZipException {
    255        reset();
    256        parseFromLocalFileData(buffer, offset, length);
    257    }
    258
    259    /**
    260     * Reset state back to newly constructed state.  Helps us make sure
    261     * parse() calls always generate clean results.
    262     */
    263    private void reset() {
    264        setFlags((byte) 0);
    265        this.modifyTime = null;
    266        this.accessTime = null;
    267        this.createTime = null;
    268    }
    269
    270    /**
    271     * Sets flags byte.  The flags byte tells us which of the
    272     * three datestamp fields are present in the data:
    273     * <pre>
    274     * bit0 - modify time
    275     * bit1 - access time
    276     * bit2 - create time
    277     * </pre>
    278     * Only first 3 bits of flags are used according to the
    279     * latest version of the spec (December 2012).
    280     *
    281     * @param flags flags byte indicating which of the
    282     *              three datestamp fields are present.
    283     */
    284    public void setFlags(final byte flags) {
    285        this.flags = flags;
    286        this.bit0_modifyTimePresent = (flags & MODIFY_TIME_BIT) == MODIFY_TIME_BIT;
    287        this.bit1_accessTimePresent = (flags & ACCESS_TIME_BIT) == ACCESS_TIME_BIT;
    288        this.bit2_createTimePresent = (flags & CREATE_TIME_BIT) == CREATE_TIME_BIT;
    289    }
    290
    291    /**
    292     * Gets flags byte.  The flags byte tells us which of the
    293     * three datestamp fields are present in the data:
    294     * <pre>
    295     * bit0 - modify time
    296     * bit1 - access time
    297     * bit2 - create time
    298     * </pre>
    299     * Only first 3 bits of flags are used according to the
    300     * latest version of the spec (December 2012).
    301     *
    302     * @return flags byte indicating which of the
    303     *         three datestamp fields are present.
    304     */
    305    public byte getFlags() { return flags; }
    306
    307    /**
    308     * Returns whether bit0 of the flags byte is set or not,
    309     * which should correspond to the presence or absence of
    310     * a modify timestamp in this particular zip entry.
    311     *
    312     * @return true if bit0 of the flags byte is set.
    313     */
    314    public boolean isBit0_modifyTimePresent() { return bit0_modifyTimePresent; }
    315
    316    /**
    317     * Returns whether bit1 of the flags byte is set or not,
    318     * which should correspond to the presence or absence of
    319     * a "last access" timestamp in this particular zip entry.
    320     *
    321     * @return true if bit1 of the flags byte is set.
    322     */
    323    public boolean isBit1_accessTimePresent() { return bit1_accessTimePresent; }
    324
    325    /**
    326     * Returns whether bit2 of the flags byte is set or not,
    327     * which should correspond to the presence or absence of
    328     * a create timestamp in this particular zip entry.
    329     *
    330     * @return true if bit2 of the flags byte is set.
    331     */
    332    public boolean isBit2_createTimePresent() { return bit2_createTimePresent; }
    333
    334    /**
    335     * Returns the modify time (seconds since epoch) of this zip entry
    336     * as a ZipLong object, or null if no such timestamp exists in the
    337     * zip entry.
    338     *
    339     * @return modify time (seconds since epoch) or null.
    340     */
    341    public ZipLong getModifyTime() { return modifyTime; }
    342
    343    /**
    344     * Returns the access time (seconds since epoch) of this zip entry
    345     * as a ZipLong object, or null if no such timestamp exists in the
    346     * zip entry.
    347     *
    348     * @return access time (seconds since epoch) or null.
    349     */
    350    public ZipLong getAccessTime() { return accessTime; }
    351
    352    /**
    353     * <p>
    354     * Returns the create time (seconds since epoch) of this zip entry
    355     * as a ZipLong object, or null if no such timestamp exists in the
    356     * zip entry.
    357     * </p><p>
    358     * Note: modern linux file systems (e.g., ext2)
    359     * do not appear to store a "create time" value, and so
    360     * it's usually omitted altogether in the zip extra
    361     * field.  Perhaps other unix systems track this.
    362     *
    363     * @return create time (seconds since epoch) or null.
    364     */
    365    public ZipLong getCreateTime() { return createTime; }
    366
    367    /**
    368     * Returns the modify time as a java.util.Date
    369     * of this zip entry, or null if no such timestamp exists in the zip entry.
    370     * The milliseconds are always zeroed out, since the underlying data
    371     * offers only per-second precision.
    372     *
    373     * @return modify time as java.util.Date or null.
    374     */
    375    public Date getModifyJavaTime() {
    376        return zipLongToDate(modifyTime);
    377    }
    378
    379    /**
    380     * Returns the access time as a java.util.Date
    381     * of this zip entry, or null if no such timestamp exists in the zip entry.
    382     * The milliseconds are always zeroed out, since the underlying data
    383     * offers only per-second precision.
    384     *
    385     * @return access time as java.util.Date or null.
    386     */
    387    public Date getAccessJavaTime() {
    388        return zipLongToDate(accessTime);
    389    }
    390
    391    /**
    392     * <p>
    393     * Returns the create time as a a java.util.Date
    394     * of this zip entry, or null if no such timestamp exists in the zip entry.
    395     * The milliseconds are always zeroed out, since the underlying data
    396     * offers only per-second precision.
    397     * </p><p>
    398     * Note: modern linux file systems (e.g., ext2)
    399     * do not appear to store a "create time" value, and so
    400     * it's usually omitted altogether in the zip extra
    401     * field.  Perhaps other unix systems track this.
    402     *
    403     * @return create time as java.util.Date or null.
    404     */
    405    public Date getCreateJavaTime() {
    406        return zipLongToDate(createTime);
    407    }
    408
    409    /**
    410     * <p>
    411     * Sets the modify time (seconds since epoch) of this zip entry
    412     * using a ZipLong object.
    413     * </p><p>
    414     * Note: the setters for flags and timestamps are decoupled.
    415     * Even if the timestamp is not-null, it will only be written
    416     * out if the corresponding bit in the flags is also set.
    417     * </p>
    418     *
    419     * @param l ZipLong of the modify time (seconds per epoch)
    420     */
    421    public void setModifyTime(final ZipLong l) {
    422        bit0_modifyTimePresent = l != null;
    423        flags = (byte) (l != null ? (flags | MODIFY_TIME_BIT)
    424                        : (flags & ~MODIFY_TIME_BIT));
    425        this.modifyTime = l;
    426    }
    427
    428    /**
    429     * <p>
    430     * Sets the access time (seconds since epoch) of this zip entry
    431     * using a ZipLong object
    432     * </p><p>
    433     * Note: the setters for flags and timestamps are decoupled.
    434     * Even if the timestamp is not-null, it will only be written
    435     * out if the corresponding bit in the flags is also set.
    436     * </p>
    437     *
    438     * @param l ZipLong of the access time (seconds per epoch)
    439     */
    440    public void setAccessTime(final ZipLong l) {
    441        bit1_accessTimePresent = l != null;
    442        flags = (byte) (l != null ? (flags | ACCESS_TIME_BIT)
    443                        : (flags & ~ACCESS_TIME_BIT));
    444        this.accessTime = l;
    445    }
    446
    447    /**
    448     * <p>
    449     * Sets the create time (seconds since epoch) of this zip entry
    450     * using a ZipLong object
    451     * </p><p>
    452     * Note: the setters for flags and timestamps are decoupled.
    453     * Even if the timestamp is not-null, it will only be written
    454     * out if the corresponding bit in the flags is also set.
    455     * </p>
    456     *
    457     * @param l ZipLong of the create time (seconds per epoch)
    458     */
    459    public void setCreateTime(final ZipLong l) {
    460        bit2_createTimePresent = l != null;
    461        flags = (byte) (l != null ? (flags | CREATE_TIME_BIT)
    462                        : (flags & ~CREATE_TIME_BIT));
    463        this.createTime = l;
    464    }
    465
    466    /**
    467     * <p>
    468     * Sets the modify time as a java.util.Date
    469     * of this zip entry.  Supplied value is truncated to per-second
    470     * precision (milliseconds zeroed-out).
    471     * </p><p>
    472     * Note: the setters for flags and timestamps are decoupled.
    473     * Even if the timestamp is not-null, it will only be written
    474     * out if the corresponding bit in the flags is also set.
    475     * </p>
    476     *
    477     * @param d modify time as java.util.Date
    478     */
    479    public void setModifyJavaTime(final Date d) { setModifyTime(dateToZipLong(d)); }
    480
    481    /**
    482     * <p>
    483     * Sets the access time as a java.util.Date
    484     * of this zip entry.  Supplied value is truncated to per-second
    485     * precision (milliseconds zeroed-out).
    486     * </p><p>
    487     * Note: the setters for flags and timestamps are decoupled.
    488     * Even if the timestamp is not-null, it will only be written
    489     * out if the corresponding bit in the flags is also set.
    490     * </p>
    491     *
    492     * @param d access time as java.util.Date
    493     */
    494    public void setAccessJavaTime(final Date d) { setAccessTime(dateToZipLong(d)); }
    495
    496    /**
    497     * <p>
    498     * Sets the create time as a java.util.Date
    499     * of this zip entry.  Supplied value is truncated to per-second
    500     * precision (milliseconds zeroed-out).
    501     * </p><p>
    502     * Note: the setters for flags and timestamps are decoupled.
    503     * Even if the timestamp is not-null, it will only be written
    504     * out if the corresponding bit in the flags is also set.
    505     * </p>
    506     *
    507     * @param d create time as java.util.Date
    508     */
    509    public void setCreateJavaTime(final Date d) { setCreateTime(dateToZipLong(d)); }
    510
    511    /**
    512     * Utility method converts java.util.Date (milliseconds since epoch)
    513     * into a ZipLong (seconds since epoch).
    514     * <p/>
    515     * Also makes sure the converted ZipLong is not too big to fit
    516     * in 32 unsigned bits.
    517     *
    518     * @param d java.util.Date to convert to ZipLong
    519     * @return ZipLong
    520     */
    521    private static ZipLong dateToZipLong(final Date d) {
    522        if (d == null) { return null; }
    523
    524        return unixTimeToZipLong(d.getTime() / 1000);
    525    }
    526
    527    /**
    528     * Returns a String representation of this class useful for
    529     * debugging purposes.
    530     *
    531     * @return A String representation of this class useful for
    532     *         debugging purposes.
    533     */
    534    @Override
    535    public String toString() {
    536        final StringBuilder buf = new StringBuilder();
    537        buf.append("0x5455 Zip Extra Field: Flags=");
    538        buf.append(Integer.toBinaryString(ZipUtil.unsignedIntToSignedByte(flags))).append(" ");
    539        if (bit0_modifyTimePresent && modifyTime != null) {
    540            final Date m = getModifyJavaTime();
    541            buf.append(" Modify:[").append(m).append("] ");
    542        }
    543        if (bit1_accessTimePresent && accessTime != null) {
    544            final Date a = getAccessJavaTime();
    545            buf.append(" Access:[").append(a).append("] ");
    546        }
    547        if (bit2_createTimePresent && createTime != null) {
    548            final Date c = getCreateJavaTime();
    549            buf.append(" Create:[").append(c).append("] ");
    550        }
    551        return buf.toString();
    552    }
    553
    554    @Override
    555    public Object clone() throws CloneNotSupportedException {
    556        return super.clone();
    557    }
    558
    559    @Override
    560    public boolean equals(final Object o) {
    561        if (o instanceof X5455_ExtendedTimestamp) {
    562            final X5455_ExtendedTimestamp xf = (X5455_ExtendedTimestamp) o;
    563
    564            // The ZipLong==ZipLong clauses handle the cases where both are null.
    565            // and only last 3 bits of flags matter.
    566            return ((flags & 0x07) == (xf.flags & 0x07)) &&
    567                    (modifyTime == xf.modifyTime || (modifyTime != null && modifyTime.equals(xf.modifyTime))) &&
    568                    (accessTime == xf.accessTime || (accessTime != null && accessTime.equals(xf.accessTime))) &&
    569                    (createTime == xf.createTime || (createTime != null && createTime.equals(xf.createTime)));
    570        }
    571        return false;
    572    }
    573
    574    @Override
    575    public int hashCode() {
    576        int hc = (-123 * (flags & 0x07)); // only last 3 bits of flags matter
    577        if (modifyTime != null) {
    578            hc ^= modifyTime.hashCode();
    579        }
    580        if (accessTime != null) {
    581            // Since accessTime is often same as modifyTime,
    582            // this prevents them from XOR negating each other.
    583            hc ^= Integer.rotateLeft(accessTime.hashCode(), 11);
    584        }
    585        if (createTime != null) {
    586            hc ^= Integer.rotateLeft(createTime.hashCode(), 22);
    587        }
    588        return hc;
    589    }
    590
    591    private static Date zipLongToDate(final ZipLong unixTime) {
    592        return unixTime != null ? new Date(unixTime.getIntValue() * 1000L) : null;
    593    }
    594
    595    private static ZipLong unixTimeToZipLong(final long l) {
    596        if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
    597            throw new IllegalArgumentException("X5455 timestamps must fit in a signed 32 bit integer: " + l);
    598        }
    599        return new ZipLong(l);
    600    }
    601
    602}