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