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