Bearsampp 2025.8.29
Loading...
Searching...
No Matches
class.bin.mailpit.php
Go to the documentation of this file.
1<?php
2/*
3 *
4 * * Copyright (c) 2022-2025 Bearsampp
5 * * License: GNU General Public License version 3 or later; see LICENSE.txt
6 * * Website: https://bearsampp.com
7 * * Github: https://github.com/Bearsampp
8 *
9 */
10
17class BinMailpit extends Module
18{
19 const SERVICE_NAME = 'bearsamppmailpit';
20 const SERVICE_PARAMS = ' --listen "%s:%d" --smtp "%s:%d" --webroot "%s"';
21
22 const ROOT_CFG_ENABLE = 'mailpitEnable';
23 const ROOT_CFG_VERSION = 'mailpitVersion';
24
25 const LOCAL_CFG_EXE = 'mailpitExe';
26 const LOCAL_CFG_WEB_ROOT = 'mailpitWebRoot';
27 const LOCAL_CFG_UI_PORT = 'mailpitUiPort';
28 const LOCAL_CFG_SMTP_PORT = 'mailpitSmtpPort';
29 const LOCAL_CFG_LISTEN = 'mailpitListen';
30
31 private $service;
32 private $log;
33
34 private $exe;
35 private $webRoot;
36 private $uiPort;
37 private $smtpPort;
38 private $listen;
39
46 public function __construct($id, $type)
47 {
48 Util::logInitClass( $this );
49 $this->reload( $id, $type );
50 }
51
58 public function reload($id = null, $type = null)
59 {
61 Util::logReloadClass( $this );
62
63 $this->name = $bearsamppLang->getValue( Lang::MAILPIT );
64 $this->version = $bearsamppConfig->getRaw( self::ROOT_CFG_VERSION );
65 parent::reload( $id, $type );
66
67 $this->enable = $this->enable && $bearsamppConfig->getRaw( self::ROOT_CFG_ENABLE );
68 $this->service = new Win32Service( self::SERVICE_NAME );
69 $this->log = $bearsamppRoot->getLogsPath() . '/mailpit.log';
70
71 if ( $this->bearsamppConfRaw !== false ) {
72 $this->exe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_EXE];
73 $this->webRoot = $this->bearsamppConfRaw[self::LOCAL_CFG_WEB_ROOT];
74 $this->uiPort = intval( $this->bearsamppConfRaw[self::LOCAL_CFG_UI_PORT] );
75 $this->smtpPort = intval( $this->bearsamppConfRaw[self::LOCAL_CFG_SMTP_PORT] );
76 $this->listen = $this->bearsamppConfRaw[self::LOCAL_CFG_LISTEN];
77 }
78
79 if ( !$this->enable ) {
80 Util::logInfo( $this->name . ' is not enabled!' );
81
82 return;
83 }
84 if ( !is_dir( $this->currentPath ) ) {
85 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->currentPath ) );
86
87 return;
88 }
89 if ( !is_dir( $this->symlinkPath ) ) {
90 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->symlinkPath ) );
91
92 return;
93 }
94 if ( !is_file( $this->bearsamppConf ) ) {
95 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->bearsamppConf ) );
96
97 return;
98 }
99 if ( !is_file( $this->exe ) ) {
100 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->exe ) );
101
102 return;
103 }
104 if ( (empty( $this->webRoot ) && $this->webRoot !== '' || is_numeric( $this->webRoot )) ) {
105 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_WEB_ROOT, $this->webRoot ) );
106
107 return;
108 }
109 if ( empty( $this->uiPort ) ) {
110 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_UI_PORT, $this->uiPort ) );
111
112 return;
113 }
114 if ( empty( $this->smtpPort ) ) {
115 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_SMTP_PORT, $this->smtpPort ) );
116
117 return;
118 }
119 if ( empty( $this->listen ) ) {
120 Util::logError( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_LISTEN, $this->listen ) );
121
122 return;
123 }
124
125 $nssm = new Nssm( self::SERVICE_NAME );
126 $nssm->setDisplayName( APP_TITLE . ' ' . $this->getName() );
127 $nssm->setBinPath( $this->exe );
128 $nssm->setParams( sprintf( self::SERVICE_PARAMS, $this->listen, $this->uiPort, $this->listen, $this->smtpPort, $this->webRoot ) );
129 $nssm->setStart( Nssm::SERVICE_DEMAND_START );
130 $nssm->setStdout( $bearsamppRoot->getLogsPath() . '/mailpit.out.log' );
131 $nssm->setStderr( $bearsamppRoot->getLogsPath() . '/mailpit.err.log' );
132
133 $this->service->setNssm( $nssm );
134 }
135
141 protected function replaceAll($params)
142 {
143 $content = file_get_contents( $this->bearsamppConf );
144
145 foreach ( $params as $key => $value ) {
146 $content = preg_replace( '|' . $key . ' = .*|', $key . ' = ' . '"' . $value . '"', $content );
147 $this->bearsamppConfRaw[$key] = $value;
148 switch ( $key ) {
149 case self::LOCAL_CFG_UI_PORT:
150 $this->uiPort = intval( $value );
151 break;
152 case self::LOCAL_CFG_SMTP_PORT:
153 $this->smtpPort = intval( $value );
154 break;
155 }
156 }
157
158 file_put_contents( $this->bearsamppConf, $content );
159 }
160
166 public function rebuildConf()
167 {
168 global $bearsamppRegistry;
169
170 Util::logTrace("Starting rebuildConf for Mailpit service");
171 Util::logTrace("Checking if registry key exists for Mailpit service parameters");
172
173 $registryPath = 'SYSTEM\CurrentControlSet\Services\\' . self::SERVICE_NAME . '\Parameters';
174 Util::logTrace("Registry path: " . $registryPath);
175
176 $exists = $bearsamppRegistry->exists(
178 $registryPath,
180 );
181
182 if ( $exists ) {
183 Util::logTrace("Registry key exists, updating service parameters");
184
185 $serviceParams = sprintf(self::SERVICE_PARAMS, $this->listen, $this->uiPort, $this->listen, $this->smtpPort, $this->webRoot);
186 Util::logTrace("Service parameters: " . $serviceParams);
187
188 $result = $bearsamppRegistry->setExpandStringValue(
190 $registryPath,
192 $serviceParams
193 );
194
195 Util::logTrace("Registry update " . ($result ? "succeeded" : "failed"));
196 return $result;
197 }
198
199 Util::logTrace("Registry key does not exist for Mailpit service parameters");
200 return false;
201 }
202
212 public function changePort($port, $checkUsed = false, $wbProgressBar = null)
213 {
214 global $bearsamppWinbinder;
215
216 if ( !Util::isValidPort( $port ) ) {
217 Util::logError( $this->getName() . ' port not valid: ' . $port );
218
219 return false;
220 }
221
222 $port = intval( $port );
223 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
224
225 $isPortInUse = Util::isPortInUse( $port );
226 if ( !$checkUsed || $isPortInUse === false ) {
227 // bearsampp.conf
228 $this->setSmtpPort( $port );
229 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
230
231 // conf
232 $this->update();
233 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
234
235 return true;
236 }
237
238 Util::logDebug( $this->getName() . ' port in used: ' . $port . ' - ' . $isPortInUse );
239
240 return $isPortInUse;
241 }
242
251 public function checkPort($port, $showWindow = false)
252 {
253 global $bearsamppLang, $bearsamppWinbinder;
254 $boxTitle = sprintf( $bearsamppLang->getValue( Lang::CHECK_PORT_TITLE ), $this->getName(), $port );
255
256 if ( !Util::isValidPort( $port ) ) {
257 Util::logError( $this->getName() . ' port not valid: ' . $port );
258
259 return false;
260 }
261
262 $headers = Util::getHeaders( $this->listen, $port );
263 if ( !empty( $headers ) ) {
264 if ( Util::contains( $headers[0], 'Mailpit' ) ) {
265 Util::logDebug( $this->getName() . ' port ' . $port . ' is used by: ' . str_replace( '220 ', '', $headers[0] ) );
266 if ( $showWindow ) {
267 $bearsamppWinbinder->messageBoxInfo(
268 sprintf( $bearsamppLang->getValue( Lang::PORT_USED_BY ), $port, str_replace( '220 ', '', $headers[0] ) ),
269 $boxTitle
270 );
271 }
272
273 return true;
274 }
275 Util::logDebug( $this->getName() . ' port ' . $port . ' is used by another application' );
276 if ( $showWindow ) {
277 $bearsamppWinbinder->messageBoxWarning(
278 sprintf( $bearsamppLang->getValue( Lang::PORT_NOT_USED_BY ), $port ),
279 $boxTitle
280 );
281 }
282 }
283 else {
284 Util::logDebug( $this->getName() . ' port ' . $port . ' is not used' );
285 if ( $showWindow ) {
286 $bearsamppWinbinder->messageBoxError(
287 sprintf( $bearsamppLang->getValue( Lang::PORT_NOT_USED ), $port ),
288 $boxTitle
289 );
290 }
291 }
292
293 return false;
294 }
295
304 public function switchVersion($version, $showWindow = false)
305 {
306 Util::logDebug( 'Switch ' . $this->name . ' version to ' . $version );
307
308 return $this->updateConfig( $version, 0, $showWindow );
309 }
310
320 protected function updateConfig($version = null, $sub = 0, $showWindow = false)
321 {
322 global $bearsamppLang, $bearsamppWinbinder;
323
324 if ( !$this->enable ) {
325 return true;
326 }
327
328 $version = $version == null ? $this->version : $version;
329 Util::logDebug( ($sub > 0 ? str_repeat( ' ', 2 * $sub ) : '') . 'Update ' . $this->name . ' ' . $version . ' config' );
330
331 $boxTitle = sprintf( $bearsamppLang->getValue( Lang::SWITCH_VERSION_TITLE ), $this->getName(), $version );
332
333 $bearsamppConf = str_replace( 'mailpit' . $this->getVersion(), 'mailpit' . $version, $this->bearsamppConf );
334 if ( !file_exists( $bearsamppConf ) ) {
335 Util::logError( 'bearsampp config files not found for ' . $this->getName() . ' ' . $version );
336 if ( $showWindow ) {
337 $bearsamppWinbinder->messageBoxError(
338 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_NOT_FOUND_ERROR ), $this->getName() . ' ' . $version ),
339 $boxTitle
340 );
341 }
342
343 return false;
344 }
345
346 $bearsamppConfRaw = parse_ini_file( $bearsamppConf );
347 if ( $bearsamppConfRaw === false || !isset( $bearsamppConfRaw[self::ROOT_CFG_VERSION] ) || $bearsamppConfRaw[self::ROOT_CFG_VERSION] != $version ) {
348 Util::logError( 'bearsampp config file malformed for ' . $this->getName() . ' ' . $version );
349 if ( $showWindow ) {
350 $bearsamppWinbinder->messageBoxError(
351 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_MALFORMED_ERROR ), $this->getName() . ' ' . $version ),
352 $boxTitle
353 );
354 }
355
356 return false;
357 }
358
359 // bearsampp.conf
360 $this->setVersion( $version );
361
362 return true;
363 }
364
370 public function setVersion($version)
371 {
372 global $bearsamppConfig;
373 $this->version = $version;
374 $bearsamppConfig->replace( self::ROOT_CFG_VERSION, $version );
375 $this->reload();
376 }
377
383 public function getService()
384 {
385 return $this->service;
386 }
387
394 public function setEnable($enabled, $showWindow = false)
395 {
396 global $bearsamppConfig, $bearsamppLang, $bearsamppWinbinder;
397
398 if ( $enabled == Config::ENABLED && !is_dir( $this->currentPath ) ) {
399 Util::logDebug( $this->getName() . ' cannot be enabled because bundle ' . $this->getVersion() . ' does not exist in ' . $this->currentPath );
400 if ( $showWindow ) {
401 $bearsamppWinbinder->messageBoxError(
402 sprintf( $bearsamppLang->getValue( Lang::ENABLE_BUNDLE_NOT_EXIST ), $this->getName(), $this->getVersion(), $this->currentPath ),
403 sprintf( $bearsamppLang->getValue( Lang::ENABLE_TITLE ), $this->getName() )
404 );
405 }
406 $enabled = Config::DISABLED;
407 }
408
409 Util::logInfo( $this->getName() . ' switched to ' . ($enabled == Config::ENABLED ? 'enabled' : 'disabled') );
410 $this->enable = $enabled == Config::ENABLED;
411 $bearsamppConfig->replace( self::ROOT_CFG_ENABLE, $enabled );
412
413 $this->reload();
414 if ( $this->enable ) {
415 Util::installService( $this, $this->smtpPort, null, $showWindow );
416 }
417 else {
418 Util::removeService( $this->service, $this->name );
419 }
420 }
421
427 public function getLog()
428 {
429 return $this->log;
430 }
431
437 public function getExe()
438 {
439 return $this->exe;
440 }
441
447 public function getWebRoot()
448 {
449 return $this->webRoot;
450 }
451
457 public function setWebRoot($webRoot)
458 {
459 $this->replace( self::LOCAL_CFG_WEB_ROOT, $webRoot );
460 }
461
467 public function getUiPort()
468 {
469 return $this->uiPort;
470 }
471
477 public function setUiPort($uiPort)
478 {
479 $this->replace( self::LOCAL_CFG_UI_PORT, $uiPort );
480 }
481
487 public function getSmtpPort()
488 {
489 return $this->smtpPort;
490 }
491
497 public function setSmtpPort($smtpPort)
498 {
499 $this->replace( self::LOCAL_CFG_SMTP_PORT, $smtpPort );
500 }
501
507 public function getListen()
508 {
509 return $this->listen;
510 }
511
517 public function setListen()
518 {
519 return $this->replace( self::LOCAL_CFG_LISTEN, $this->listen );
520 }
521}
$result
global $bearsamppLang
global $bearsamppRoot
$port
setWebRoot($webRoot)
updateConfig($version=null, $sub=0, $showWindow=false)
const LOCAL_CFG_SMTP_PORT
switchVersion($version, $showWindow=false)
setVersion($version)
checkPort($port, $showWindow=false)
changePort($port, $checkUsed=false, $wbProgressBar=null)
setEnable($enabled, $showWindow=false)
setSmtpPort($smtpPort)
reload($id=null, $type=null)
__construct($id, $type)
const DISABLED
const ENABLED
const ENABLE_BUNDLE_NOT_EXIST
const BEARSAMPP_CONF_MALFORMED_ERROR
const ERROR_EXE_NOT_FOUND
const ERROR_CONF_NOT_FOUND
const BEARSAMPP_CONF_NOT_FOUND_ERROR
const PORT_NOT_USED
const ERROR_INVALID_PARAMETER
const ENABLE_TITLE
const PORT_NOT_USED_BY
const SWITCH_VERSION_TITLE
const CHECK_PORT_TITLE
const PORT_USED_BY
const ERROR_FILE_NOT_FOUND
const MAILPIT
update($sub=0, $showWindow=false)
replace($key, $value)
const INFO_APP_PARAMETERS
const SERVICE_DEMAND_START
const HKEY_LOCAL_MACHINE
static logError($data, $file=null)
static installService($bin, $port, $syntaxCheckCmd, $showWindow=false)
static logInitClass($classInstance)
static getHeaders($host, $port, $ssl=false)
static removeService($service, $name)
static logTrace($data, $file=null)
static isValidPort($port)
static logInfo($data, $file=null)
static contains($string, $search)
static logDebug($data, $file=null)
static logReloadClass($classInstance)
static isPortInUse($port)
global $bearsamppConfig
Definition homepage.php:27
const APP_TITLE
Definition root.php:13