2024.8.23
Loading...
Searching...
No Matches
class.bin.mailpit.php
Go to the documentation of this file.
1<?php
2/*
3 * Copyright (c) 2021-2024 Bearsampp
4 * License: GNU General Public License version 3 or later; see LICENSE.txt
5 * Author: Bear
6 * Website: https://bearsampp.com
7 * Github: https://github.com/Bearsampp
8 */
9
10/**
11 * Class BinMailpit
12 *
13 * This class represents the Mailpit module in the Bearsampp application.
14 * It handles the configuration, initialization, and management of the Mailpit service.
15 */
16class BinMailpit extends Module
17{
18 const SERVICE_NAME = 'bearsamppmailpit';
19 const SERVICE_PARAMS = ' --listen "%s:%d" --smtp "%s:%d" --webroot "%s"';
20
21 const ROOT_CFG_ENABLE = 'mailpitEnable';
22 const ROOT_CFG_VERSION = 'mailpitVersion';
23
24 const LOCAL_CFG_EXE = 'mailpitExe';
25 const LOCAL_CFG_WEB_ROOT = 'mailpitWebRoot';
26 const LOCAL_CFG_UI_PORT = 'mailpitUiPort';
27 const LOCAL_CFG_SMTP_PORT = 'mailpitSmtpPort';
28 const LOCAL_CFG_LISTEN = 'mailpitListen';
29
30 private $service;
31 private $log;
32
33 private $exe;
34 private $webRoot;
35 private $uiPort;
36 private $smtpPort;
37 private $listen;
38
39 /**
40 * Constructs a BinMailpit object and initializes the module.
41 *
42 * @param string $id The ID of the module.
43 * @param string $type The type of the module.
44 */
45 public function __construct($id, $type)
46 {
47 Util::logInitClass( $this );
48 $this->reload( $id, $type );
49 }
50
51 /**
52 * Reloads the module configuration based on the provided ID and type.
53 *
54 * @param string|null $id The ID of the module. If null, the current ID is used.
55 * @param string|null $type The type of the module. If null, the current type is used.
56 */
57 public function reload($id = null, $type = null)
58 {
60 Util::logReloadClass( $this );
61
62 $this->name = $bearsamppLang->getValue( Lang::MAILPIT );
63 $this->version = $bearsamppConfig->getRaw( self::ROOT_CFG_VERSION );
64 parent::reload( $id, $type );
65
66 $this->enable = $this->enable && $bearsamppConfig->getRaw( self::ROOT_CFG_ENABLE );
67 $this->service = new Win32Service( self::SERVICE_NAME );
68 $this->log = $bearsamppRoot->getLogsPath() . '/mailpit.log';
69
70 if ( $this->bearsamppConfRaw !== false ) {
71 $this->exe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_EXE];
72 $this->webRoot = $this->bearsamppConfRaw[self::LOCAL_CFG_WEB_ROOT];
73 $this->uiPort = intval( $this->bearsamppConfRaw[self::LOCAL_CFG_UI_PORT] );
74 $this->smtpPort = intval( $this->bearsamppConfRaw[self::LOCAL_CFG_SMTP_PORT] );
75 $this->listen = $this->bearsamppConfRaw[self::LOCAL_CFG_LISTEN];
76 }
77
78 if ( !$this->enable ) {
79 Util::logInfo( $this->name . ' is not enabled!' );
80
81 return;
82 }
83 if ( !is_dir( $this->currentPath ) ) {
84 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->currentPath ) );
85
86 return;
87 }
88 if ( !is_dir( $this->symlinkPath ) ) {
89 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->symlinkPath ) );
90
91 return;
92 }
93 if ( !is_file( $this->bearsamppConf ) ) {
94 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->bearsamppConf ) );
95
96 return;
97 }
98 if ( !is_file( $this->exe ) ) {
99 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->exe ) );
100
101 return;
102 }
103 if ( (empty( $this->webRoot ) && $this->webRoot !== '' || is_numeric( $this->webRoot )) ) {
104 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_WEB_ROOT, $this->webRoot ) );
105
106 return;
107 }
108 if ( empty( $this->uiPort ) ) {
109 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_UI_PORT, $this->uiPort ) );
110
111 return;
112 }
113 if ( empty( $this->smtpPort ) ) {
114 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_SMTP_PORT, $this->smtpPort ) );
115
116 return;
117 }
118 if ( empty( $this->listen ) ) {
119 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_LISTEN, $this->listen ) );
120
121 return;
122 }
123
124 $nssm = new Nssm( self::SERVICE_NAME );
125 $nssm->setDisplayName( APP_TITLE . ' ' . $this->getName() );
126 $nssm->setBinPath( $this->exe );
127 $nssm->setParams( sprintf( self::SERVICE_PARAMS, $this->listen, $this->uiPort, $this->listen, $this->smtpPort, $this->webRoot ) );
128 $nssm->setStart( Nssm::SERVICE_DEMAND_START );
129 $nssm->setStdout( $bearsamppRoot->getLogsPath() . '/mailpit.out.log' );
130 $nssm->setStderr( $bearsamppRoot->getLogsPath() . '/mailpit.err.log' );
131
132 $this->service->setNssm( $nssm );
133 }
134
135 /**
136 * Replaces multiple key-value pairs in the configuration file.
137 *
138 * @param array $params An associative array of key-value pairs to replace.
139 */
140 protected function replaceAll($params)
141 {
142 $content = file_get_contents( $this->bearsamppConf );
143
144 foreach ( $params as $key => $value ) {
145 $content = preg_replace( '|' . $key . ' = .*|', $key . ' = ' . '"' . $value . '"', $content );
146 $this->bearsamppConfRaw[$key] = $value;
147 switch ( $key ) {
149 $this->uiPort = intval( $value );
150 break;
152 $this->smtpPort = intval( $value );
153 break;
154 }
155 }
156
157 file_put_contents( $this->bearsamppConf, $content );
158 }
159
160 /**
161 * Rebuilds the configuration for the Mailpit service in the Windows Registry.
162 *
163 * @return bool True if the configuration was successfully rebuilt, false otherwise.
164 */
165 public function rebuildConf()
166 {
167 global $bearsamppRegistry;
168
169 $exists = $bearsamppRegistry->exists(
171 'SYSTEM\CurrentControlSet\Services\\' . self::SERVICE_NAME . '\Parameters',
173 );
174 if ( $exists ) {
175 return $bearsamppRegistry->setExpandStringValue(
177 'SYSTEM\CurrentControlSet\Services\\' . self::SERVICE_NAME . '\Parameters',
179 sprintf( self::SERVICE_PARAMS, $this->listen, $this->uiPort, $this->listen, $this->smtpPort, $this->webRoot )
180 );
181 }
182
183 return false;
184 }
185
186 /**
187 * Changes the SMTP port for the Mailpit service.
188 *
189 * @param int $port The new port number.
190 * @param bool $checkUsed Whether to check if the port is already in use.
191 * @param mixed $wbProgressBar The progress bar object for UI updates.
192 *
193 * @return bool|int True if the port was successfully changed, or the process using the port if it is in use.
194 */
195 public function changePort($port, $checkUsed = false, $wbProgressBar = null)
196 {
197 global $bearsamppWinbinder;
198
199 if ( !Util::isValidPort( $port ) ) {
200 Util::logError( $this->getName() . ' port not valid: ' . $port );
201
202 return false;
203 }
204
205 $port = intval( $port );
206 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
207
208 $isPortInUse = Util::isPortInUse( $port );
209 if ( !$checkUsed || $isPortInUse === false ) {
210 // bearsampp.conf
211 $this->setSmtpPort( $port );
212 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
213
214 // conf
215 $this->update();
216 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
217
218 return true;
219 }
220
221 Util::logDebug( $this->getName() . ' port in used: ' . $port . ' - ' . $isPortInUse );
222
223 return $isPortInUse;
224 }
225
226 /**
227 * Checks if the specified port is used by the Mailpit service.
228 *
229 * @param int $port The port number to check.
230 * @param bool $showWindow Whether to show a message box with the result.
231 *
232 * @return bool True if the port is used by the Mailpit service, false otherwise.
233 */
234 public function checkPort($port, $showWindow = false)
235 {
236 global $bearsamppLang, $bearsamppWinbinder;
237 $boxTitle = sprintf( $bearsamppLang->getValue( Lang::CHECK_PORT_TITLE ), $this->getName(), $port );
238
239 if ( !Util::isValidPort( $port ) ) {
240 Util::logError( $this->getName() . ' port not valid: ' . $port );
241
242 return false;
243 }
244
245 $headers = Util::getHeaders( $this->listen, $port );
246 if ( !empty( $headers ) ) {
247 if ( Util::contains( $headers[0], 'Mailpit' ) ) {
248 Util::logDebug( $this->getName() . ' port ' . $port . ' is used by: ' . str_replace( '220 ', '', $headers[0] ) );
249 if ( $showWindow ) {
250 $bearsamppWinbinder->messageBoxInfo(
251 sprintf( $bearsamppLang->getValue( Lang::PORT_USED_BY ), $port, str_replace( '220 ', '', $headers[0] ) ),
252 $boxTitle
253 );
254 }
255
256 return true;
257 }
258 Util::logDebug( $this->getName() . ' port ' . $port . ' is used by another application' );
259 if ( $showWindow ) {
260 $bearsamppWinbinder->messageBoxWarning(
261 sprintf( $bearsamppLang->getValue( Lang::PORT_NOT_USED_BY ), $port ),
262 $boxTitle
263 );
264 }
265 }
266 else {
267 Util::logDebug( $this->getName() . ' port ' . $port . ' is not used' );
268 if ( $showWindow ) {
269 $bearsamppWinbinder->messageBoxError(
270 sprintf( $bearsamppLang->getValue( Lang::PORT_NOT_USED ), $port ),
271 $boxTitle
272 );
273 }
274 }
275
276 return false;
277 }
278
279 /**
280 * Switches the version of the Mailpit service.
281 *
282 * @param string $version The version to switch to.
283 * @param bool $showWindow Whether to show a message box with the result.
284 *
285 * @return bool True if the version was successfully switched, false otherwise.
286 */
287 public function switchVersion($version, $showWindow = false)
288 {
289 Util::logDebug( 'Switch ' . $this->name . ' version to ' . $version );
290
291 return $this->updateConfig( $version, 0, $showWindow );
292 }
293
294 /**
295 * Updates the configuration for the Mailpit service.
296 *
297 * @param string|null $version The version to update to. If null, the current version is used.
298 * @param int $sub The sub-level for logging indentation.
299 * @param bool $showWindow Whether to show a message box with the result.
300 *
301 * @return bool True if the configuration was successfully updated, false otherwise.
302 */
303 protected function updateConfig($version = null, $sub = 0, $showWindow = false)
304 {
305 global $bearsamppLang, $bearsamppWinbinder;
306
307 if ( !$this->enable ) {
308 return true;
309 }
310
311 $version = $version == null ? $this->version : $version;
312 Util::logDebug( ($sub > 0 ? str_repeat( ' ', 2 * $sub ) : '') . 'Update ' . $this->name . ' ' . $version . ' config' );
313
314 $boxTitle = sprintf( $bearsamppLang->getValue( Lang::SWITCH_VERSION_TITLE ), $this->getName(), $version );
315
316 $bearsamppConf = str_replace( 'mailpit' . $this->getVersion(), 'mailpit' . $version, $this->bearsamppConf );
317 if ( !file_exists( $bearsamppConf ) ) {
318 Util::logError( 'bearsampp config files not found for ' . $this->getName() . ' ' . $version );
319 if ( $showWindow ) {
320 $bearsamppWinbinder->messageBoxError(
321 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_NOT_FOUND_ERROR ), $this->getName() . ' ' . $version ),
322 $boxTitle
323 );
324 }
325
326 return false;
327 }
328
329 $bearsamppConfRaw = parse_ini_file( $bearsamppConf );
330 if ( $bearsamppConfRaw === false || !isset( $bearsamppConfRaw[self::ROOT_CFG_VERSION] ) || $bearsamppConfRaw[self::ROOT_CFG_VERSION] != $version ) {
331 Util::logError( 'bearsampp config file malformed for ' . $this->getName() . ' ' . $version );
332 if ( $showWindow ) {
333 $bearsamppWinbinder->messageBoxError(
334 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_MALFORMED_ERROR ), $this->getName() . ' ' . $version ),
335 $boxTitle
336 );
337 }
338
339 return false;
340 }
341
342 // bearsampp.conf
343 $this->setVersion( $version );
344
345 return true;
346 }
347
348 /**
349 * Sets the version of the Mailpit service.
350 *
351 * @param string $version The version to set.
352 */
353 public function setVersion($version)
354 {
355 global $bearsamppConfig;
356 $this->version = $version;
357 $bearsamppConfig->replace( self::ROOT_CFG_VERSION, $version );
358 $this->reload();
359 }
360
361 /**
362 * Retrieves the service object for the Mailpit service.
363 *
364 * @return Win32Service The service object.
365 */
366 public function getService()
367 {
368 return $this->service;
369 }
370
371 /**
372 * Enables or disables the Mailpit service.
373 *
374 * @param bool $enabled Whether to enable or disable the service.
375 * @param bool $showWindow Whether to show a message box with the result.
376 */
377 public function setEnable($enabled, $showWindow = false)
378 {
379 global $bearsamppConfig, $bearsamppLang, $bearsamppWinbinder;
380
381 if ( $enabled == Config::ENABLED && !is_dir( $this->currentPath ) ) {
382 Util::logDebug( $this->getName() . ' cannot be enabled because bundle ' . $this->getVersion() . ' does not exist in ' . $this->currentPath );
383 if ( $showWindow ) {
384 $bearsamppWinbinder->messageBoxError(
385 sprintf( $bearsamppLang->getValue( Lang::ENABLE_BUNDLE_NOT_EXIST ), $this->getName(), $this->getVersion(), $this->currentPath ),
386 sprintf( $bearsamppLang->getValue( Lang::ENABLE_TITLE ), $this->getName() )
387 );
388 }
389 $enabled = Config::DISABLED;
390 }
391
392 Util::logInfo( $this->getName() . ' switched to ' . ($enabled == Config::ENABLED ? 'enabled' : 'disabled') );
393 $this->enable = $enabled == Config::ENABLED;
394 $bearsamppConfig->replace( self::ROOT_CFG_ENABLE, $enabled );
395
396 $this->reload();
397 if ( $this->enable ) {
398 Util::installService( $this, $this->smtpPort, null, $showWindow );
399 }
400 else {
401 Util::removeService( $this->service, $this->name );
402 }
403 }
404
405 /**
406 * Retrieves the log file path for the Mailpit service.
407 *
408 * @return string The log file path.
409 */
410 public function getLog()
411 {
412 return $this->log;
413 }
414
415 /**
416 * Retrieves the executable file path for the Mailpit service.
417 *
418 * @return string The executable file path.
419 */
420 public function getExe()
421 {
422 return $this->exe;
423 }
424
425 /**
426 * Retrieves the web root directory for the Mailpit service.
427 *
428 * @return string The web root directory.
429 */
430 public function getWebRoot()
431 {
432 return $this->webRoot;
433 }
434
435 /**
436 * Sets the web root directory for the Mailpit service.
437 *
438 * @param string $webRoot The web root directory to set.
439 */
440 public function setWebRoot($webRoot)
441 {
442 $this->replace( self::LOCAL_CFG_WEB_ROOT, $webRoot );
443 }
444
445 /**
446 * Retrieves the UI port for the Mailpit service.
447 *
448 * @return int The UI port.
449 */
450 public function getUiPort()
451 {
452 return $this->uiPort;
453 }
454
455 /**
456 * Sets the UI port for the Mailpit service.
457 *
458 * @param int $uiPort The UI port to set.
459 */
460 public function setUiPort($uiPort)
461 {
462 $this->replace( self::LOCAL_CFG_UI_PORT, $uiPort );
463 }
464
465 /**
466 * Retrieves the SMTP port for the Mailpit service.
467 *
468 * @return int The SMTP port.
469 */
470 public function getSmtpPort()
471 {
472 return $this->smtpPort;
473 }
474
475 /**
476 * Sets the SMTP port for the Mailpit service.
477 *
478 * @param int $smtpPort The SMTP port to set.
479 */
480 public function setSmtpPort($smtpPort)
481 {
482 $this->replace( self::LOCAL_CFG_SMTP_PORT, $smtpPort );
483 }
484
485 /**
486 * Retrieves the listen address for the Mailpit service.
487 *
488 * @return string The listen address.
489 */
490 public function getListen()
491 {
492 return $this->listen;
493 }
494
495 /**
496 * Sets the listen address for the Mailpit service.
497 *
498 * @return bool True if the listen address was successfully set, false otherwise.
499 */
500 public function setListen()
501 {
502 return $this->replace( self::LOCAL_CFG_LISTEN, $this->listen );
503 }
504}
global $bearsamppLang
global $bearsamppRoot
$port
setEnable($enabled, $showWindow=false)
reload($id=null, $type=null)
const LOCAL_CFG_SMTP_PORT
setWebRoot($webRoot)
checkPort($port, $showWindow=false)
__construct($id, $type)
switchVersion($version, $showWindow=false)
updateConfig($version=null, $sub=0, $showWindow=false)
changePort($port, $checkUsed=false, $wbProgressBar=null)
setSmtpPort($smtpPort)
setVersion($version)
const DISABLED
const ENABLED
const ENABLE_TITLE
const PORT_NOT_USED_BY
const ERROR_FILE_NOT_FOUND
const ERROR_INVALID_PARAMETER
const BEARSAMPP_CONF_NOT_FOUND_ERROR
const CHECK_PORT_TITLE
const ENABLE_BUNDLE_NOT_EXIST
const ERROR_CONF_NOT_FOUND
const SWITCH_VERSION_TITLE
const ERROR_EXE_NOT_FOUND
const PORT_USED_BY
const MAILPIT
const PORT_NOT_USED
const BEARSAMPP_CONF_MALFORMED_ERROR
update($sub=0, $showWindow=false)
replace($key, $value)
const SERVICE_DEMAND_START
const INFO_APP_PARAMETERS
const HKEY_LOCAL_MACHINE
static logReloadClass($classInstance)
static getHeaders($host, $port, $ssl=false)
static contains($string, $search)
static logError($data, $file=null)
static isValidPort($port)
static installService($bin, $port, $syntaxCheckCmd, $showWindow=false)
static removeService($service, $name)
static logDebug($data, $file=null)
static logInitClass($classInstance)
static isPortInUse($port)
static logInfo($data, $file=null)
global $bearsamppConfig
Definition homepage.php:26
const APP_TITLE
Definition root.php:12