Releases: bradleygb/whmcs-cwp-module
Release list
v2.5.1 — the dashboard panels filter and page too
Drop-in as always — extract over modules/servers/cwp7/ and run Test Connection. No
configuration changes and nothing to reconfigure on existing services.
Added
The Domains, Subdomains and Databases panels filter and page, five to a page.
The filter matches on every column, not just the first — a database is as likely to be
looked up by its user as by its name.
Neither the filter nor the pager appears until a panel holds more than five rows, so an
account with three databases looks exactly as it did before.
This follows the same treatment the mailbox list got in 2.5.0, which pages at ten. CWP's
email/list and accountdetail/list both return everything in one response with no
pagination of their own, so the paging is done in the browser.
Full history in CHANGELOG.md.
v2.5.0 — a mailbox list that scales, and terminations that report honestly
Drop-in as always — extract over modules/servers/cwp7/ and run Test Connection. No
configuration changes, no database changes, nothing to reconfigure on existing services.
Includes everything from 2.3.0 and 2.4.0, which were never tagged.
Added
The mailbox list filters and pages. Ten to a page, with a filter box once there are more
than ten. An account with hundreds of addresses used to render every one of them, and every
row carried a hidden inline editor with three inputs behind it — five hundred mailboxes
meant a thousand table rows and no way to find anything among them.
email/list can't help here: its documented request takes key, action, user and
debug and nothing else, so the whole account arrives in one response and the paging has to
happen in the browser.
Account dashboard in the client area — disk, bandwidth, email accounts, FTP accounts,
databases and subdomains, from accountdetail/list.
Mailbox management from the client area — add, change password, change size, delete,
without sending customers into CWP. Off by default (mailbox_management in config.php);
needs ADD, UPD and DEL on Emails in addition to LIST.
Fixed
A termination that succeeded was reported as a failure. account/del answers
{"status":"OK"} and then appends its panel's HTML confirmation, which json_decode
rejects outright. WHMCS recorded a failed termination and left the service Suspended while
the account had already been deleted from the server.
The parser now takes the first complete JSON object off the front of a reply. It isn't
special-cased to del — account/susp and account/unsp go through the same CWP
controller.
Suspend, unsuspend and terminate check the server before reporting a failure. If the
account is already gone, already suspended or already active, the request has done what it
was asked to do, whatever CWP said on the way. Absence counts as success for a termination
and as a fault for a suspension, so the two aren't treated alike.
Mailbox passwords and password hashes kept out of the WHMCS module log. email/list
returns every mailbox's {SHA512-CRYPT} hash with no way to suppress it, and email/add
calls the password pass while email/udp calls it password — masking by field name
caught only the first. It's masked by shape now.
The module log no longer stores an entire response body on every call. The client area
fetches the mailbox list on every page view, so a large account wrote hundreds of rows into
tblmodulelog each time somebody opened their service page. Bodies cap at 4000 bytes and
payload rows at 20, each saying how much was left out.
CWP API notes
Worked out the hard way against a live server, in case they save someone else the time:
email/addtakes the local part only in itsemailfield, not the whole address —
CWP appends the domain itself. Sendtest@domain.co.zaand you get
testdomain.co.za@domain.co.za.email/udpandemail/delusemailbox(the full address) andpassword— not
emailandpass. The wrong names produce an HTTP 500 withUndefined offset: 1.quotais ignored onemail/addbut applied onemail/udp, in bytes.changepack/udptakes a package ID only. A name comes back as a bareErrorwith no
message.
v2.1.1 — turn off a resource-limits call the server refuses
Added
apply_resource_limits in config.php (default on).
The product's inode, open-file and process limits are applied through account/udp after a package change, and a refusal there has always been non-fatal — the package still moves. But some servers refuse that call with Account/UPD granted in API Manager, which put a failed call and a warning into the WHMCS Module Log on every package change, for a grant that could not be obtained.
Set it to false on such a server and the call is not made:
'apply_resource_limits' => false,The three limits then come from the CWP package alone, and must be set in the panel if they matter.
CWP checks that call internally as accout_upd — its own typo, and the same string the API Manager checkbox saves, so the two do agree. The check itself lives in encoded code, so there is no way to see from outside which condition is failing. If your server refuses it, this is the switch.
Upgrading
Replace the directory contents. No configuration changes are required; the default behaviour is unchanged.
v2.1.0 — package creation from products, and packages by name
Products can now create their CWP packages, and a package set by name works.
Added
Products can create their CWP packages. Ten new product options describe the package — disk quota, bandwidth, and the FTP, email, email-list, database, subdomain, parked-domain, addon-domain and hourly-email limits. Saving the product creates the package on every CWP server in that product's server group, or updates it if one of that name already exists. No more building the same package by hand on each server.
Off by default — push_packages_on_product_save in config.php. Enabling it decides ownership: WHMCS becomes the source of truth, and a package edited in CWP is overwritten the next time its product is saved. Needs ADD and UPD on Packages.
A limit left blank keeps CWP's own default; set it to 0 to mean none allowed.
The CWP Package field may be left blank, in which case the product's own name is used — both when pushing the package and when provisioning against it.
Fixed
A package change failed whenever the package was set by name. CWP's changepack takes a package id and nothing else — given a name it answers a bare Error with no explanation. Whatever the product holds, name or id, is now resolved against the server's own package list before anything is sent, so a name works and each server in a group can assign that package its own local id.
This replaces the existence check added in 2.0.3, which confirmed the package was real and then sent the unusable form of it anyway. Resolution still fails open on a key without list on Packages, and an unknown package is still refused with the available ones named.
tblservers.accesshash is not encrypted on every install. Where it holds the API key verbatim, running it through DecryptPassword does not fail — it reports success and returns binary noise, which CWP rejects as "No special characters are allowed!". A stored value that already looks like a key is now used as-is.
Upgrading
Replace the directory contents. Nothing else changes — the module type, config option order and stored usernames are all unchanged.
The ten new product options are appended after the existing five, so current settings carry over untouched. Fill them in only if you want package pushing.
Full detail in CHANGELOG.md.
v2.0.3
Fixed
-
An unknown package ID no longer leaves an account with no package. CWP's
changepackanswersstatus OKfor an ID that does not exist, silently detaching the
account from any package. The requested ID is now checked against the server's package
list before anything changes, and a mismatch is refused with the valid IDs listed.
Applies to account creation as well.The check needs
Packages/listand fails open without it, so it never blocks a
package change that would otherwise succeed.
Changed
- The Change Package button is hidden while
apply_package_on_service_saveis on,
since the Product/Service dropdown then does the same job. Hidden, not removed: WHMCS
draws the button becausecwp7_ChangePackageexists, and that function must stay —
a paid upgrade or downgrade order calls it, and dropping it would bill a customer for a
package the server never applies. Turning the setting off brings the button back.
v2.0.2
Security
- The module log no longer records the client's personal data. WHMCS passes a
model
parameter carrying the service, its product and the full client record; a failed module
command wrote the stored service password, the customer's name, postal address, phone
number and last-login IP into the WHMCS Module Log. Logging now uses an allow-list of
diagnostic fields, so nothing unanticipated is ever written.
Fixed
- Resource limits were never applied to new accounts. CWP's
addendpoint expects
limit_nofileandlimit_nproc;udpcalls the same two limitsopenfilesand
processes; the module sentnofileandnproc, which neither accepts. Every account
created by earlier versions therefore carries its CWP package defaults rather than the
product's open-file and process limits. Run Change Package on existing services to
apply them. - Package changes used the wrong endpoint entirely. CWP has a dedicated
/v1/changepackfor this, gated by the narrow "Account pack change" permission. The
module posted to/v1/accountwithaction=udp— a full account update, checked as a
broader grant — and with the package suffixed12@where that endpoint documents a
@12prefix. Package changes now go tochangepack, which takes the bare ID. Account/updis no longer required. The product's inode, open-file and process
limits are applied through it after the package moves, but a refusal is non-fatal, so a
key holding only "Account pack change" changes packages successfully.- A package change is now verified.
status OKis not evidence, so the account is
re-read afterwards and the operation fails loudly if CWP did not move it. - Account creation no longer times out at 20 seconds. Reads keep the short budget;
anything that changes the server getsprovision_timeout, 180 seconds by default.
Creating an account builds a user, home directory, vhost, DNS zone and mail
configuration, and takes far longer than a read. - A creation that times out is reconciled. CWP keeps working after the module gives
up, which previously left an account on the server and a failed service in WHMCS. The
account is now re-checked, and a creation that finished late is reported as the success
it was. - Error messages carry only advice that applies: the API Manager guidance appears solely
forUnauthorized action, a missing account is stated plainly, and the private-address
note no longer appears on a timeout, where the connection had in fact succeeded.
Added
hooks.php— optionally apply a package change to CWP when an admin changes a
service's Product/Service and saves, instead of pressing Change Package afterwards.
Off by default; enableapply_package_on_service_saveinconfig.php.
v2.0.1
Fixed
- Disk usage was imported as zero for every service. CWP's
account/listnames the
fielddiskused, which was not among the names the module looked for. - Disk usage now comes from
accountdetail.account/listreports a placeholder
rather than real consumption — the same account showsdiskused => 1there and
space_usage => 480715onaccountdetail. Bandwidth and all limits still come from
the single list call, which reports them accurately. - Responses under
resultare read. Current CWP builds return success payloads under
result, older ones undermsj, and errors undermsg. All three are accepted;
account/listandaccountdetailuse different keys as the same server. -1is understood as unlimited and stored as0, which is how WHMCS reads it. A
negative limit previously rendered as a negative usage bar.- Usage rows are matched to services by primary key rather than by a domain query, so a
domain shared by two services can no longer have both rewritten. - Terminated, cancelled and fraud services no longer trigger API calls. The admin
service page called CWP on every view for a service whose account no longer exists,
showing a rejection banner each time; usage import likewise treated dead services as
candidates, letting one shadow a live service that reused the same domain. - A missing account now reads as a plain statement rather than an error banner — that is
an ordinary state, not a fault.
Added
usage_detail_lookupinconfig.php(default on). The accurate disk figure costs one
extra API call per account that matches a WHMCS service on that server — accounts
with no service are skipped before the call is made. Set to false to use the single
list call and accept the placeholder figure.
v2.0.0
A full rewrite of the CWP provisioning module. The module name, directory and function
prefix are unchanged, so this drops straight over version 1.7 with no reconfiguration:
server entries and products keep working, and no service needs re-linking.
Requires PHP 7.4–8.3 and WHMCS 8.5–9.0.
Fixed
- Usage reporting now works. It matched
tblhosting.dedicatedipagainst CWP's
ip_address, butdedicatedipis empty for every shared-IP account, so the query
matched no rows. It now scopes by server ID and domain, as WHMCS documents. - Invalid
lastupdatetimestamps. Usage rows were written with
date('Y-m-d H:i:S')— capitalSis PHP's English ordinal suffix, producing values
like14:30:th, which MySQL rejects in strict mode. Now written withnow(). ChangePasswordsent a misspelledacctionfield, so the action never reached
CWP and the endpoint ran on its default behaviour.- Fatal
TypeErrorin the daily cron.count()was called on the API payload
without checking it was an array; on PHP 8 a missing or renamed key aborted the cron
run. All cron-facing paths are now exception-safe. curl_errno()was checked beforecurl_exec(), so transport errors were never
reported.- Module functions could return an array where WHMCS expects a string, producing
"Array to string conversion" instead of a usable error. - cURL handles are closed on every path.
Security
- TLS verification is enabled. Previously
CURLOPT_SSL_VERIFYPEERand
CURLOPT_SSL_VERIFYHOSTwere disabled on every call, while the request body carried
an API key with administrative scope over every account on the server. - The API key is no longer written to the WHMCS Module Log in plaintext. It is
masked in the request, passed to WHMCS's redaction, and stripped from CWP's own error
text — CWP echoes the submitted key back insideUnauthorized actionresponses. - Autologin tokens are no longer rendered into page HTML. A live session URL was
minted on every product-details page load and printed into the page, leaving it in
page source, browser history and referrer headers. Sessions are now minted on click
through WHMCS single sign-on. - Redirects are not followed and the transport is pinned to HTTPS, so a redirect cannot
replay the API key to another host. - Client-facing errors no longer carry raw CWP output, which can name other accounts and
filesystem paths.
Added
MetaData— display name, API version, default port, single sign-on label.TestConnection, with failure messages that distinguish DNS, refused connections,
timeouts and certificate faults, and name the address actually dialled.ServiceSingleSignOnfor panel login from the client area and admin service page.ListAccountsfor Server Sync, so existing CWP accounts can be listed and imported.AdminServicesTabFieldsshowing live account detail on the admin service page.Renewas an explicit no-op.- Client area overview output that makes no API call during page render.
- Product options for resource limits and a username length cap.
- Optional
config.phpfor TLS policy, ports and timeouts. - Support for both
msgandmsjresponse keys, which CWP has used at different times.
Changed
- Usernames are corrected to CWP's rules for new accounts only, and the corrected
value is written back to the service. Existing accounts are addressed exactly as
stored, so services created by earlier versions are unaffected. AdminLinkis a plain link to the CWP admin panel; it previously submitted an empty
form with no credentials.- Config option labels use
FriendlyName. Option order is unchanged, so existing
product settings carry over.
Removed
- AutoSSL triggering. CWP issues and renews AutoSSL certificates on its own schedule, so
the module does not call it and the API key needs no AutoSSL permission.