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