1<?php
2
3declare(strict_types=1);
4
5/**
6 * @author Daniel Kesselberg <mail@danielkesselberg.de>
7 *
8 * Mail
9 *
10 * This code is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License, version 3,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License, version 3,
20 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21 *
22 */
23
24namespace OCA\Mail\Controller;
25
26use Horde\ManageSieve\Exception as ManagesieveException;
27use OCA\Mail\AppInfo\Application;
28use OCA\Mail\Db\MailAccountMapper;
29use OCA\Mail\Exception\ClientException;
30use OCA\Mail\Exception\CouldNotConnectException;
31use OCA\Mail\Service\AccountService;
32use OCA\Mail\Sieve\SieveClientFactory;
33use OCP\AppFramework\Controller;
34use OCP\AppFramework\Db\DoesNotExistException;
35use OCP\AppFramework\Http\JSONResponse;
36use OCP\IRequest;
37use OCP\Security\ICrypto;
38
39class SieveController extends Controller {
40
41	/** @var AccountService */
42	private $accountService;
43
44	/** @var MailAccountMapper */
45	private $mailAccountMapper;
46
47	/** @var SieveClientFactory */
48	private $sieveClientFactory;
49
50	/** @var string */
51	private $currentUserId;
52
53	/** @var ICrypto */
54	private $crypto;
55
56	/**
57	 * AccountsController constructor.
58	 *
59	 * @param IRequest $request
60	 * @param string $UserId
61	 * @param AccountService $accountService
62	 * @param MailAccountMapper $mailAccountMapper
63	 * @param SieveClientFactory $sieveClientFactory
64	 * @param ICrypto $crypto
65	 */
66	public function __construct(IRequest $request,
67								string $UserId,
68								AccountService $accountService,
69								MailAccountMapper $mailAccountMapper,
70								SieveClientFactory $sieveClientFactory,
71								ICrypto $crypto
72	) {
73		parent::__construct(Application::APP_ID, $request);
74		$this->currentUserId = $UserId;
75		$this->accountService = $accountService;
76		$this->mailAccountMapper = $mailAccountMapper;
77		$this->sieveClientFactory = $sieveClientFactory;
78		$this->crypto = $crypto;
79	}
80
81	/**
82	 * @NoAdminRequired
83	 * @TrapError
84	 *
85	 * @param int $id account id
86	 *
87	 * @return JSONResponse
88	 *
89	 * @throws CouldNotConnectException
90	 * @throws ClientException
91	 */
92	public function getActiveScript(int $id): JSONResponse {
93		$sieve = $this->getClient($id);
94
95		$scriptName = $sieve->getActive();
96		if ($scriptName === null) {
97			$script = '';
98		} else {
99			$script = $sieve->getScript($scriptName);
100		}
101
102		return new JSONResponse([
103			'scriptName' => $scriptName,
104			'script' => $script,
105		]);
106	}
107
108	/**
109	 * @NoAdminRequired
110	 * @TrapError
111	 *
112	 * @param int $id account id
113	 * @param string $script
114	 *
115	 * @return JSONResponse
116	 *
117	 * @throws ClientException
118	 * @throws CouldNotConnectException
119	 * @throws ManagesieveException
120	 */
121	public function updateActiveScript(int $id, string $script): JSONResponse {
122		$sieve = $this->getClient($id);
123
124		$scriptName = $sieve->getActive() ?? 'nextcloud';
125		$sieve->installScript($scriptName, $script, true);
126
127		return new JSONResponse();
128	}
129
130	/**
131	 * @NoAdminRequired
132	 * @TrapError
133	 *
134	 * @param int $id account id
135	 * @param bool $sieveEnabled
136	 * @param string $sieveHost
137	 * @param int $sievePort
138	 * @param string $sieveUser
139	 * @param string $sievePassword
140	 * @param string $sieveSslMode
141	 *
142	 * @return JSONResponse
143	 *
144	 * @throws CouldNotConnectException
145	 * @throws DoesNotExistException
146	 */
147	public function updateAccount(int $id,
148								  bool $sieveEnabled,
149								  string $sieveHost,
150								  int $sievePort,
151								  string $sieveUser,
152								  string $sievePassword,
153								  string $sieveSslMode
154	): JSONResponse {
155		$mailAccount = $this->mailAccountMapper->find($this->currentUserId, $id);
156
157		if ($sieveEnabled === false) {
158			$mailAccount->setSieveEnabled(false);
159			$mailAccount->setSieveHost(null);
160			$mailAccount->setSievePort(null);
161			$mailAccount->setSieveUser(null);
162			$mailAccount->setSievePassword(null);
163			$mailAccount->setSieveSslMode(null);
164
165			$this->mailAccountMapper->save($mailAccount);
166			return new JSONResponse(['sieveEnabled' => $mailAccount->isSieveEnabled()]);
167		}
168
169		if (empty($sieveUser)) {
170			$sieveUser = $mailAccount->getInboundUser();
171		}
172
173		if (empty($sievePassword)) {
174			$sievePassword = $mailAccount->getInboundPassword();
175		} else {
176			$sievePassword = $this->crypto->encrypt($sievePassword);
177		}
178
179		try {
180			$this->sieveClientFactory->createClient($sieveHost, $sievePort, $sieveUser, $sievePassword, $sieveSslMode);
181		} catch (ManagesieveException $e) {
182			throw CouldNotConnectException::create($e, 'ManageSieve', $sieveHost, $sievePort);
183		}
184
185		$mailAccount->setSieveEnabled(true);
186		$mailAccount->setSieveHost($sieveHost);
187		$mailAccount->setSievePort($sievePort);
188		$mailAccount->setSieveUser($mailAccount->getInboundUser() === $sieveUser ? null : $sieveUser);
189		$mailAccount->setSievePassword($mailAccount->getInboundPassword() === $sievePassword ? null : $sievePassword);
190		$mailAccount->setSieveSslMode($sieveSslMode);
191
192		$this->mailAccountMapper->save($mailAccount);
193		return new JSONResponse(['sieveEnabled' => $mailAccount->isSieveEnabled()]);
194	}
195
196	/**
197	 * @param int $id
198	 *
199	 * @return \Horde\ManageSieve
200	 *
201	 * @throws ClientException
202	 * @throws CouldNotConnectException
203	 */
204	protected function getClient(int $id): \Horde\ManageSieve {
205		$account = $this->accountService->find($this->currentUserId, $id);
206
207		if (!$account->getMailAccount()->isSieveEnabled()) {
208			throw new CouldNotConnectException('ManageSieve is disabled.');
209		}
210
211		try {
212			$sieve = $this->sieveClientFactory->getClient($account);
213		} catch (ManagesieveException $e) {
214			throw CouldNotConnectException::create($e, 'ManageSieve', $account->getMailAccount()->getSieveHost(), $account->getMailAccount()->getSievePort());
215		}
216
217		return $sieve;
218	}
219}
220