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 behaviour 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 behaviour,
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        }
231        if (bit1_accessTimePresent && offset + 4 <= len) {
232            accessTime = new ZipLong(data, offset);
233            offset += 4;
234        }
235        if (bit2_createTimePresent && offset + 4 <= len) {
236            createTime = new ZipLong(data, offset);
237            offset += 4; // NOSONAR - assignment as documentation
238        }
239    }
240
241    /**
242     * Doesn't do anything special since this class always uses the
243     * same parsing logic for both central directory and local file data.
244     */
245    @Override
246    public void parseFromCentralDirectoryData(
247            final byte[] buffer, final int offset, final int length
248    ) throws ZipException {
249        reset();
250        parseFromLocalFileData(buffer, offset, length);
251    }
252
253    /**
254     * Reset state back to newly constructed state.  Helps us make sure
255     * parse() calls always generate clean results.
256     */
257    private void reset() {
258        setFlags((byte) 0);
259        this.modifyTime = null;
260        this.accessTime = null;
261        this.createTime = null;
262    }
263
264    /**
265     * Sets flags byte.  The flags byte tells us which of the
266     * three datestamp fields are present in the data:
267     * <pre>
268     * bit0 - modify time
269     * bit1 - access time
270     * bit2 - create time
271     * </pre>
272     * Only first 3 bits of flags are used according to the
273     * latest version of the spec (December 2012).
274     *
275     * @param flags flags byte indicating which of the
276     *              three datestamp fields are present.
277     */
278    public void setFlags(final byte flags) {
279        this.flags = flags;
280        this.bit0_modifyTimePresent = (flags & MODIFY_TIME_BIT) == MODIFY_TIME_BIT;
281        this.bit1_accessTimePresent = (flags & ACCESS_TIME_BIT) == ACCESS_TIME_BIT;
282        this.bit2_createTimePresent = (flags & CREATE_TIME_BIT) == CREATE_TIME_BIT;
283    }
284
285    /**
286     * Gets flags byte.  The flags byte tells us which of the
287     * three datestamp fields are present in the data:
288     * <pre>
289     * bit0 - modify time
290     * bit1 - access time
291     * bit2 - create time
292     * </pre>
293     * Only first 3 bits of flags are used according to the
294     * latest version of the spec (December 2012).
295     *
296     * @return flags byte indicating which of the
297     *         three datestamp fields are present.
298     */
299    public byte getFlags() { return flags; }
300
301    /**
302     * Returns whether bit0 of the flags byte is set or not,
303     * which should correspond to the presence or absence of
304     * a modify timestamp in this particular zip entry.
305     *
306     * @return true if bit0 of the flags byte is set.
307     */
308    public boolean isBit0_modifyTimePresent() { return bit0_modifyTimePresent; }
309
310    /**
311     * Returns whether bit1 of the flags byte is set or not,
312     * which should correspond to the presence or absence of
313     * a "last access" timestamp in this particular zip entry.
314     *
315     * @return true if bit1 of the flags byte is set.
316     */
317    public boolean isBit1_accessTimePresent() { return bit1_accessTimePresent; }
318
319    /**
320     * Returns whether bit2 of the flags byte is set or not,
321     * which should correspond to the presence or absence of
322     * a create timestamp in this particular zip entry.
323     *
324     * @return true if bit2 of the flags byte is set.
325     */
326    public boolean isBit2_createTimePresent() { return bit2_createTimePresent; }
327
328    /**
329     * Returns the modify time (seconds since epoch) of this zip entry
330     * as a ZipLong object, or null if no such timestamp exists in the
331     * zip entry.
332     *
333     * @return modify time (seconds since epoch) or null.
334     */
335    public ZipLong getModifyTime() { return modifyTime; }
336
337    /**
338     * Returns the access time (seconds since epoch) of this zip entry
339     * as a ZipLong object, or null if no such timestamp exists in the
340     * zip entry.
341     *
342     * @return access time (seconds since epoch) or null.
343     */
344    public ZipLong getAccessTime() { return accessTime; }
345
346    /**
347     * <p>
348     * Returns the create time (seconds since epoch) of this zip entry
349     * as a ZipLong object, or null if no such timestamp exists in the
350     * zip entry.
351     * </p><p>
352     * Note: modern linux file systems (e.g., ext2)
353     * do not appear to store a "create time" value, and so
354     * it's usually omitted altogether in the zip extra
355     * field.  Perhaps other unix systems track this.
356     *
357     * @return create time (seconds since epoch) or null.
358     */
359    public ZipLong getCreateTime() { return createTime; }
360
361    /**
362     * Returns the modify time as a java.util.Date
363     * of this zip entry, or null if no such timestamp exists in the zip entry.
364     * The milliseconds are always zeroed out, since the underlying data
365     * offers only per-second precision.
366     *
367     * @return modify time as java.util.Date or null.
368     */
369    public Date getModifyJavaTime() {
370        return zipLongToDate(modifyTime);
371    }
372
373    /**
374     * Returns the access time as a java.util.Date
375     * of this zip entry, or null if no such timestamp exists in the zip entry.
376     * The milliseconds are always zeroed out, since the underlying data
377     * offers only per-second precision.
378     *
379     * @return access time as java.util.Date or null.
380     */
381    public Date getAccessJavaTime() {
382        return zipLongToDate(accessTime);
383    }
384
385    /**
386     * <p>
387     * Returns the create time as a a java.util.Date
388     * of this zip entry, or null if no such timestamp exists in the zip entry.
389     * The milliseconds are always zeroed out, since the underlying data
390     * offers only per-second precision.
391     * </p><p>
392     * Note: modern linux file systems (e.g., ext2)
393     * do not appear to store a "create time" value, and so
394     * it's usually omitted altogether in the zip extra
395     * field.  Perhaps other unix systems track this.
396     *
397     * @return create time as java.util.Date or null.
398     */
399    public Date getCreateJavaTime() {
400        return zipLongToDate(createTime);
401    }
402
403    /**
404     * <p>
405     * Sets the modify time (seconds since epoch) of this zip entry
406     * using a ZipLong object.
407     * </p><p>
408     * Note: the setters for flags and timestamps are decoupled.
409     * Even if the timestamp is not-null, it will only be written
410     * out if the corresponding bit in the flags is also set.
411     * </p>
412     *
413     * @param l ZipLong of the modify time (seconds per epoch)
414     */
415    public void setModifyTime(final ZipLong l) {
416        bit0_modifyTimePresent = l != null;
417        flags = (byte) (l != null ? (flags | MODIFY_TIME_BIT)
418                        : (flags & ~MODIFY_TIME_BIT));
419        this.modifyTime = l;
420    }
421
422    /**
423     * <p>
424     * Sets the access time (seconds since epoch) of this zip entry
425     * using a ZipLong object
426     * </p><p>
427     * Note: the setters for flags and timestamps are decoupled.
428     * Even if the timestamp is not-null, it will only be written
429     * out if the corresponding bit in the flags is also set.
430     * </p>
431     *
432     * @param l ZipLong of the access time (seconds per epoch)
433     */
434    public void setAccessTime(final ZipLong l) {
435        bit1_accessTimePresent = l != null;
436        flags = (byte) (l != null ? (flags | ACCESS_TIME_BIT)
437                        : (flags & ~ACCESS_TIME_BIT));
438        this.accessTime = l;
439    }
440
441    /**
442     * <p>
443     * Sets the create time (seconds since epoch) of this zip entry
444     * using a ZipLong object
445     * </p><p>
446     * Note: the setters for flags and timestamps are decoupled.
447     * Even if the timestamp is not-null, it will only be written
448     * out if the corresponding bit in the flags is also set.
449     * </p>
450     *
451     * @param l ZipLong of the create time (seconds per epoch)
452     */
453    public void setCreateTime(final ZipLong l) {
454        bit2_createTimePresent = l != null;
455        flags = (byte) (l != null ? (flags | CREATE_TIME_BIT)
456                        : (flags & ~CREATE_TIME_BIT));
457        this.createTime = l;
458    }
459
460    /**
461     * <p>
462     * Sets the modify time as a java.util.Date
463     * of this zip entry.  Supplied value is truncated to per-second
464     * precision (milliseconds zeroed-out).
465     * </p><p>
466     * Note: the setters for flags and timestamps are decoupled.
467     * Even if the timestamp is not-null, it will only be written
468     * out if the corresponding bit in the flags is also set.
469     * </p>
470     *
471     * @param d modify time as java.util.Date
472     */
473    public void setModifyJavaTime(final Date d) { setModifyTime(dateToZipLong(d)); }
474
475    /**
476     * <p>
477     * Sets the access time as a java.util.Date
478     * of this zip entry.  Supplied value is truncated to per-second
479     * precision (milliseconds zeroed-out).
480     * </p><p>
481     * Note: the setters for flags and timestamps are decoupled.
482     * Even if the timestamp is not-null, it will only be written
483     * out if the corresponding bit in the flags is also set.
484     * </p>
485     *
486     * @param d access time as java.util.Date
487     */
488    public void setAccessJavaTime(final Date d) { setAccessTime(dateToZipLong(d)); }
489
490    /**
491     * <p>
492     * Sets the create time as a java.util.Date
493     * of this zip entry.  Supplied value is truncated to per-second
494     * precision (milliseconds zeroed-out).
495     * </p><p>
496     * Note: the setters for flags and timestamps are decoupled.
497     * Even if the timestamp is not-null, it will only be written
498     * out if the corresponding bit in the flags is also set.
499     * </p>
500     *
501     * @param d create time as java.util.Date
502     */
503    public void setCreateJavaTime(final Date d) { setCreateTime(dateToZipLong(d)); }
504
505    /**
506     * Utility method converts java.util.Date (milliseconds since epoch)
507     * into a ZipLong (seconds since epoch).
508     * <p/>
509     * Also makes sure the converted ZipLong is not too big to fit
510     * in 32 unsigned bits.
511     *
512     * @param d java.util.Date to convert to ZipLong
513     * @return ZipLong
514     */
515    private static ZipLong dateToZipLong(final Date d) {
516        if (d == null) { return null; }
517
518        return unixTimeToZipLong(d.getTime() / 1000);
519    }
520
521    /**
522     * Returns a String representation of this class useful for
523     * debugging purposes.
524     *
525     * @return A String representation of this class useful for
526     *         debugging purposes.
527     */
528    @Override
529    public String toString() {
530        final StringBuilder buf = new StringBuilder();
531        buf.append("0x5455 Zip Extra Field: Flags=");
532        buf.append(Integer.toBinaryString(ZipUtil.unsignedIntToSignedByte(flags))).append(" ");
533        if (bit0_modifyTimePresent && modifyTime != null) {
534            final Date m = getModifyJavaTime();
535            buf.append(" Modify:[").append(m).append("] ");
536        }
537        if (bit1_accessTimePresent && accessTime != null) {
538            final Date a = getAccessJavaTime();
539            buf.append(" Access:[").append(a).append("] ");
540        }
541        if (bit2_createTimePresent && createTime != null) {
542            final Date c = getCreateJavaTime();
543            buf.append(" Create:[").append(c).append("] ");
544        }
545        return buf.toString();
546    }
547
548    @Override
549    public Object clone() throws CloneNotSupportedException {
550        return super.clone();
551    }
552
553    @Override
554    public boolean equals(final Object o) {
555        if (o instanceof X5455_ExtendedTimestamp) {
556            final X5455_ExtendedTimestamp xf = (X5455_ExtendedTimestamp) o;
557
558            // The ZipLong==ZipLong clauses handle the cases where both are null.
559            // and only last 3 bits of flags matter.
560            return ((flags & 0x07) == (xf.flags & 0x07)) &&
561                    (modifyTime == xf.modifyTime || (modifyTime != null && modifyTime.equals(xf.modifyTime))) &&
562                    (accessTime == xf.accessTime || (accessTime != null && accessTime.equals(xf.accessTime))) &&
563                    (createTime == xf.createTime || (createTime != null && createTime.equals(xf.createTime)));
564        }
565        return false;
566    }
567
568    @Override
569    public int hashCode() {
570        int hc = (-123 * (flags & 0x07)); // only last 3 bits of flags matter
571        if (modifyTime != null) {
572            hc ^= modifyTime.hashCode();
573        }
574        if (accessTime != null) {
575            // Since accessTime is often same as modifyTime,
576            // this prevents them from XOR negating each other.
577            hc ^= Integer.rotateLeft(accessTime.hashCode(), 11);
578        }
579        if (createTime != null) {
580            hc ^= Integer.rotateLeft(createTime.hashCode(), 22);
581        }
582        return hc;
583    }
584
585    private static Date zipLongToDate(ZipLong unixTime) {
586        return unixTime != null ? new Date(unixTime.getIntValue() * 1000L) : null;
587    }
588
589    private static ZipLong unixTimeToZipLong(long l) {
590        if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
591            throw new IllegalArgumentException("X5455 timestamps must fit in a signed 32 bit integer: " + l);
592        }
593        return new ZipLong(l);
594    }
595
596}