Issues (36)

lib/NextcloudMailCatcher.php (7 issues)

1
<?php declare(strict_types=1);
2
3
4
/**
5
 * Files_FromMail - Recover your email attachments from your cloud.
6
 *
7
 * This file is licensed under the Affero General Public License version 3 or
8
 * later. See the COPYING file.
9
 *
10
 * @author Maxence Lange <[email protected]>
11
 * @copyright 2017
12
 * @license GNU AGPL version 3 or any later version
13
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License as
16
 * published by the Free Software Foundation, either version 3 of the
17
 * License, or (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 *  You should have received a copy of the GNU Affero General Public License
25
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
26
 *
27
 */
28
29
30
$config = [
31
	'nextcloud' => 'https://cloud.example.net/',
32
	'username'  => 'frommail',
33
	'password'  => 'Ledxc-jRFiR-wBMXD-jyyjt-Y87CZ',
34
	'debug'     => false
35
];
36
37
38
// --- do not edit below this line ---
39
40
class NextcloudMailCatcher {
41
42
43
	/** @var string */
44
	private $content;
45
46
	/** @var array */
47
	private $config;
48
49
50
	/**
51
	 * NextcloudMailCatcher constructor.
52
	 *
53
	 * @param array $config
54
	 */
55
	public function __construct(array $config) {
56
		$nextcloud = $config['nextcloud'];
57
		if (substr($nextcloud, -1) === '/') {
58
			$config['nextcloud'] = substr($nextcloud, 0, -1);
59
		}
60
61
		$this->config = $config;
62
	}
63
64
65
	/**
66
	 * @param $content
67
	 *
68
	 * @return $this
69
	 */
70
	public function setContent(string $content): self {
71
		$this->content = $content;
72
73
		return $this;
74
	}
75
76
77
	/**
78
	 * @return string
79
	 */
80
	public function getContent(): string {
81
		return $this->content;
82
	}
83
84
85
	/**
86
	 *
87
	 */
88
	public function sendToNextcloud(): void {
89
		$content = rawurlencode(base64_encode($this->getContent()));
90
91
		$curl = $this->generateAuthedCurl();
92
		$this->fillCurlWithContent($curl, 'content=' . $content);
93
94
		$result = curl_exec($curl);
95
96
		$this->debugCurl($curl, $result);
97
	}
98
99
100
	/**
101
	 *
102
	 */
103
	public function test(): void {
104
		$this->config['debug'] = true;
105
		$this->debug('testing!');
106
107
		$pwd = $this->config['password'];
108
		if (substr($pwd, 5, 1) !== '-') {
109
			$this->debug('');
110
			$this->debug('Error: password have to be a generated token');
111
			$this->debug(
112
				'Generate a token on the webclient: Settings / Security / Devices & session'
113
			);
114
			exit();
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
115
		}
116
117
		$curl = $this->generateAuthedCurl();
118
		$this->fillCurlWithContent($curl, 'content=null');
119
120
		$result = curl_exec($curl);
121
122
		$this->debugCurl($curl, $result);
123
	}
124
125
126
	/**
127
	 * @param resource $curl
128
	 * @param string|array $result
129
	 */
130
	private function debugCurl($curl, $result): void {
131
		if ($result === false) {
0 ignored issues
show
The condition $result === false is always false.
Loading history...
132
			$this->debug('Mail NOT forwarded: ' . curl_error($curl));
133
134
			return;
135
		}
136
137
		try {
138
			$this->debugCurlResponseCode($curl);
139
		} catch (Exception $e) {
140
			$this->debug('Mail NOT forwarded: ' . $e->getMessage());
141
142
			return;
143
		}
144
145
		$this->debug('Mail forwarded, result was ' . $result);
0 ignored issues
show
Are you sure $result of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

145
		$this->debug('Mail forwarded, result was ' . /** @scrutinizer ignore-type */ $result);
Loading history...
146
	}
147
148
149
	/**
150
	 * @param $curl
151
	 *
152
	 * @throws Exception
153
	 */
154
	private function debugCurlResponseCode($curl) {
155
156
		$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
157
		if ($code === 201) {
158
			return;
159
		}
160
161
		if ($code === 401 || $code === 500) {
162
			throw new Exception('Unauthorized access');
163
		}
164
165
		if ($code === 404) {
166
			throw new Exception('404 Not Found');
167
		}
168
169
		if ($code === 503 || $code === 302) {
170
			throw new Exception('The \'files_frommail\' app does not seems enabled');
171
		}
172
173
		throw new Exception('Request returned code ' . $code);
174
	}
175
176
177
	/**
178
	 * @return resource
179
	 */
180
	private function generateAuthedCurl() {
181
182
		$url = $this->config['nextcloud'] . '/index.php/apps/files_frommail/remote';
183
		$curl = curl_init($url);
184
185
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
0 ignored issues
show
It seems like $curl can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, 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

185
		curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_RETURNTRANSFER, true);
Loading history...
186
		curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
187
		curl_setopt(
188
			$curl, CURLOPT_USERPWD,
189
			$this->config['username'] . ':' . $this->config['password']
190
		);
191
192
		$this->debug(
193
			'Generate curl request to ' . $url . ' with username \'' . $this->config['username']
194
			. '\''
195
		);
196
197
		return $curl;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $curl could also return false which is incompatible with the documented return type resource. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
198
	}
199
200
201
	/**
202
	 * @param resource $curl
203
	 * @param string $put
204
	 */
205
	private function fillCurlWithContent(&$curl, string $put): void {
206
207
		curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
208
		curl_setopt($curl, CURLOPT_POSTFIELDS, $put);
209
210
		$length = strlen($put);
211
		curl_setopt(
212
			$curl, CURLOPT_HTTPHEADER,
213
			[
214
				'Content-type: application/x-www-form-urlencoded',
215
				'OCS-APIRequest: true',
216
				'Content-Length: ' . $length
217
			]
218
		);
219
220
		$this->debug('Content-Length: ' . $length . ' (' . round($length / 1024 / 1024, 1) . 'MB)');
221
	}
222
223
224
	/**
225
	 * @param string $string
226
	 */
227
	private function debug(string $string): void {
228
		if (!array_key_exists('debug', $this->config) || $this->config['debug'] !== true) {
229
			return;
230
		}
231
232
		echo $string . "\n";
233
//		$log = '/tmp/' . basename(__FILE__, '.php') . '.log';
234
//		file_put_contents($log, date('Y-m-d H:i:s') . ' ' . $string . "\n", FILE_APPEND);
235
	}
236
237
}
238
239
240
$mailCatcher = new NextcloudMailCatcher($config);
241
242
if (sizeof($argv) === 2 && $argv[1] === 'test') {
243
	$mailCatcher->test();
244
245
	return;
246
}
247
248
echo 'Catching a new mail';
249
250
$content = '';
251
$fd = fopen('php://stdin', 'r');
252
while (!feof($fd)) {
0 ignored issues
show
It seems like $fd can also be of type false; however, parameter $handle of feof() does only seem to accept resource, 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

252
while (!feof(/** @scrutinizer ignore-type */ $fd)) {
Loading history...
253
	$content .= fread($fd, 1024);
0 ignored issues
show
It seems like $fd can also be of type false; however, parameter $handle of fread() does only seem to accept resource, 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

253
	$content .= fread(/** @scrutinizer ignore-type */ $fd, 1024);
Loading history...
254
}
255
256
$mailCatcher->setContent($content);
257
$mailCatcher->sendToNextcloud();
258
259
260
261
262