Completed
Push — master ( b3b676...8fdc21 )
by Alexey
06:06 queued 11s
created

AsiExtraField::getLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace PhpZip\Model\Extra\Fields;
4
5
use PhpZip\Constants\UnixStat;
6
use PhpZip\Exception\Crc32Exception;
7
use PhpZip\Model\Extra\ZipExtraField;
8
use PhpZip\Model\ZipEntry;
9
10
/**
11
 * ASi Unix Extra Field:
12
 * ====================.
13
 *
14
 * The following is the layout of the ASi extra block for Unix.  The
15
 * local-header and central-header versions are identical.
16
 * (Last Revision 19960916)
17
 *
18
 * Value         Size        Description
19
 * -----         ----        -----------
20
 * (Unix3) 0x756e        Short       tag for this extra block type ("nu")
21
 * TSize         Short       total data size for this block
22
 * CRC           Long        CRC-32 of the remaining data
23
 * Mode          Short       file permissions
24
 * SizDev        Long        symlink'd size OR major/minor dev num
25
 * UID           Short       user ID
26
 * GID           Short       group ID
27
 * (var.)        variable    symbolic link filename
28
 *
29
 * Mode is the standard Unix st_mode field from struct stat, containing
30
 * user/group/other permissions, setuid/setgid and symlink info, etc.
31
 *
32
 * If Mode indicates that this file is a symbolic link, SizDev is the
33
 * size of the file to which the link points.  Otherwise, if the file
34
 * is a device, SizDev contains the standard Unix st_rdev field from
35
 * struct stat (includes the major and minor numbers of the device).
36
 * SizDev is undefined in other cases.
37
 *
38
 * If Mode indicates that the file is a symbolic link, the final field
39
 * will be the name of the file to which the link points.  The file-
40
 * name length can be inferred from TSize.
41
 *
42
 * [Note that TSize may incorrectly refer to the data size not counting
43
 * the CRC; i.e., it may be four bytes too small.]
44
 *
45
 * @see ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip Info-ZIP version Specification
46
 */
