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    public ZipShort getHeaderId() {
130        return HEADER_ID;
131    }
132
133    /**
134     * Length of the extra field in the local file data - without
135     * Header-ID or length specifier.
136     *
137     * @return a <code>ZipShort</code> for the length of the data of this extra field
138     */
139    public ZipShort getLocalFileDataLength() {
140        return new ZipShort(1 +
141                (bit0_modifyTimePresent ? 4 : 0) +
142                (bit1_accessTimePresent && accessTime != null ? 4 : 0) +
143                (bit2_createTimePresent && createTime != null ? 4 : 0)
144        );
145    }
146
147    /**
148     * Length of the extra field in the local file data - without
149     * Header-ID or length specifier.
150     *
151     * <p>For X5455 the central length is often smaller than the
152     * local length, because central cannot contain access or create
153     * timestamps.</p>
154     *
155     * @return a <code>ZipShort</code> for the length of the data of this extra field
156     */
157    public ZipShort getCentralDirectoryLength() {
158        return new ZipShort(1 +
159                (bit0_modifyTimePresent ? 4 : 0)
160        );
161    }
162
163    /**
164     * The actual data to put into local file data - without Header-ID
165     * or length specifier.
166     *
167     * @return get the data
168     */
169    public byte[] getLocalFileDataData() {
170        byte[] data = new byte[getLocalFileDataLength().getValue()];
171        int pos = 0;
172        data[pos++] = 0;
173        if (bit0_modifyTimePresent) {
174            data[0] |= MODIFY_TIME_BIT;
175            System.arraycopy(modifyTime.getBytes(), 0, data, pos, 4);
176            pos += 4;
177        }
178        if (bit1_accessTimePresent && accessTime != null) {
179            data[0] |= ACCESS_TIME_BIT;
180            System.arraycopy(accessTime.getBytes(), 0, data, pos, 4);
181            pos += 4;
182        }
183        if (bit2_createTimePresent && createTime != null) {
184            data[0] |= CREATE_TIME_BIT;
185            System.arraycopy(createTime.getBytes(), 0, data, pos, 4);
186            pos += 4;
187        }
188        return data;
189    }
190
191    /**
192     * The actual data to put into central directory data - without Header-ID
193     * or length specifier.
194     *
195     * @return the central directory data
196     */
197    public byte[] getCentralDirectoryData() {
198        byte[] centralData = new byte[getCentralDirectoryLength().getValue()];
199        byte[] localData = getLocalFileDataData();
200
201        // Truncate out create & access time (last 8 bytes) from
202        // the copy of the local data we obtained:
203        System.arraycopy(localData, 0, centralData, 0, centralData.length);
204        return centralData;
205    }
206
207    /**
208     * Populate data from this array as if it was in local file data.
209     *
210     * @param data   an array of bytes
211     * @param offset the start offset
212     * @param length the number of bytes in the array from offset
213     * @throws java.util.zip.ZipException on error
214     */
215    public void parseFromLocalFileData(
216            byte[] data, int offset, int length
217    ) throws ZipException {
218        reset();
219        final int len = offset + length;
220        setFlags(data[offset++]);
221        if (bit0_modifyTimePresent) {
222            modifyTime = new ZipLong(data, offset);
223            offset += 4;
224        }
225
226        // Notice the extra length check in case we are parsing the shorter
227        // central data field (for both access and create timestamps).
228        if (bit1_accessTimePresent && offset + 4 <= len) {
229            accessTime = new ZipLong(data, offset);
230            offset += 4;
231        }
232        if (bit2_createTimePresent && offset + 4 <= len) {
233            createTime = new ZipLong(data, offset);
234            offset += 4;
235        }
236    }
237
238    /**
239     * Doesn't do anything special since this class always uses the
240     * same parsing logic for both central directory and local file data.
241     */
242    public void parseFromCentralDirectoryData(
243            byte[] buffer, int offset, int length
244    ) throws ZipException {
245        reset();
246        parseFromLocalFileData(buffer, offset, length);
247    }
248
249    /**
250     * Reset state back to newly constructed state.  Helps us make sure
251     * parse() calls always generate clean results.
252     */
253    private void reset() {
254        setFlags((byte) 0);
255        this.modifyTime = null;
256        this.accessTime = null;
257        this.createTime = null;
258    }
259
260    /**
261     * Sets flags byte.  The flags byte tells us which of the
262     * three datestamp fields are present in the data:
263     * <pre>
264     * bit0 - modify time
265     * bit1 - access time
266     * bit2 - create time
267     * </pre>
268     * Only first 3 bits of flags are used according to the
269     * latest version of the spec (December 2012).
270     *
271     * @param flags flags byte indicating which of the
272     *              three datestamp fields are present.
273     */
274    public void setFlags(byte flags) {
275        this.flags = flags;
276        this.bit0_modifyTimePresent = (flags & MODIFY_TIME_BIT) == MODIFY_TIME_BIT;
277        this.bit1_accessTimePresent = (flags & ACCESS_TIME_BIT) == ACCESS_TIME_BIT;
278        this.bit2_createTimePresent = (flags & CREATE_TIME_BIT) == CREATE_TIME_BIT;
279    }
280
281    /**
282     * Gets flags byte.  The flags byte tells us which of the
283     * three datestamp fields are present in the data:
284     * <pre>
285     * bit0 - modify time
286     * bit1 - access time
287     * bit2 - create time
288     * </pre>
289     * Only first 3 bits of flags are used according to the
290     * latest version of the spec (December 2012).
291     *
292     * @return flags byte indicating which of the
293     *         three datestamp fields are present.
294     */
295    public byte getFlags() { return flags; }
296
297    /**
298     * Returns whether bit0 of the flags byte is set or not,
299     * which should correspond to the presence or absence of
300     * a modify timestamp in this particular zip entry.
301     *
302     * @return true if bit0 of the flags byte is set.
303     */
304    public boolean isBit0_modifyTimePresent() { return bit0_modifyTimePresent; }
305
306    /**
307     * Returns whether bit1 of the flags byte is set or not,
308     * which should correspond to the presence or absence of
309     * a "last access" timestamp in this particular zip entry.
310     *
311     * @return true if bit1 of the flags byte is set.
312     */
313    public boolean isBit1_accessTimePresent() { return bit1_accessTimePresent; }
314
315    /**
316     * Returns whether bit2 of the flags byte is set or not,
317     * which should correspond to the presence or absence of
318     * a create timestamp in this particular zip entry.
319     *
320     * @return true if bit2 of the flags byte is set.
321     */
322    public boolean isBit2_createTimePresent() { return bit2_createTimePresent; }
323
324    /**
325     * Returns the modify time (seconds since epoch) of this zip entry
326     * as a ZipLong object, or null if no such timestamp exists in the
327     * zip entry.
328     *
329     * @return modify time (seconds since epoch) or null.
330     */
331    public ZipLong getModifyTime() { return modifyTime; }
332
333    /**
334     * Returns the access time (seconds since epoch) of this zip entry
335     * as a ZipLong object, or null if no such timestamp exists in the
336     * zip entry.
337     *
338     * @return access time (seconds since epoch) or null.
339     */
340    public ZipLong getAccessTime() { return accessTime; }
341
342    /**
343     * <p>
344     * Returns the create 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     * </p><p>
348     * Note: modern linux file systems (e.g., ext2)
349     * do not appear to store a "create time" value, and so
350     * it's usually omitted altogether in the zip extra
351     * field.  Perhaps other unix systems track this.
352     *
353     * @return create time (seconds since epoch) or null.
354     */
355    public ZipLong getCreateTime() { return createTime; }
356
357    /**
358     * Returns the modify time as a java.util.Date
359     * of this zip entry, or null if no such timestamp exists in the zip entry.
360     * The milliseconds are always zeroed out, since the underlying data
361     * offers only per-second precision.
362     *
363     * @return modify time as java.util.Date or null.
364     */
365    public Date getModifyJavaTime() {
366        return modifyTime != null ? new Date(modifyTime.getValue() * 1000) : null;
367    }
368
369    /**
370     * Returns the access time as a java.util.Date
371     * of this zip entry, or null if no such timestamp exists in the zip entry.
372     * The milliseconds are always zeroed out, since the underlying data
373     * offers only per-second precision.
374     *
375     * @return access time as java.util.Date or null.
376     */
377    public Date getAccessJavaTime() {
378        return accessTime != null ? new Date(accessTime.getValue() * 1000) : null;
379    }
380
381    /**
382     * <p>
383     * Returns the create time as a a java.util.Date
384     * of this zip entry, or null if no such timestamp exists in the zip entry.
385     * The milliseconds are always zeroed out, since the underlying data
386     * offers only per-second precision.
387     * </p><p>
388     * Note: modern linux file systems (e.g., ext2)
389     * do not appear to store a "create time" value, and so
390     * it's usually omitted altogether in the zip extra
391     * field.  Perhaps other unix systems track this.
392     *
393     * @return create time as java.util.Date or null.
394     */
395    public Date getCreateJavaTime() {
396        return createTime != null ? new Date(createTime.getValue() * 1000) : null;
397    }
398
399    /**
400     * <p>
401     * Sets the modify time (seconds since epoch) of this zip entry
402     * using a ZipLong object.
403     * </p><p>
404     * Note: the setters for flags and timestamps are decoupled.
405     * Even if the timestamp is not-null, it will only be written
406     * out if the corresponding bit in the flags is also set.
407     * </p>
408     *
409     * @param l ZipLong of the modify time (seconds per epoch)
410     */
411    public void setModifyTime(ZipLong l) {
412        bit0_modifyTimePresent = l != null;
413        flags = (byte) (l != null ? (flags | MODIFY_TIME_BIT)
414                        : (flags & ~MODIFY_TIME_BIT));
415        this.modifyTime = l;
416    }
417
418    /**
419     * <p>
420     * Sets the access time (seconds since epoch) of this zip entry
421     * using a ZipLong object
422     * </p><p>
423     * Note: the setters for flags and timestamps are decoupled.
424     * Even if the timestamp is not-null, it will only be written
425     * out if the corresponding bit in the flags is also set.
426     * </p>
427     *
428     * @param l ZipLong of the access time (seconds per epoch)
429     */
430    public void setAccessTime(ZipLong l) {
431        bit1_accessTimePresent = l != null;
432        flags = (byte) (l != null ? (flags | ACCESS_TIME_BIT)
433                        : (flags & ~ACCESS_TIME_BIT));
434        this.accessTime = l;
435    }
436
437    /**
438     * <p>
439     * Sets the create time (seconds since epoch) of this zip entry
440     * using a ZipLong object
441     * </p><p>
442     * Note: the setters for flags and timestamps are decoupled.
443     * Even if the timestamp is not-null, it will only be written
444     * out if the corresponding bit in the flags is also set.
445     * </p>
446     *
447     * @param l ZipLong of the create time (seconds per epoch)
448     */
449    public void setCreateTime(ZipLong l) {
450        bit2_createTimePresent = l != null;
451        flags = (byte) (l != null ? (flags | CREATE_TIME_BIT)
452                        : (flags & ~CREATE_TIME_BIT));
453        this.createTime = l;
454    }
455
456    /**
457     * <p>
458     * Sets the modify time as a java.util.Date
459     * of this zip entry.  Supplied value is truncated to per-second
460     * precision (milliseconds zeroed-out).
461     * </p><p>
462     * Note: the setters for flags and timestamps are decoupled.
463     * Even if the timestamp is not-null, it will only be written
464     * out if the corresponding bit in the flags is also set.
465     * </p>
466     *
467     * @param d modify time as java.util.Date
468     */
469    public void setModifyJavaTime(Date d) { setModifyTime(dateToZipLong(d)); }
470
471    /**
472     * <p>
473     * Sets the access time as a java.util.Date
474     * of this zip entry.  Supplied value is truncated to per-second
475     * precision (milliseconds zeroed-out).
476     * </p><p>
477     * Note: the setters for flags and timestamps are decoupled.
478     * Even if the timestamp is not-null, it will only be written
479     * out if the corresponding bit in the flags is also set.
480     * </p>
481     *
482     * @param d access time as java.util.Date
483     */
484    public void setAccessJavaTime(Date d) { setAccessTime(dateToZipLong(d)); }
485
486    /**
487     * <p>
488     * Sets the create time as a java.util.Date
489     * of this zip entry.  Supplied value is truncated to per-second
490     * precision (milliseconds zeroed-out).
491     * </p><p>
492     * Note: the setters for flags and timestamps are decoupled.
493     * Even if the timestamp is not-null, it will only be written
494     * out if the corresponding bit in the flags is also set.
495     * </p>
496     *
497     * @param d create time as java.util.Date
498     */
499    public void setCreateJavaTime(Date d) { setCreateTime(dateToZipLong(d)); }
500
501    /**
502     * Utility method converts java.util.Date (milliseconds since epoch)
503     * into a ZipLong (seconds since epoch).
504     * <p/>
505     * Also makes sure the converted ZipLong is not too big to fit
506     * in 32 unsigned bits.
507     *
508     * @param d java.util.Date to convert to ZipLong
509     * @return ZipLong
510     */
511    private static ZipLong dateToZipLong(final Date d) {
512        if (d == null) { return null; }
513
514        final long TWO_TO_32 = 0x100000000L;
515        final long l = d.getTime() / 1000;
516        if (l >= TWO_TO_32) {
517            throw new IllegalArgumentException("Cannot set an X5455 timestamp larger than 2^32: " + l);
518        }
519        return new ZipLong(l);
520    }
521
522    /**
523     * Returns a String representation of this class useful for
524     * debugging purposes.
525     *
526     * @return A String representation of this class useful for
527     *         debugging purposes.
528     */
529    @Override
530    public String toString() {
531        StringBuilder buf = new StringBuilder();
532        buf.append("0x5455 Zip Extra Field: Flags=");
533        buf.append(Integer.toBinaryString(ZipUtil.unsignedIntToSignedByte(flags))).append(" ");
534        if (bit0_modifyTimePresent && modifyTime != null) {
535            Date m = getModifyJavaTime();
536            buf.append(" Modify:[").append(m).append("] ");
537        }
538        if (bit1_accessTimePresent && accessTime != null) {
539            Date a = getAccessJavaTime();
540            buf.append(" Access:[").append(a).append("] ");
541        }
542        if (bit2_createTimePresent && createTime != null) {
543            Date c = getCreateJavaTime();
544            buf.append(" Create:[").append(c).append("] ");
545        }
546        return buf.toString();
547    }
548
549    @Override
550    public Object clone() throws CloneNotSupportedException {
551        return super.clone();
552    }
553
554    @Override
555    public boolean equals(Object o) {
556        if (o instanceof X5455_ExtendedTimestamp) {
557            X5455_ExtendedTimestamp xf = (X5455_ExtendedTimestamp) o;
558
559            // The ZipLong==ZipLong clauses handle the cases where both are null.
560            // and only last 3 bits of flags matter.
561            return ((flags & 0x07) == (xf.flags & 0x07)) &&
562                    (modifyTime == xf.modifyTime || (modifyTime != null && modifyTime.equals(xf.modifyTime))) &&
563                    (accessTime == xf.accessTime || (accessTime != null && accessTime.equals(xf.accessTime))) &&
564                    (createTime == xf.createTime || (createTime != null && createTime.equals(xf.createTime)));
565        } else {
566            return false;
567        }
568    }
569
570    @Override
571    public int hashCode() {
572        int hc = (-123 * (flags & 0x07)); // only last 3 bits of flags matter
573        if (modifyTime != null) {
574            hc ^= modifyTime.hashCode();
575        }
576        if (accessTime != null) {
577            // Since accessTime is often same as modifyTime,
578            // this prevents them from XOR negating each other.
579            hc ^= Integer.rotateLeft(accessTime.hashCode(), 11);
580        }
581        if (createTime != null) {
582            hc ^= Integer.rotateLeft(createTime.hashCode(), 22);
583        }
584        return hc;
585    }
586
587}