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}