22 | ```smarty
23 | {if $apisVars['is_banned']}
24 |
25 |
IP Ban Notice
26 | We found your IP Address in the Blacklist in the Panel.
Your IP was detected in the following Jails:
27 |
28 | {foreach from=$apisVars['jails'] item=jail}
29 | - {$jail}
30 | {/foreach}
31 |
32 | We've removed the ban on your IP but any additional suspicious activity may result in banning your IP Again.
33 | {if $apisVars['rampart_enabled']}
34 |
35 | You should also be sure to add your IP {$apisVars['ip']} to the Whitelist
36 |
here.
37 | {/if}
38 |
39 | {/if}
40 | ```
41 |
42 |
43 | ## Supported Features
44 | - Creating
45 | - Suspending
46 | - Unsuspending
47 | - Terminating
48 | - Changing Password
49 | - Changing Plans
50 | - SSO from WHMCS for Client and Admin
51 | - SSO with custom links to different apps
52 | - Automatically unban a user's IP
53 | - Cancellation Hold
54 | - Statistics update
55 | - SiteID Population on stats update
56 |
57 | ## Cancellation Hold
58 | This feature allows you to defer termination of cancelled and terminated accounts for 30 days.
59 | To enable, uncomment lines 242 and 243 in apnscp.php and comment out lines 245 and 246
60 |
61 | Comment:
62 | ```php
63 | $opts['force'] = 'true';
64 | $client->admin_delete_site($site_domain, $opts);
65 | ```
66 | By commenting out that section, you are preventing account deletion.
67 |
68 | Uncomment:
69 | ```php
70 | $opts['reason'] = 'Customer Requested Cancellation';
71 | $client->admin_deactivate_site($site_domain, $opts);
72 | ```
73 | By uncommenting that section, you are forcing account suspension. This means the site will effectively be suspended until the automated process purges the account from the server.
74 | To enable that feature, uncomment the last action hook block in hooks.php
75 |
76 | ## Summary
77 | The ApisCP provisioning module for WHMCS allows you to integrate your billing system with your server management panel so new user accounts will be automatically provisioned, suspended and terminated as needed. Users can change their password as well as use the Single Sign-On (SSO) feature to seamlessly transition from WHMCS to ApisCP.
78 |
79 | ## License
80 | This product is licensed under the GPL v3 (see LICENSE file). Basically, you can't call it your own or sell it.
81 | This is meant to be free for the benefit of the community. Help us by improving with Pull Requests!
82 |
83 | ## Contributing
84 | Submit a PR and have fun!
85 | I am a developer by hobby, not profession so don't judge me and I won't judge you :P
86 |
87 | ## Need Help?
88 | Join us in the [ApisCP Discord](https://discord.gg/5bQr3Dm) in the #whmcs channel!
--------------------------------------------------------------------------------
/modules/servers/apnscp/apnscp.php:
--------------------------------------------------------------------------------
1 | 'apnscp',
34 | 'APIVersion' => '1.0', // Use API Version 1.1
35 | 'RequiresServer' => true, // Set true if module requires a server to work
36 | 'DefaultNonSSLPort' => '2082', // Default Non-SSL Connection Port
37 | 'DefaultSSLPort' => '2083', // Default SSL Connection Port
38 | 'ServiceSingleSignOnLabel' => 'Login to ApisCP',
39 | // 'AdminSingleSignOnLabel' => 'Login to ApisCP as Admin',
40 | // // The display name of the unique identifier to be displayed on the table output
41 | // 'ListAccountsUniqueIdentifierDisplayName' => 'Domain',
42 | // // The field in the return that matches the unique identifier
43 | // 'ListAccountsUniqueIdentifierField' => 'domain',
44 | // // The config option indexed field from the _ConfigOptions function that identifies the product on the remote system
45 | // 'ListAccountsProductField' => 'configoption1',
46 | ];
47 | }
48 |
49 | /**
50 | * Define product configuration options.
51 | *
52 | * The values you return here define the configuration options that are
53 | * presented to a user when configuring a product for use with the module. These
54 | * values are then made available in all module function calls with the key name
55 | * configoptionX - with X being the index number of the field from 1 to 24.
56 | *
57 | * You can specify up to 24 parameters, with field types:
58 | * * text
59 | * * password
60 | * * yesno
61 | * * dropdown
62 | * * radio
63 | * * textarea
64 | *
65 | * Examples of each and their possible configuration parameters are provided in
66 | * this sample function.
67 | *
68 | * @see https://developers.whmcs.com/provisioning-modules/config-options/
69 | *
70 | * @return array
71 | */
72 | function apnscp_ConfigOptions()
73 | {
74 | return [
75 | 'ApisCP Plan' => [
76 | 'Type' => 'dropdown',
77 | 'Default' => 'basic',
78 | 'Description' => 'Choose a plan (auto populated)
Format: <server> - <plan>',
79 | 'Loader' => 'apnscp_getPlans',
80 | 'SimpleMode' => true,
81 | ],
82 | ];
83 | }
84 |
85 | /**
86 | * Provision a new instance of a product/service.
87 | *
88 | * Attempt to provision a new instance of a given product/service. This is
89 | * called any time provisioning is requested inside of WHMCS. Depending upon the
90 | * configuration, this can be any of:
91 | * * When a new order is placed
92 | * * When an invoice for a new order is paid
93 | * * Upon manual request by an admin user
94 | *
95 | * @param array $params common module parameters
96 | *
97 | * @return string "success" or an error message
98 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
99 | *
100 | */
101 | function apnscp_CreateAccount(array $params)
102 | {
103 | // Setup Server Params
104 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
105 | $apnscp_apikey = $params['serverpassword'];
106 |
107 | $domain = strtolower($params['domain']);
108 |
109 | $opts = Helper::generateOptions($params);
110 |
111 | $cliCommand = Helper::generateCommand($opts, 'AddDomain');
112 |
113 | try {
114 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
115 | $client->admin_add_site($domain, $params['username'], $opts);
116 | Helper::apnscpValidateCustomFields($params['pid']);
117 |
118 | logModuleCall('apnscp', 'Create', ['Request' => str_ireplace('><', ">\n<", $client->__getLastRequest()), 'CommandString' => $cliCommand], str_ireplace('><', ">\n<", $client->__getLastResponse()));
119 | } catch (Exception $e) {
120 | // Record the error in WHMCS's module log.
121 | logModuleCall(
122 | 'apnscp',
123 | 'Create',
124 | str_ireplace('><', ">\n<", $client->__getLastRequest()),
125 | $e->getMessage()."\n\n".$e->getTraceAsString()."\n\n".str_ireplace('><', ">\n<", $client->__getLastResponse())
126 | );
127 |
128 | return $e->getMessage();
129 | }
130 |
131 | return 'success';
132 | }
133 |
134 | /**
135 | * Suspend an instance of a product/service.
136 | *
137 | * Called when a suspension is requested. This is invoked automatically by WHMCS
138 | * when a product becomes overdue on payment or can be called manually by admin
139 | * user.
140 | *
141 | * @param array $params common module parameters
142 | *
143 | * @return string "success" or an error message
144 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
145 | *
146 | */
147 | function apnscp_SuspendAccount(array $params)
148 | {
149 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
150 | $apnscp_apikey = $params['serverpassword'];
151 | $site_domain = strtolower($params['domain']);
152 |
153 | $opts['reason'] = $params['suspendreason'];
154 |
155 | try {
156 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
157 |
158 | $client->admin_deactivate_site($site_domain, $opts);
159 |
160 | logModuleCall('apnscp', 'Suspend', ['Request' => str_ireplace('><', ">\n<", $client->__getLastRequest())], str_ireplace('><', ">\n<", $client->__getLastResponse()));
161 | } catch (Exception $e) {
162 | // Record the error in WHMCS's module log.
163 | logModuleCall(
164 | 'apnscp',
165 | 'Suspend',
166 | str_ireplace('><', ">\n<", $client->__getLastRequest()),
167 | $e->getMessage()."\n\n".$e->getTraceAsString()."\n\n".str_ireplace('><', ">\n<", $client->__getLastResponse())
168 | );
169 |
170 | return $e->getMessage();
171 | }
172 |
173 | return 'success';
174 | }
175 |
176 | /**
177 | * Un-suspend instance of a product/service.
178 | *
179 | * Called when an un-suspension is requested. This is invoked
180 | * automatically upon payment of an overdue invoice for a product, or
181 | * can be called manually by admin user.
182 | *
183 | * @param array $params common module parameters
184 | *
185 | * @return string "success" or an error message
186 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
187 | *
188 | */
189 | function apnscp_UnsuspendAccount(array $params)
190 | {
191 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
192 | $apnscp_apikey = $params['serverpassword'];
193 | $site_domain = strtolower($params['domain']);
194 |
195 | try {
196 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
197 |
198 | $client->admin_activate_site($site_domain);
199 |
200 | logModuleCall('apnscp', 'Unsuspend', ['Request' => str_ireplace('><', ">\n<", $client->__getLastRequest())], str_ireplace('><', ">\n<", $client->__getLastResponse()));
201 | } catch (Exception $e) {
202 | // Record the error in WHMCS's module log.
203 | logModuleCall(
204 | 'apnscp',
205 | 'Unsuspend',
206 | str_ireplace('><', ">\n<", $client->__getLastRequest()),
207 | $e->getMessage()."\n\n".$e->getTraceAsString()."\n\n".str_ireplace('><', ">\n<", $client->__getLastResponse())
208 | );
209 |
210 | return $e->getMessage();
211 | }
212 |
213 | return 'success';
214 | }
215 |
216 | /**
217 | * Terminate instance of a product/service.
218 | *
219 | * Called when a termination is requested. This can be invoked automatically for
220 | * overdue products if enabled, or requested manually by an admin user.
221 | *
222 | * @param array $params common module parameters
223 | *
224 | * @return string "success" or an error message
225 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
226 | *
227 | */
228 | function apnscp_TerminateAccount(array $params)
229 | {
230 | // Logging
231 | $module = 'apnscp';
232 | $method = 'terminate';
233 | // end
234 |
235 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
236 | $apnscp_apikey = $params['serverpassword'];
237 | $site_domain = strtolower($params['domain']);
238 |
239 | try {
240 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
241 |
242 | $opts['force'] = true;
243 | $client->admin_delete_site($site_domain, $opts);
244 |
245 | // $opts['reason'] = 'Customer Requested Cancellation';
246 | // $client->admin_deactivate_site($site_domain, $opts);
247 |
248 | logModuleCall(
249 | $module,
250 | $method,
251 | "Request: ".str_ireplace('><', ">\n<", $client->__getLastRequest())."\n\nHeaders:".$client->__getLastRequestHeaders(),
252 | "Response: ".$client->__getLastResponse()."\n\n".
253 | "Headers: ".$client->__getLastResponseHeaders()
254 | );
255 |
256 | return 'success';
257 | } catch (Exception $e) {
258 | logModuleCall(
259 | $module,
260 | $method,
261 | "Request: ".str_ireplace('><', ">\n<", $client->__getLastRequest())."\n\nHeaders:".$client->__getLastRequestHeaders(),
262 | "Exception: ".$e->getMessage()."\n\n".
263 | $e->getLine()."\n\n".
264 | $e->getTraceAsString()."\n\n".
265 | "Response: ".$client->__getLastResponse()."\n\n".
266 | "Headers: ".$client->__getLastResponseHeaders()
267 | );
268 |
269 | if (empty($e->getMessage()) || ($e->getMessage() === 'Error Fetching http headers')) {
270 | /**
271 | * This is almost certainly a timeout error where the panel received and processed the termination command but didn't reply in time and SOAP threw an error
272 | * If I'm wrong, I'll re-think this approach :)
273 | */
274 |
275 | return 'success';
276 | }
277 |
278 | return $e->getMessage();
279 | }
280 | }
281 |
282 | /**
283 | * Change the password for an instance of a product/service.
284 | *
285 | * Called when a password change is requested. This can occur either due to a
286 | * client requesting it via the client area or an admin requesting it from the
287 | * admin side.
288 | *
289 | * This option is only available to client end users when the product is in an
290 | * active status.
291 | *
292 | * @param array $params common module parameters
293 | *
294 | * @return string "success" or an error message
295 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
296 | *
297 | */
298 | function apnscp_ChangePassword(array $params)
299 | {
300 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
301 | $apnscp_apikey = $params['serverpassword'];
302 | $site_domain = strtolower($params['domain']);
303 | $site_admin = $params['username'];
304 | $site_password = $params['password'];
305 |
306 | try {
307 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
308 |
309 | $client->auth_change_password($site_password, $site_admin, $site_domain);
310 |
311 | logModuleCall('apnscp', 'ChangePassword', ['Request' => str_ireplace('><', ">\n<", $client->__getLastRequest())], str_ireplace('><', ">\n<", $client->__getLastResponse()));
312 | } catch (Exception $e) {
313 | // Record the error in WHMCS's module log.
314 | logModuleCall(
315 | 'apnscp',
316 | 'ChangePassword',
317 | str_ireplace('><', ">\n<", $client->__getLastRequest()),
318 | $e->getMessage()."\n\n".$e->getTraceAsString()."\n\n".$client->__getLastResponseHeaders()."\n\n".str_ireplace('><', ">\n<", $client->__getLastResponse())
319 | );
320 |
321 | return $e->getMessage();
322 | }
323 |
324 | return 'success';
325 | }
326 |
327 | /**
328 | * Upgrade or downgrade an instance of a product/service.
329 | *
330 | * Called to apply any change in product assignment or parameters. It
331 | * is called to provision upgrade or downgrade orders, as well as being
332 | * able to be invoked manually by an admin user.
333 | *
334 | * This same function is called for upgrades and downgrades of both
335 | * products and configurable options.
336 | *
337 | * @param array $params common module parameters
338 | *
339 | * @return string "success" or an error message
340 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
341 | *
342 | */
343 | function apnscp_ChangePackage(array $params)
344 | {
345 | // Setup Server Params
346 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
347 | $apnscp_apikey = $params['serverpassword'];
348 |
349 | $domain = strtolower($params['domain']);
350 |
351 | $opts['siteinfo.plan'] = $params['configoption1'];
352 | $extra['reset'] = true;
353 |
354 | $cliCommand = Helper::generateCommand($opts, 'EditDomain');
355 |
356 | try {
357 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
358 |
359 | $client->admin_edit_site($domain, $opts, $extra);
360 |
361 | logModuleCall('apnscp', 'ChangePackage', ['Request' => str_ireplace('><', ">\n<", $client->__getLastRequest()), 'CommandString' => $cliCommand], str_ireplace('><', ">\n<", $client->__getLastResponse()));
362 | } catch (Exception $e) {
363 | // Record the error in WHMCS's module log.
364 | logModuleCall(
365 | 'apnscp',
366 | 'ChangePackage',
367 | str_ireplace('><', ">\n<", $client->__getLastRequest()),
368 | $e->getMessage()."\n\n".$e->getTraceAsString()."\n\n".str_ireplace('><', ">\n<", $client->__getLastResponse())
369 | );
370 |
371 | return $e->getMessage();
372 | }
373 |
374 | return 'success';
375 | }
376 |
377 | /**
378 | * Perform single sign-on for a given instance of a product/service.
379 | *
380 | * Called when single sign-on is requested for an instance of a product/service.
381 | *
382 | * When successful, returns a URL to which the user should be redirected.
383 | *
384 | * @param array $params common module parameters
385 | *
386 | * @return array
387 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
388 | *
389 | */
390 | function apnscp_ServiceSingleSignOn(array $params)
391 | {
392 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
393 | $apnscp_apikey = $params['serverpassword'];
394 | $site_domain = strtolower($params['domain']);
395 | $site_admin = $params['username'];
396 | $app = App::get_req_var('app');
397 | $extra = [];
398 | $allowed_apps = [
399 | 'usermanage',
400 | 'mailboxroutes',
401 | 'vacation',
402 | 'filemanager',
403 | 'domainmanager',
404 | 'bandwidthbd',
405 | 'crontab',
406 | 'subdomains',
407 | 'changemysql',
408 | 'phpmyadmin',
409 | 'webapps',
410 | 'terminal',
411 | 'whitelist',
412 | ];
413 |
414 | try {
415 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
416 |
417 | $session_id = $client->admin_hijack($site_domain, $site_admin, 'UI');
418 |
419 | if (! isset($app) or ! in_array($app, $allowed_apps)) {
420 | $app = 'dashboard';
421 | }
422 |
423 | if ($app === 'subdomains') {
424 | $extra['mode'] = 'add';
425 | }
426 |
427 | $extra['esprit_id'] = $session_id;
428 | $query = http_build_query($extra);
429 |
430 | $url = "${apnscp_apiendpoint}/apps/${app}?${query}";
431 |
432 | return [
433 | 'success' => true,
434 | 'redirectTo' => $url,
435 | ];
436 | } catch (Exception $e) {
437 | // Record the error in WHMCS's module log.
438 | logModuleCall(
439 | 'apnscp',
440 | 'SSO',
441 | str_ireplace('><', ">\n<", $client->__getLastRequest()),
442 | $e->getMessage()."\n\n".$e->getTraceAsString()."\n\n".str_ireplace('><', ">\n<", $client->__getLastResponse())
443 | );
444 |
445 | return [
446 | 'success' => false,
447 | 'errorMsg' => $e->getMessage(),
448 | ];
449 | }
450 | }
451 |
452 |
453 | /**
454 | * Client area output logic handling.
455 | *
456 | * This function is used to define module specific client area output. It should
457 | * return an array consisting of a template file and optional additional
458 | * template variables to make available to that template.
459 | *
460 | * @param array $params common module parameters
461 | *
462 | * @return array
463 | * @see https://developers.whmcs.com/provisioning-modules/module-parameters/
464 | *
465 | */
466 | function apnscp_ClientArea(array $params)
467 | {
468 | return [
469 | 'overrideDisplayTitle' => $params['domain'],
470 | 'tabOverviewReplacementTemplate' => 'overview.tpl',
471 | ];
472 | }
473 |
474 | function apnscp_getPlans()
475 | {
476 | $servers = DB::table('tblservers')->where('type', 'apnscp')->get();
477 |
478 | try {
479 | foreach ($servers as $server) {
480 | $apnscp_apiendpoint = ($server->secure === 'on' ? 'https' : 'http').'://'.$server->hostname.':'.($server->secure === 'on' ? '2083' : '2082');
481 | $apnscp_apikey = decrypt($server->password);
482 |
483 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
484 |
485 | $plans = $client->admin_list_plans();
486 |
487 | foreach ($plans as $plan) {
488 | $return[$plan] = $server->name.' - '.$plan;
489 | }
490 | }
491 |
492 | return $return;
493 | } catch (Exception $e) {
494 | // No easy way to return an error so we'll default to the basic plan only
495 | return ['basic' => 'basic (api call failed)'];
496 | }
497 | }
498 |
499 | function apnscp_UsageUpdate($params)
500 | {
501 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
502 | $apnscp_apikey = $params['serverpassword'];
503 | $serverid = $params['serverid'];
504 |
505 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
506 | $siteInfoArr = $client->admin_collect(['siteinfo.domain']);
507 | $storageArr = $client->admin_get_usage('storage');
508 | $bandwidthArr = $client->admin_get_usage('bandwidth');
509 |
510 | $products = DB::table('tblproducts')->where('type', 'hostingaccount')->where('servertype', 'apnscp')->get();
511 | foreach ($products as $product) {
512 | // This is less than efficient, but it works and is only a few extra queries!
513 | Helper::apnscpValidateCustomFields($product->id);
514 | }
515 |
516 | foreach ($siteInfoArr as $site => $values) {
517 | $service = DB::table('tblhosting')
518 | ->where('server', $serverid)
519 | ->where('domain', $values['domain'])
520 | ->first();
521 |
522 | if (! empty($service)) {
523 | DB::table('tblhosting')
524 | ->where('id', $service->id)
525 | ->update([
526 | 'diskusage' => $storageArr[$site]['qused'] / 1024,
527 | 'disklimit' => $storageArr[$site]['qhard'] / 1024,
528 | 'bwusage' => ($bandwidthArr[$site]['sum'] / 1024) / 1024,
529 | 'bwlimit' => ($bandwidthArr[$site]['threshold'] / 1024) / 1024,
530 | 'lastupdate' => DB::raw('now()'),
531 | ]);
532 |
533 | $customFields = Helper::apnscpGetCustomFields($service->packageid);
534 | Helper::apnscpAddCustomFieldValue($service->id, $customFields['SiteID']['id'], $site);
535 | }
536 | }
537 | }
538 |
539 |
540 | /**
541 | * Log module call.
542 | *
543 | * @param string $module The name of the module
544 | * @param string $action The name of the action being performed
545 | * @param string|array $requestString The input parameters for the API call
546 | * @param string|array $responseData The response data from the API call
547 | * @param string|array $processedData The resulting data after any post processing (eg. json decode, xml decode,
548 | * etc...)
549 | * @param array $replaceVars An array of strings for replacement
550 | */
551 | //logModuleCall($module, $action, $requestString, $responseData, $processedData, $replaceVars);
--------------------------------------------------------------------------------
/modules/servers/apnscp/hooks.php:
--------------------------------------------------------------------------------
1 | getClient());
12 | $service = new WHMCS\Service($vars['serviceid'], $legacyClient->getID());
13 | $product = WHMCS\Product\Product::where('id', $service->getData('packageid'))->first();
14 |
15 | if ($product->module !== "apnscp") {
16 | return null;
17 | }
18 |
19 | if (version_compare(WHMCS\Config\Setting::getValue("Version"), '8', '<')) {
20 | $ip = WHMCS\Utility\Environment\CurrentUser::getIP();
21 | } else {
22 | $user = (new WHMCS\Authentication\CurrentUser())->user();
23 | $ip = $user->currentIp();
24 | }
25 |
26 | // $ip = '2.2.2.2'; // Only for debug...
27 |
28 | $knownJails = [
29 | 'f2b-sshd' => 'SSH (f2b-sshd)',
30 | 'f2b-postfix' => 'SMTP (f2b-postfix)',
31 | 'f2b-postfix-sasl' => 'SMTP (f2b-postfix-sasl)',
32 | 'f2b-dovecot' => 'IMAP/POP3 (f2b-dovecot)',
33 | 'f2b-vsftpd' => 'FTP (f2b-vsftpd)',
34 | 'f2b-evasive' => 'Apache Mod Evasive (f2b-evasive)',
35 | 'f2b-spambots' => 'Irregular Mail Patterns (f2b-spambots)',
36 | 'f2b-recidive' => 'Repeatedly getting banned (f2b-recidive)',
37 | 'f2b-pgsql' => 'pgSQL (f2b-pgsql)',
38 | 'f2b-mysql' => 'MySQL (f2b-mysql)',
39 | 'f2b-malware' => 'Malware Scans (f2b-malware)',
40 | ];
41 |
42 | $serverParams = [
43 | 'serverhttpprefix' => $server['secure'] === 'on' ? 'https' : 'http',
44 | 'serverhostname' => $server['hostname'],
45 | 'serverport' => $server['port'] ?: '2083',
46 | 'serverpassword' => decrypt($server['password']),
47 | 'domain' => strtolower($service->getData('domain')),
48 | 'username' => $service->getData('username'),
49 | 'ip' => $ip,
50 | ];
51 |
52 | $result = apnscp_checkIP($serverParams);
53 |
54 | if ($result['jails']) {
55 | if (is_array($result['jails'])) {
56 | foreach ($result['jails'] as $jail) {
57 | $jails[] = $knownJails[$jail];
58 | }
59 | } else {
60 | $jails[] = $result['jails'];
61 | }
62 |
63 | return ['apisVars' => ['is_banned' => true, 'jails' => $jails, 'ip' => $ip, 'rampart_enabled' => $result['rampart_enabled']]];
64 | }
65 |
66 | return ['apisVars' => ['is_banned' => false, 'jails' => [], 'ip' => $ip, 'rampart_enabled' => false]];
67 | });
68 |
69 | add_hook('ClientAreaPrimarySidebar', 1, function($sidebar) {
70 | if (! $sidebar->getChild("Service Details Actions")) {
71 | return null;
72 | }
73 | $service = Menu::context("service");
74 | if ($service instanceof WHMCS\Service\Service && $service->product->module !== "apnscp") {
75 | return null;
76 | }
77 |
78 | $sidebar->getChild("Service Details Actions")->addChild("Login to ApisCP", ["uri" => "clientarea.php?action=productdetails&id=".$service->id."&dosinglesignon=1", "label" => 'Login to ApisCP', "attributes" => ["target" => "_blank"], "disabled" => $service->status != "Active", "order" => 1]);
79 | });
80 |
81 | function apnscp_checkIP(array $params)
82 | {
83 | $apnscp_apiendpoint = $params['serverhttpprefix'].'://'.$params['serverhostname'].':'.$params['serverport'];
84 | $apnscp_apikey = $params['serverpassword'];
85 | $site_domain = $params['domain'];
86 | $site_admin = $params['username'];
87 | $clientIp = $params['ip'];
88 |
89 | try {
90 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
91 |
92 | if ($client->rampart_is_banned($clientIp)) {
93 | $jails = $client->rampart_banned_services($clientIp);
94 | $client->rampart_unban($clientIp);
95 |
96 | $rampartCheck = $client->admin_collect(['rampart.enabled'], null, [$site_domain]);
97 | $rampart_enabled = $rampartCheck[$site_domain]['rampart']['enabled'] === 1;
98 |
99 | // Disabled whitelisting to avoid conflicts, will instruct customer to whitelist themselves!
100 | // $session_id = $client->admin_hijack($site_domain, $site_admin, 'SOAP');
101 | // $newClient = Connector::create_client($apnscp_apikey, $apnscp_apiendpoint, $session_id, $clientIp);
102 | //
103 | // $newClient->rampart_whitelist($clientIp);
104 |
105 | return ['jails' => $jails, 'rampart_enabled' => $rampart_enabled];
106 | }
107 |
108 | return false;
109 | } catch (Exception $e) {
110 | // Record the error in WHMCS's activity log.
111 | logActivity('ApisCP IP Ban Check Failed: '.$e->getTraceAsString());
112 |
113 | return $e->getMessage();
114 | }
115 | }
116 |
117 | add_hook('DailyCronJob', 1, function() {
118 | $servers = WHMCS\Product\Server::where('type', 'apnscp')->where('active', 1)->where('disabled', 0)->get();
119 | foreach ($servers as $server) {
120 | $apnscp_apiendpoint = ($server->secure === 'on' ? 'https' : 'http').'://'.$server->hostname.':'.($server->secure === 'on' ? 2083 : 2082);
121 | $apnscp_apikey = decrypt($server->password);
122 |
123 | if (! empty($server->hostname)) {
124 | $client = ApisConnector::create_client($apnscp_apikey, $apnscp_apiendpoint);
125 | }
126 |
127 | $opts['since'] = "30 days ago";
128 | $opts['match'] = "Deferred Account Cancellation";
129 | $opts['dry-run'];
130 | $client->admin_delete_site(null, $opts);
131 | logModuleCall('apnscp', 'Deferred Cancellation - '.$server->hostname, str_ireplace('><', ">\n<", $client->__getLastRequest()), str_ireplace('><', ">\n<", $client->__getLastResponse()));
132 | }
133 | });
--------------------------------------------------------------------------------
/modules/servers/apnscp/lib/ApisConnector.php:
--------------------------------------------------------------------------------
1 | 30,
49 | 'location' => $uri,
50 | 'uri' => 'urn:apnscp.api.soap',
51 | 'trace' => true,
52 | 'stream_context' => stream_context_create([
53 | 'http' => [
54 | 'header' => implode("\r\n", $headers)."\r\n",
55 | ],
56 | ]),
57 | ];
58 |
59 | $connopts['location'] = $uri.'?authkey='.$api_key;
60 |
61 | $instance = (new static($wsdl, $connopts));
62 |
63 | if (isset($ctor[0])) {
64 | $instance->setId($ctor[0]);
65 | }
66 |
67 | return $instance;
68 | }
69 |
70 | /**
71 | * Set session name to pass between requests
72 | *
73 | * @param string $name
74 | *
75 | * @return ApisConnector
76 | */
77 | public function setId(string $name): self
78 | {
79 | $this->id = $name;
80 |
81 | return $this;
82 | }
83 |
84 | public function __call($function_name, $arguments)
85 | {
86 | static $ctr = 0;
87 | if ($this->id) {
88 | $this->__setCookie(self::COOKIE_NAME, $this->id);
89 | }
90 | $ret = parent::__call($function_name, $arguments);
91 | if ($ret !== null || $ctr >= 5) {
92 | $ctr = 0;
93 |
94 | return $ret;
95 | }
96 | // 50 ms sleep
97 | usleep(50000);
98 | $ctr++;
99 |
100 | return $this->__call($function_name, $arguments);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/modules/servers/apnscp/lib/Helper.php:
--------------------------------------------------------------------------------
1 | DNS provider key
82 | //
83 | ////FTP Module
84 | // $opts['ftp.enabled'] = $params['configoption10'] ? 1 : 0;
85 | //// $opts['ftp.ftpserver'] = 'ftp.'; // FTP Prefix
86 | //
87 | ////IP Stuffs
88 | // $opts['ipinfo.enabled'] = 1; // [0,1] Assign account unique IPv4 address from pool
89 | // $opts['ipinfo.namebased'] = $params['configoption11'] === 'shared' ? 1 : 0; // [0,1] Site uses shared IP address (unique otherwise, see ipaddrs)
90 | //
91 | //// Not supported yet!!!
92 | ////IPv6 Stuffs
93 | //// $opts['ipinfo6.enabled'] = $params['configoption12'] === 'disabled' ? '0' : '1'; // [0,1] Assign account unique IPv6 address from pool
94 | //// $opts['ipinfo6.namebased'] = $params['configoption12'] === 'shared' ? '1' : '0'; // [0,1] Site uses shared IP address (unique otherwise, see ipaddrs)
95 | //
96 | //// Logging
97 | //// $opts['logs.enabled'] = 1; // [0,1] Record web server access
98 | //
99 | //// Mail
100 | // $opts['mail.enabled'] = $params['configoption13'] ? 1 : 0; // [0,1] Enable mail service
101 | //
102 | ////MySQL
103 | // if ((int) $params['configoption14'] === 0)
104 | // {
105 | // $opts['mysql.enabled'] = 0; // [0,1] MySQL database access. Required for Web App usage.
106 | // }
107 | // else
108 | // {
109 | // $opts['mysql.enabled'] = '1'; // [0,1] MySQL database access. Required for Web App usage.
110 | // $opts['mysql.dbaseadmin'] = $params['username']; //
Set mysql admin user
111 | // $opts['mysql.dbaseprefix'] = $params['username'] . '_'; // < Set MySQL database prefix. Must end with '_'
112 | // if ($params['configoption15'] !== '')
113 | // {
114 | // $opts['mysql.dbasenum'] = $params['configoption14'] === '-1' ? 'null' : $params['configoption14']; // [null, 0-999] Limit total database count
115 | // }
116 | // $opts['mysql.passwd'] = $params['password']; // Plain-text password for mysql user.
117 | // }
118 | //
119 | ////PostgreSQL
120 | // if ((int) $params['configoption15'] === 0)
121 | // {
122 | // $opts['pgsql.enabled'] = 0; // [0,1] Enable PostgreSQL database access. Required for Discourse usage.
123 | // }
124 | // else
125 | // {
126 | // $opts['pgsql.enabled'] = '1'; // [0,1] Enable PostgreSQL database access. Required for Discourse usage.
127 | // $opts['pgsql.dbaseadmin'] = $params['username']; // Set pgsql admin user
128 | // $opts['pgsql.dbaseprefix'] = $params['username'] . '_'; // Set PostgreSQL database prefix. Must end with '_'
129 | // if ($params['configoption16'] !== '')
130 | // {
131 | // $opts['pgsql.dbasenum'] = $params['configoption15'] === '-1' ? 'null' : $params['configoption15']; // [null, 0-999] Limit total database count
132 | // }
133 | // $opts['pgsql.passwd'] = $params['password']; // Plain-text password for pgsql user.
134 | // }
135 | //
136 | //// Rampart
137 | //// $opts['rampart.enabled'] = '1'; // [0,1] Delegate brute-force whitelisting
138 | //// $opts['rampart.max'] = $params['configoption16'] ?: 100; // [-1, 0 => 4096] Maximum number of IP address whitelists.
139 | // $opts['rampart.whitelist'] = [$params['model']->client->ip]; // IPv4 | IPv6 IPv4 + IPv6 addresses
140 | //
141 | //// Spam Filtering
142 | // $opts['spamfilter.enabled'] = $params['configoption17'] ? 1 : 0; // [0,1] Mail filtering
143 | // $opts['spamfilter.provider'] = $params['configoption18']; // [spamassassin,rspamd] Inbound spam filter
144 | //
145 | //// SSH Module
146 | // $opts['ssh.enabled'] = $params['configoption19'] ? 1 : 0; // [0,1] Enable ssh service
147 | // $opts['ssh.jail'] = '1'; // [0,1] Jail all SSH sessions to account
148 | //
149 | ////SSL
150 | // $opts['ssl.enabled'] = $params['configoption20'] ? 1 : 0; // [0,1] Enable ssl service
151 | //
152 | //// Users
153 | // if ((int) $params['configoption21'] === 0)
154 | // {
155 | // $opts['users.enabled'] = 0;
156 | // }
157 | // else
158 | // {
159 | // $opts['users.enabled'] = 1;
160 | // if ($params['configoption21'] === '-1')
161 | // {
162 | // $opts['users.max'] = 'null';
163 | // }
164 | // else
165 | // {
166 | // $opts['users.max'] = (int) $params['configoption21']; // Actual Number of Users
167 | // }
168 | // }
169 |
170 | // Account Password
171 | $opts['auth.tpasswd'] = $params['password']; // Plain Text Password for account
172 |
173 | //Site Info
174 | $opts['siteinfo.enabled'] = '1'; // [0,1] Core account attributes
175 | $opts['siteinfo.domain'] = $params['domain']; // Primary domain of the account
176 | $opts['siteinfo.admin_user'] = $params['username']; // Administrative user of account
177 | $opts['siteinfo.email'] = $params['model']->client->email; // [email,[email1,email2...]] Contact address on account
178 | $opts['siteinfo.plan'] = $params['configoption1'];
179 |
180 | //Billing
181 | $opts['billing.invoice'] = 'WHMCS-'.$params['serviceid']; // Invoice id to link to customer
182 |
183 | // MySQL
184 | $opts['mysql.dbaseadmin'] = $params['username']; // Set mysql admin user
185 | $opts['mysql.dbaseprefix'] = $params['username'].'_'; // < Set MySQL database prefix. Must end with '_'
186 |
187 | // PGSQL
188 | $opts['pgsql.dbaseadmin'] = $params['username']; // Set pgsql admin user
189 | $opts['pgsql.dbaseprefix'] = $params['username'].'_'; // Set PostgreSQL database prefix. Must end with '_'
190 |
191 | return $opts;
192 | }
193 |
194 | /**
195 | * @param array $opts
196 | *
197 | * @return string
198 | */
199 | public static function generateCommand(array $opts, $action): string
200 | {
201 | $optArray[] = $action;
202 |
203 | foreach ($opts as $service => $value) {
204 | $service = str_replace('.', ',', $service);
205 | $optArray[] = "-c '{$service}'='{$value}'";
206 | }
207 |
208 | return implode(' ', $optArray);
209 | }
210 |
211 |
212 | public static function apnscpValidateCustomFields($productId): void
213 | {
214 | $requiredFields = ['SiteID'];
215 | $existingFields = [];
216 |
217 | $customFields = DB::table('tblcustomfields')->where('type', 'product')->where('relid', $productId)->get();
218 |
219 | if (! empty($customFields)) {
220 | foreach ($customFields as $field) {
221 | $existingFields[] = $field->fieldname;
222 | }
223 | }
224 | $newFields = array_diff($requiredFields, $existingFields);
225 | foreach ($newFields as $field) {
226 | switch ($field) {
227 | case 'SiteID':
228 | DB::table('tblcustomfields')->insert(['type' => 'product', 'relid' => $productId, 'fieldname' => 'SiteID', 'fieldtype' => 'text', 'description' => 'ApisCP Site ID', 'fieldoptions' => '', 'adminonly' => 'on', 'sortorder' => 0]);
229 | break;
230 | }
231 | }
232 | }
233 |
234 |
235 | public static function apnscpGetCustomFields($productId): array
236 | {
237 | $customFields = DB::table('tblcustomfields')->where('type', 'product')->where('relid', $productId)->get();
238 | if (! empty($customFields)) {
239 | foreach ($customFields as $field) {
240 | $fields[$field->fieldname] = ['id' => $field->id, 'description' => $field->description]; // ACCESS Custom Fields via $customFields['OrderID']['id']; Where OrderID is the name of the custom field.
241 | }
242 | }
243 |
244 | return $fields ?? [];
245 | }
246 |
247 | public static function apnscpGetCustomFieldId($hostingId, $fieldId): string
248 | {
249 | $result = DB::table('tblcustomfieldsvalues')->where('fieldid', $fieldId)->where('relid', $hostingId)->first();
250 | return $result->id ?? '';
251 | }
252 |
253 | public static function apnscpAddCustomFieldValue($hostingId, $fieldId, $value): void
254 | {
255 | $result = DB::table('tblcustomfieldsvalues')->where('fieldid', $fieldId)->where('relid', $hostingId)->first();
256 | if (! empty($result)) {
257 | // update
258 | DB::table('tblcustomfieldsvalues')->where('id', $result->id)->update(['value' => $value]);
259 | } else {
260 | // insert
261 | DB::table('tblcustomfieldsvalues')->insert(['fieldid' => $fieldId, 'relid' => $hostingId, 'value' => $value]);
262 | }
263 | }
264 |
265 | public static function apnscpGetCustomFieldValue($hostingId, $fieldId): string
266 | {
267 | $result = DB::table('tblcustomfieldsvalues')->where('fieldid', $fieldId)->where('relid', $hostingId)->first();
268 | return $result->value ?? '';
269 | }
270 | }
--------------------------------------------------------------------------------
/modules/servers/apnscp/templates/overview.tpl:
--------------------------------------------------------------------------------
1 |
25 | Overview
26 |
27 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
77 |
78 | {if $bwpercent|substr:0:-1 > 75}
79 |
80 | {if $bwpercent|substr:0:-1 > 100}
81 | {$LANG.cPanel.usageStatsBwOverLimit}
82 | {else}
83 | {$LANG.cPanel.usageStatsBwLimitNear}
84 | {/if}
85 | {if $packagesupgrade}
86 |
87 |
88 | {$LANG.cPanel.usageUpgradeNow}
89 |
90 | {/if}
91 |
92 | {elseif $diskpercent|substr:0:-1 > 75}
93 |
94 | {if $diskpercent|substr:0:-1 > 100}
95 | {$LANG.cPanel.usageStatsDiskOverLimit}
96 | {else}
97 | {$LANG.cPanel.usageStatsDiskLimitNear}
98 | {/if}
99 | {if $packagesupgrade}
100 |
101 |
102 | {$LANG.cPanel.usageUpgradeNow}
103 |
104 | {/if}
105 |
106 | {else}
107 |
108 | {$LANG.cPanel.usageLastUpdated} {$lastupdate}
109 |
110 | {/if}
111 |
112 |
113 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | {if $availableAddonProducts}
129 |
150 | {/if}
151 |
152 |
153 |
156 |
157 |
158 |
159 |
160 | {$LANG.clientareahostingregdate}
161 |
162 |
163 | {$regdate}
164 |
165 |
166 |
167 |
168 |
169 | {$LANG.orderproduct}
170 |
171 |
172 | {$groupname} - {$product}
173 |
174 |
175 |
176 |
177 | {if $domain}
178 |
179 |
180 | {$LANG.orderdomain}
181 |
182 |
186 |
187 | {/if}
188 | {if $username}
189 |
190 |
191 | {$LANG.serverusername}
192 |
193 |
194 | {$username}
195 |
196 |
197 | {/if}
198 | {if $serverdata}
199 |
200 |
201 | {$LANG.servername}
202 |
203 |
204 | {$serverdata.hostname}
205 |
206 |
207 |
208 |
209 | {$LANG.domainregisternsip}
210 |
211 |
212 | {$serverdata.ipaddress}
213 |
214 |
215 | {if $serverdata.nameserver1 || $serverdata.nameserver2 || $serverdata.nameserver3 || $serverdata.nameserver4 || $serverdata.nameserver5}
216 |
217 |
218 | {$LANG.domainnameservers}
219 |
220 |
221 | {if $serverdata.nameserver1}{$serverdata.nameserver1}{if $serverdata.nameserver1ip} ({$serverdata.nameserver1ip}){/if}
{/if}
222 | {if $serverdata.nameserver2}{$serverdata.nameserver2}{if $serverdata.nameserver2ip} ({$serverdata.nameserver2ip}){/if}
{/if}
223 | {if $serverdata.nameserver3}{$serverdata.nameserver3}{if $serverdata.nameserver3ip} ({$serverdata.nameserver3ip}){/if}
{/if}
224 | {if $serverdata.nameserver4}{$serverdata.nameserver4}{if $serverdata.nameserver4ip} ({$serverdata.nameserver4ip}){/if}
{/if}
225 | {if $serverdata.nameserver5}{$serverdata.nameserver5}{if $serverdata.nameserver5ip} ({$serverdata.nameserver5ip}){/if}
{/if}
226 |
227 |
228 | {/if}
229 | {/if}
230 |
231 | {if $dedicatedip}
232 |
233 |
234 | {$LANG.domainregisternsip}
235 |
236 |
237 | {$dedicatedip}
238 |
239 |
240 | {/if}
241 |
242 | {foreach from=$configurableoptions item=configoption}
243 |
244 |
245 | {$configoption.optionname}
246 |
247 |
248 | {if $configoption.optiontype eq 3}
249 | {if $configoption.selectedqty}
250 | {$LANG.yes}
251 | {else}
252 | {$LANG.no}
253 | {/if}
254 | {elseif $configoption.optiontype eq 4}
255 | {$configoption.selectedqty} x {$configoption.selectedoption}
256 | {else}
257 | {$configoption.selectedoption}
258 | {/if}
259 |
260 |
261 | {/foreach}
262 |
263 | {foreach from=$productcustomfields item=customfield}
264 |
265 |
266 | {$customfield.name}
267 |
268 |
269 | {$customfield.value}
270 |
271 |
272 | {/foreach}
273 |
274 | {if $lastupdate}
275 |
276 |
277 | {$LANG.clientareadiskusage}
278 |
279 |
280 | {$diskusage}MB / {$disklimit}MB ({$diskpercent})
281 |
282 |
283 |
284 |
285 | {$LANG.clientareabwusage}
286 |
287 |
288 | {$bwusage}MB / {$bwlimit}MB ({$bwpercent})
289 |
290 |
291 | {/if}
292 |
293 |
294 |
295 | {$LANG.orderpaymentmethod}
296 |
297 |
298 | {$paymentmethod}
299 |
300 |
301 |
302 |
303 |
304 | {$LANG.firstpaymentamount}
305 |
306 |
307 | {$firstpaymentamount}
308 |
309 |
310 |
311 |
312 |
313 | {$LANG.recurringamount}
314 |
315 |
316 | {$recurringamount}
317 |
318 |
319 |
320 |
321 |
322 | {$LANG.clientareahostingnextduedate}
323 |
324 |
325 | {$nextduedate}
326 |
327 |
328 |
329 |
330 |
331 | {$LANG.orderbillingcycle}
332 |
333 |
334 | {$billingcycle}
335 |
336 |
337 |
338 |
339 |
340 | {$LANG.clientareastatus}
341 |
342 |
343 | {$status}
344 |
345 |
346 |
347 | {if $suspendreason}
348 |
349 |
350 | {$LANG.suspendreason}
351 |
352 |
353 | {$suspendreason}
354 |
355 |
356 | {/if}
357 |
358 |
359 | {if $systemStatus == 'Active'}
360 |
361 |
364 |
365 |
366 |
367 |
373 |
374 |
380 |
381 |
387 |
388 |
394 |
395 |
401 |
402 |
408 |
409 |
415 |
416 |
422 |
423 |
429 |
430 |
436 |
437 |
443 |
444 |
450 |
451 |
452 |
453 | {/if}
454 |
455 |
456 |
457 | {if $systemStatus == 'Active'}
458 |
463 | {/if}
464 |
465 | {if $packagesupgrade}
466 |
471 | {/if}
472 |
473 |
482 |
--------------------------------------------------------------------------------