Part   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 287
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 2
Metric Value
wmc 36
eloc 99
c 7
b 0
f 2
dl 0
loc 287
rs 9.52

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A parseDescription() 0 5 2
B parse() 0 40 7
A findHeaders() 0 11 2
A parseSubtype() 0 13 5
B parseEncoding() 0 22 7
A parseDisposition() 0 5 3
B isAttachment() 0 9 9
1
<?php
2
/*
3
* File: Part.php
4
* Category: -
5
* Author: M.Goldenbaum
6
* Created: 17.09.20 20:38
7
* Updated: -
8
*
9
* Description:
10
*  -
11
*/
12
13
namespace Webklex\PHPIMAP;
14
15
16
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
17
18
/**
19
 * Class Part
20
 *
21
 * @package Webklex\PHPIMAP
22
 */
23
class Part {
24
25
    /**
26
     * Raw part
27
     *
28
     * @var string $raw
29
     */
30
    public $raw = "";
31
32
    /**
33
     * Part type
34
     *
35
     * @var int $type
36
     */
37
    public $type = IMAP::MESSAGE_TYPE_TEXT;
38
39
    /**
40
     * Part content
41
     *
42
     * @var string $content
43
     */
44
    public $content = "";
45
46
    /**
47
     * Part subtype
48
     *
49
     * @var string $subtype
50
     */
51
    public $subtype = null;
52
53
    /**
54
     * Part charset - if available
55
     *
56
     * @var string $charset
57
     */
58
    public $charset = "utf-8";
59
60
    /**
61
     * Part encoding method
62
     *
63
     * @var int $encoding
64
     */
65
    public $encoding = IMAP::MESSAGE_ENC_OTHER;
66
67
    /**
68
     * Alias to check if the part is an attachment
69
     *
70
     * @var boolean $ifdisposition
71
     */
72
    public $ifdisposition = false;
73
74
    /**
75
     * Indicates if the part is an attachment
76
     *
77
     * @var string $disposition
78
     */
79
    public $disposition = null;
80
81
    /**
82
     * Alias to check if the part has a description
83
     *
84
     * @var boolean $ifdescription
85
     */
86
    public $ifdescription = false;
87
88
    /**
89
     * Part description if available
90
     *
91
     * @var string $description
92
     */
93
    public $description = null;
94
95
    /**
96
     * Part filename if available
97
     *
98
     * @var string $filename
99
     */
100
    public $filename = null;
101
102
    /**
103
     * Part name if available
104
     *
105
     * @var string $name
106
     */
107
    public $name = null;
108
109
    /**
110
     * Part id if available
111
     *
112
     * @var string $id
113
     */
114
    public $id = null;
115
116
    /**
117
     * The part number of the current part
118
     *
119
     * @var integer $part_number
120
     */
121
    public $part_number = 0;
122
123
    /**
124
     * Part length in bytes
125
     *
126
     * @var integer $bytes
127
     */
128
    public $bytes = null;
129
130
    /**
131
     * Part content type
132
     *
133
     * @var string|null $content_type
134
     */
135
    public $content_type = null;
136
137
    /**
138
     * @var Header $header
139
     */
140
    private $header = null;
141
142
    /**
143
     * Part constructor.
144
     * @param $raw_part
145
     * @param Header|null $header
146
     * @param integer $part_number
147
     *
148
     * @throws InvalidMessageDateException
149
     */
150
    public function __construct($raw_part, Header $header = null, int $part_number = 0) {
151
        $this->raw = $raw_part;
152
        $this->header = $header;
153
        $this->part_number = $part_number;
154
        $this->parse();
155
    }
156
157
    /**
158
     * Parse the raw parts
159
     *
160
     * @throws InvalidMessageDateException
161
     */
162
    protected function parse(){
163
        if ($this->header === null) {
164
            $body = $this->findHeaders();
165
        }else{
166
            $body = $this->raw;
167
        }
168
169
        $this->parseDisposition();
170
        $this->parseDescription();
171
        $this->parseEncoding();
172
173
        $this->charset = $this->header->get("charset");
174
        $this->name = $this->header->get("name");
175
        $this->filename = $this->header->get("filename");
176
177
        if(!empty($this->header->get("id"))) {
178
            $this->id = $this->header->get("id");
179
        } else if(!empty($this->header->get("x_attachment_id"))){
180
            $this->id = $this->header->get("x_attachment_id");
181
        } else if(!empty($this->header->get("content_id"))){
182
            $this->id = strtr($this->header->get("content_id"), [
183
                '<' => '',
184
                '>' => ''
185
            ]);
186
        }
187
188
        $content_types = $this->header->get("content_type");
189
        if(!empty($content_types)){
190
            $this->subtype = $this->parseSubtype($content_types);
191
            $content_type = $content_types;
192
            if (is_array($content_types)) {
193
                $content_type = $content_types[0];
194
            }
195
            $parts = explode(';', $content_type);
196
            $this->content_type = trim($parts[0]);
197
        }
198
199
200
        $this->content = trim(rtrim($body));
201
        $this->bytes = strlen($this->content);
202
    }
203
204
    /**
205
     * Find all available headers and return the leftover body segment
206
     *
207
     * @return string
208
     * @throws InvalidMessageDateException
209
     */
210
    private function findHeaders(): string {
211
        $body = $this->raw;
212
        while (($pos = strpos($body, "\r\n")) > 0) {
213
            $body = substr($body, $pos + 2);
214
        }
215
        $headers = substr($this->raw, 0, strlen($body) * -1);
216
        $body = substr($body, 0, -2);
217
218
        $this->header = new Header($headers);
219
220
        return $body;
221
    }
222
223
    /**
224
     * Try to parse the subtype if any is present
225
     * @param $content_type
226
     *
227
     * @return string
228
     */
229
    private function parseSubtype($content_type){
230
        if (is_array($content_type)) {
231
            foreach ($content_type as $part){
232
                if ((strpos($part, "/")) !== false){
233
                    return $this->parseSubtype($part);
234
                }
235
            }
236
            return null;
237
        }
238
        if (($pos = strpos($content_type, "/")) !== false){
239
            return substr($content_type, $pos + 1);
240
        }
241
        return null;
242
    }
243
244
    /**
245
     * Try to parse the disposition if any is present
246
     */
247
    private function parseDisposition(){
248
        $content_disposition = $this->header->get("content_disposition");
249
        if($content_disposition !== null) {
250
            $this->ifdisposition = true;
251
            $this->disposition = (is_array($content_disposition)) ? implode(' ', $content_disposition) : $content_disposition;
252
        }
253
    }
254
255
    /**
256
     * Try to parse the description if any is present
257
     */
258
    private function parseDescription(){
259
        $content_description = $this->header->get("content_description");
260
        if($content_description !== null) {
261
            $this->ifdescription = true;
262
            $this->description = $content_description;
263
        }
264
    }
265
266
    /**
267
     * Try to parse the encoding if any is present
268
     */
269
    private function parseEncoding(){
270
        $encoding = $this->header->get("content_transfer_encoding");
271
        if($encoding !== null) {
272
            switch (strtolower($encoding)) {
273
                case "quoted-printable":
274
                    $this->encoding = IMAP::MESSAGE_ENC_QUOTED_PRINTABLE;
275
                    break;
276
                case "base64":
277
                    $this->encoding = IMAP::MESSAGE_ENC_BASE64;
278
                    break;
279
                case "7bit":
280
                    $this->encoding = IMAP::MESSAGE_ENC_7BIT;
281
                    break;
282
                case "8bit":
283
                    $this->encoding = IMAP::MESSAGE_ENC_8BIT;
284
                    break;
285
                case "binary":
286
                    $this->encoding = IMAP::MESSAGE_ENC_BINARY;
287
                    break;
288
                default:
289
                    $this->encoding = IMAP::MESSAGE_ENC_OTHER;
290
                    break;
291
292
            }
293
        }
294
    }
295
296
    /**
297
     * Check if the current part represents an attachment
298
     *
299
     * @return bool
300
     */
301
    public function isAttachment(): bool {
302
        $valid_disposition = in_array(strtolower($this->disposition ?? ''), ClientManager::get('options.dispositions'));
0 ignored issues
show
Bug introduced by
It seems like Webklex\PHPIMAP\ClientMa...'options.dispositions') can also be of type null; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

302
        $valid_disposition = in_array(strtolower($this->disposition ?? ''), /** @scrutinizer ignore-type */ ClientManager::get('options.dispositions'));
Loading history...
303
304
        if ($this->type == IMAP::MESSAGE_TYPE_TEXT && ($this->ifdisposition == 0 || empty($this->disposition) || !$valid_disposition)) {
305
            if (($this->subtype == null || in_array((strtolower($this->subtype)), ["plain", "html"])) && $this->filename == null && $this->name == null) {
306
                return false;
307
            }
308
        }
309
        return true;
310
    }
311
312
}
313