Completed
Push — master ( 014d21...1b6ebf )
by Alex
02:06
created

Client::connect()   A

Complexity

Conditions 5
Paths 13

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 3 Features 0
Metric Value
cc 5
eloc 15
c 3
b 3
f 0
nc 13
nop 5
dl 0
loc 28
rs 9.4555
1
<?php
2
namespace Mezon\Pop3;
3
4
/**
5
 * Class Client
6
 *
7
 * @package Mezon
8
 * @subpackage Pop3Client
9
 * @author Dodonov A.A.
10
 * @version v.1.0 (2019/08/13)
11
 * @copyright Copyright (c) 2019, aeon.org
12
 */
13
14
/**
15
 * POP3 protocol client.
16
 */
17
class Client
18
{
19
20
    /**
21
     * Connection
22
     *
23
     * @var resource
24
     */
25
    private $connection = null;
26
27
    /**
28
     * Method returns connection
29
     *
30
     * @param string $server
31
     *            Server domain
32
     * @param string $login
33
     *            Login
34
     * @param string $password
35
     *            Password
36
     * @param int $timeOut
37
     *            Timeout
38
     * @param int $port
39
     *            Port number
40
     * @return resource connection
41
     */
42
    protected function initConnection(string $server, string $login, string $password, int $timeOut = 5, int $port = 110)
0 ignored issues
show
Unused Code introduced by
The parameter $password is not used and could be removed. ( Ignorable by Annotation )

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

42
    protected function initConnection(string $server, string $login, /** @scrutinizer ignore-unused */ string $password, int $timeOut = 5, int $port = 110)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $login is not used and could be removed. ( Ignorable by Annotation )

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

42
    protected function initConnection(string $server, /** @scrutinizer ignore-unused */ string $login, string $password, int $timeOut = 5, int $port = 110)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
43
    {
44
        $errorMessage = '';
45
        $errorCode = 0;
46
47
        $context = stream_context_create([
48
            'ssl' => [
49
                'verify_peer' => false
50
            ]
51
        ]);
52
53
        $connection = stream_socket_client(
54
            $server . ":$port",
55
            $errorCode,
56
            $errorMessage,
57
            $timeOut,
58
            STREAM_CLIENT_CONNECT,
59
            $context);
60
61
        if ($connection === false) {
62
            throw (new \Exception('Connection was not established', - 1));
63
        }
64
65
        return ($connection);
66
    }
67
68
    /**
69
     * Method connects to server
70
     *
71
     * @param string $server
72
     *            Server domain
73
     * @param string $login
74
     *            Login
75
     * @param string $password
76
     *            Password
77
     * @param int $timeOut
78
     *            Timeout
79
     * @param int $port
80
     *            Port number
81
     */
82
    public function connect(string $server, string $login, string $password, int $timeOut = 5, int $port = 110)
83
    {
84
        try {
85
            $this->connection = $this->initConnection($server, $login, $password, $timeOut, $port);
86
87
            $result = fgets($this->connection, 1024);
88
89
            if (substr($result, 0, 3) !== '+OK') {
90
                throw (new \Exception('Connection. ' . $result, 0));
91
            }
92
93
            fputs($this->connection, "USER $login\r\n");
94
95
            $result = fgets($this->connection, 1024);
96
97
            if (substr($result, 0, 3) !== '+OK') {
98
                throw (new \Exception("USER $login " . $result, 0));
99
            }
100
101
            fputs($this->connection, "PASS $password\r\n");
102
103
            $result = fgets($this->connection, 1024);
104
105
            if (substr($result, 0, 3) !== '+OK') {
106
                throw (new \Exception("PASS " . $result . $login, 0));
107
            }
108
        } catch (\Exception $e) {
109
            throw ($e);
110
        }
111
    }
112
113
    /**
114
     * Constructor
115
     *
116
     * @param string $server
117
     *            Server domain
118
     * @param string $login
119
     *            Login
120
     * @param string $password
121
     *            Password
122
     * @param int $timeOut
123
     *            Timeout
124
     * @param int $port
125
     *            Port number
126
     */
127
    public function __construct(string $server, string $login, string $password, int $timeOut = 5, int $port = 110)
128
    {
129
        $this->connect($server, $login, $password, $timeOut, $port);
130
    }
131
132
    /**
133
     * Method returns emails count.
134
     */
135
    public function getCount(): int
136
    {
137
        fputs($this->connection, "STAT\r\n");
138
139
        $result = fgets($this->connection, 1024);
140
141
        if (substr($result, 0, 3) !== '+OK') {
142
            throw (new \Exception("STAT " . $result, 0));
143
        }
144
145
        $result = explode(' ', $result);
146
147
        return intval($result[1]);
148
    }
149
150
    /**
151
     * Method returns data from connection
152
     *
153
     * @return string Fetched data
154
     */
155
    protected function getData(): string
156
    {
157
        $data = '';
158
159
        while (! feof($this->connection)) {
160
            $buffer = chop(fgets($this->connection, 1024));
161
162
            if (strpos($buffer, '-ERR') === 0) {
163
                throw (new \Exception(str_replace('-ERR ', '', $buffer), 0));
164
            }
165
166
            $data .= "$buffer\r\n";
167
168
            if (trim($buffer) == '.') {
169
                break;
170
            }
171
        }
172
173
        return $data;
174
    }
175
176
    /**
177
     * Method returns email's headers
178
     *
179
     * @param int $i
180
     *            Number of the message
181
     * @return string Headers
182
     */
183
    public function getMessageHeaders(int $i): string
184
    {
185
        fputs($this->connection, "TOP $i 3\r\n");
186
187
        return $this->getData();
188
    }
189
190
    /**
191
     * Method deletes email
192
     *
193
     * @param int $i
194
     *            Number of the message
195
     * @return string Result of the deletion
196
     */
197
    public function deleteMessage($i): string
198
    {
199
        fputs($this->connection, "DELE $i\r\n");
200
201
        return fgets($this->connection);
202
    }
203
204
    /**
205
     * Method terminates session
206
     */
207
    public function quit()
208
    {
209
        fputs($this->connection, "QUIT\r\n");
210
    }
211
212
    /**
213
     * Method parses subject with any prefix
214
     *
215
     * @param string $line
216
     *            Line of the email
217
     * @param int $i
218
     *            Line cursor
219
     * @param array $headers
220
     *            Email headers
221
     * @param string $type
222
     *            Mime type
223
     * @return string Decoded data
224
     */
225
    protected function parseAnyType(string $line, int $i, array $headers, string $type): string
226
    {
227
        $subject = substr($line, 0, strlen($line) - 2);
228
229
        $count = count($headers);
230
        for ($j = $i + 1; $j < $count; $j ++) {
231
            if (substr($headers[$j], 0, 1) == ' ') {
232
                $subject .= str_replace([
233
                    ' ' . $type,
234
                    '?='
235
                ], [
236
                    '',
237
                    ''
238
                ], $headers[$j]);
239
            } else {
240
                return str_replace('Subject: ', '', iconv_mime_decode($subject . "?=\r\n", 0, "UTF-8"));
241
            }
242
        }
243
244
        return '';
245
    }
246
247
    /**
248
     * Method returns message's subject
249
     *
250
     * @param int $i
251
     *            Line number
252
     * @return string Decoded data
253
     */
254
    public function getMessageSubject(int $i): string
255
    {
256
        $headers = $this->getMessageHeaders($i);
257
258
        $headers = explode("\r\n", $headers);
259
260
        foreach ($headers as $i => $line) {
261
            if (strpos($line, 'Subject: ') === 0) {
262
                if (strpos($line, '=?UTF-8?Q?') !== false) {
263
                    return $this->parseAnyType($line, $i, $headers, '=?UTF-8?Q?');
264
                } elseif (strpos($line, '=?UTF-8?B?') !== false) {
265
                    return $this->parseAnyType($line, $i, $headers, '=?UTF-8?B?');
266
                }
267
            }
268
        }
269
270
        return '';
271
    }
272
273
    /**
274
     * Method returns true if the mail with the specified subject exists
275
     *
276
     * @param string $subject
277
     *            Searching subject
278
     * @return bool Email exists
279
     */
280
    public function messageWithSubjectExists(string $subject): bool
281
    {
282
        $count = $this->getCount();
283
284
        for ($i = 1; $i <= $count; $i ++) {
285
            $mailSubject = $this->getMessageSubject($i);
286
287
            if ($subject == $mailSubject) {
288
                return true;
289
            }
290
        }
291
292
        return false;
293
    }
294
295
    /**
296
     * Method removes all the mails with the specified subject
297
     *
298
     * @param string $subject
299
     *            subject of emails to be deleted
300
     */
301
    public function deleteMessagesWithSubject(string $subject)
302
    {
303
        $count = $this->getCount();
304
305
        for ($i = 1; $i <= $count; $i ++) {
306
            $mailSubject = $this->getMessageSubject($i);
307
308
            if ($subject == $mailSubject) {
309
                $this->deleteMessage($i);
310
            }
311
        }
312
    }
313
314
    /**
315
     * Method returns Message-ID
316
     *
317
     * @param string $headers
318
     *            email headers
319
     * @return string Message-ID
320
     */
321
    public static function getMessageId(string $headers): string
322
    {
323
        $matches = [];
324
325
        preg_match('/Message-ID: <([0-9a-zA-Z\.@\-]*)>/mi', $headers, $matches);
326
327
        return $matches[1];
328
    }
329
}
330