47
class AsiExtraField implements ZipExtraField
48
{
49
    /** @var int Header id */
50
    const HEADER_ID = 0x756e;
51
52
    const USER_GID_PID = 1000;
53
54
    /** Bits used for permissions (and sticky bit). */
55
    const PERM_MASK = 07777;
56
57
    /** @var int Standard Unix stat(2) file mode. */
58
    private $mode;
59
60
    /** @var int User ID. */
61
    private $uid;
62
63
    /** @var int Group ID. */
64
    private $gid;
65
66
    /**
67
     * @var string File this entry points to, if it is a symbolic link.
68
     *             Empty string - if entry is not a symbolic link.
69
     */
70
    private $link;
71
72
    /**
73
     * AsiExtraField constructor.
74
     *
75
     * @param int    $mode
76
     * @param int    $uid
77
     * @param int    $gid
78
     * @param string $link
79
     */
80 6
    public function __construct($mode, $uid = self::USER_GID_PID, $gid = self::USER_GID_PID, $link = '')
81
    {
82 6
        $this->mode = $mode;
83 6
        $this->uid = $uid;
84 6
        $this->gid = $gid;
85 6
        $this->link = $link;
86 6
    }
87
88
    /**
89
     * Returns the Header ID (type) of this Extra Field.
90
     * The Header ID is an unsigned short integer (two bytes)
91
     * which must be constant during the life cycle of this object.
92
     *
93
     * @return int
94
     */
95 5
    public function getHeaderId()
96
    {
97 5
        return self::HEADER_ID;
98
    }
99
100
    /**
101
     * Populate data from this array as if it was in local file data.
102
     *
103
     * @param string        $buffer the buffer to read data from
104
     * @param ZipEntry|null $entry
105
     *
106
     * @throws Crc32Exception
107
     *
108
     * @return static
109
     */
110 3
    public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
111
    {
112 3
        $givenChecksum = unpack('V', $buffer)[1];
113 3
        $buffer = substr($buffer, 4);
114 3
        $realChecksum = crc32($buffer);
115
116 3
        if ($givenChecksum !== $realChecksum) {
117 1
            throw new Crc32Exception('Asi Unix Extra Filed Data', $givenChecksum, $realChecksum);
118
        }
119
120 2
        $data = unpack('vmode/VlinkSize/vuid/vgid', $buffer);
121 2
        $link = '';
122
123 2
        if ($data['linkSize'] > 0) {
124 1
            $link = substr($buffer, 10);
125
        }
126
127 2
        return new self($data['mode'], $data['uid'], $data['gid'], $link);
128
    }
129
130
    /**
131
     * Populate data from this array as if it was in central directory data.
132
     *
133
     * @param string        $buffer the buffer to read data from
134
     * @param ZipEntry|null $entry
135
     *
136
     * @throws Crc32Exception
137
     *
138
     * @return AsiExtraField
139
     */
140 2
    public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
141
    {
142 2
        return self::unpackLocalFileData($buffer, $entry);
143
    }
144
145
    /**
146
     * The actual data to put into local file data - without Header-ID
147
     * or length specifier.
148
     *
149
     * @return string the data
150
     */
151 2
    public function packLocalFileData()
152
    {
153 2
        $data = pack(
154 2
            'vVvv',
155 2
            $this->mode,
156 2
            \strlen($this->link),
157 2
            $this->uid,
158 2
            $this->gid
159 2
        ) . $this->link;
160
161 2
        return pack('V', crc32($data)) . $data;
162
    }
163
164
    /**
165
     * The actual data to put into central directory - without Header-ID or
166
     * length specifier.
167
     *
168
     * @return string the data
169
     */
170 2
    public function packCentralDirData()
171
    {
172 2
        return $this->packLocalFileData();
173
    }
174
175
    /**
176
     * Name of linked file.
177
     *
178
     * @return string name of the file this entry links to if it is a
179
     *                symbolic link, the empty string otherwise
180
     */
181 3
    public function getLink()
182
    {
183 3
        return $this->link;
184
    }
185
186
    /**
187
     * Indicate that this entry is a symbolic link to the given filename.
188
     *
189
     * @param string $link name of the file this entry links to, empty
190
     *                     string if it is not a symbolic link
191
     */
192 1
    public function setLink($link)
193
    {
194 1
        $this->link = (string) $link;
195 1
        $this->mode = $this->getPermissionsMode($this->mode);
196 1
    }
197
198
    /**
199
     * Is this entry a symbolic link?
200
     *
201
     * @return bool true if this is a symbolic link
202
     */
203 1
    public function isLink()
204
    {
205 1
        return !empty($this->link);
206
    }
207
208
    /**
209
     * Get the file mode for given permissions with the correct file type.
210
     *
211
     * @param int $mode the mode
212
     *
213
     * @return int the type with the mode
214
     */
215 1
    protected function getPermissionsMode($mode)
216
    {
217 1
        $type = 0;
218
219 1
        if ($this->isLink()) {
220 1
            $type = UnixStat::UNX_IFLNK;
221 1
        } elseif (($mode & UnixStat::UNX_IFREG) !== 0) {
222 1
            $type = UnixStat::UNX_IFREG;
223
        } elseif (($mode & UnixStat::UNX_IFDIR) !== 0) {
224
            $type = UnixStat::UNX_IFDIR;
225
        }
226
227 1
        return $type | ($mode & self::PERM_MASK);
228
    }
229
230
    /**
231
     * Is this entry a directory?
232
     *
233
     * @return bool true if this entry is a directory
234
     */
235
    public function isDirectory()
236
    {
237
        return ($this->mode & UnixStat::UNX_IFDIR) !== 0 && !$this->isLink();
238
    }
239
240
    /**
241
     * @return int
242
     */
243 4
    public function getMode()
244
    {
245 4
        return $this->mode;
246
    }
247
248
    /**
249
     * @param int $mode
250
     */
251 1
    public function setMode($mode)
252
    {
253 1
        $this->mode = $this->getPermissionsMode($mode);
254 1
    }
255
256
    /**
257
     * @return int
258
     */
259 3
    public function getUserId()
260
    {
261 3
        return $this->uid;
262
    }
263
264
    /**
265
     * @param int $uid
266
     */
267 1
    public function setUserId($uid)
268
    {
269 1
        $this->uid = (int) $uid;
270 1
    }
271
272
    /**
273
     * @return int
274
     */
275 3
    public function getGroupId()
276
    {
277 3
        return $this->gid;
278
    }
279
280
    /**
281
     * @param int $gid
282
     */
283 1
    public function setGroupId($gid)
284
    {
285 1
        $this->gid = (int) $gid;
286 1
    }
287
288
    /**
289
     * @return string
290
     */
291
    public function __toString()
292
    {
293
        return sprintf(
294
            '0x%04x ASI: Mode=%o UID=%d GID=%d Link="%s',
295
            self::HEADER_ID,
296
            $this->mode,
297
            $this->uid,
298
            $this->gid,
299
            $this->link
300
        );
301
    }
302
}
303