'Seafile Provisioning', 'APIVersion' => '1.2', 'DefaultNonSSLPort' => '8000', 'DefaultSSLPort' => '443', 'RequiresServer' => true, 'ServiceSingleSignOnLabel' => 'Login to Seafile', 'AdminSingleSignOnLabel' => 'Login to Seafile Admin', 'ListAccountsUniqueIdentifierDisplayName' => 'Domain', 'ListAccountsUniqueIdentifierField' => 'domain', 'ListAccountsProductField' => 'configoption1', ); } /** * Define SeaFile product configuration options. * * @see https://developers.whmcs.com/provisioning-modules/config-options/ * * @return array */ function seafile_ConfigOptions() { $configarray = array( 'quota' => array( 'Type' => 'text', 'Description' => 'Basis User-Quota für dieses Produkt in GB', 'Default' => '10', 'Size' => '15', 'FriendlyName' => 'User Quota', ), ); return $configarray; } /** * Test connection to a SeaFile server with the given server parameters. * * Allows an admin user to verify that an API connection can be * successfully made with the given configuration parameters for a * server. * * When defined in a module, a Test Connection button will appear * alongside the Server Type dropdown when adding or editing an * existing server. * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return array */ function seafile_TestConnection($params) { $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; error_log("Seafile:TestConnection: " . $seafileURL); //error_log(" -> " . $params['serverusername'] . " " . $params['serverpassword']); $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return array( 'success' => false, 'error' => 'Error: could not login to ' . $seafileURL, ); } return array( 'success' => true, 'error' => '', ); } /** * Usage Update * * Important: Runs daily per server not per product * Run Manually: /admin/reports.php?report=disk_usage_summary&action=updatestats * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/usage-update/ */ function seafile_UsageUpdate($params) { $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return 'Error: could not login to ' . $seafileURL; } $servicesObj = Capsule::table('tblhosting') ->select('*') ->where('server', '=', $params['serverid']) ->where('domainstatus', '=', 'Active') ->get(); foreach((array)$servicesObj as $serviceObj) { $service = get_object_vars($serviceObj[0]); $accountInfo = $seafileAPI->getAccount($service['username']); error_log("Seafile Module: Updating Disk Usage for: " . $accountInfo . " : " . round($accountInfo['quota_usage'] / 1000000,2) . "/" . round($accountInfo['quota_total'] / 1000000,2)); if(!isset($accountInfo['error_msg'])) { try { Capsule::table('tblhosting') ->where('id', '=', $service['id']) ->update( array( 'diskusage' => round($accountInfo['quota_usage'] / 1000000,2), 'disklimit' => round($accountInfo['quota_total'] / 1000000,2), 'lastupdate' => Capsule::raw('now()') ) ); } catch (Exception $e) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could update usage information for ' . $service['username'], $e->getMessage() ); } } } } /** * Client area output logic handling. * * This function is used to define module specific client area output. It should * return an array consisting of a template file and optional additional * template variables to make available to that template. * * The template file you return can be one of two types: * * * tabOverviewModuleOutputTemplate - The output of the template provided here * will be displayed as part of the default product/service client area * product overview page. * * * tabOverviewReplacementTemplate - Alternatively using this option allows you * to entirely take control of the product/service overview page within the * client area. * * Whichever option you choose, extra template variables are defined in the same * way. This demonstrates the use of the full replacement. * * Please Note: Using tabOverviewReplacementTemplate means you should display * the standard information such as pricing and billing details in your custom * template or they will not be visible to the end user. * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return array */ function seafile_ClientArea($params) { switch ($params['serverport']) { case '80': case '443': $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname']; break; default: $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; break; }; $app = 'https://itunes.apple.com/cn/app/seafile-pro/id639202512?l=en&mt=8'; $google = 'https://play.google.com/store/apps/details?id=com.seafile.seadroid2'; $driveWin = 'https://s3.eu-central-1.amazonaws.com/download.seadrive.org/seadrive-2.0.5-en.msi'; $winClient = 'https://s3.eu-central-1.amazonaws.com/download.seadrive.org/seafile-7.0.9-en.msi'; $macClient = 'https://s3.eu-central-1.amazonaws.com/download.seadrive.org/seafile-client-7.0.9.dmg'; $driveMac = 'https://s3.eu-central-1.amazonaws.com/download.seadrive.org/seadrive-2.0.5.dmg'; $linClient = 'https://download.seafile.com/published/seafile-user-manual/syncing_client/install_linux_client.md'; $clientInfo['basequota'] = $params['configoption1'] ? $params['configoption1'] : 1; $clientInfo['addonquota'] = $params['configoptions']['addonQuota'] ? $params['configoptions']['addonQuota'] : 0; $clientInfo['userquota'] = $clientInfo['basequota'] + $clientInfo['addonquota']; $clientInfo['mailaddress'] = $params['username']; $clientinfo['stitle'] = $params['model']['product']['name']; $langObj = Capsule::table('tblconfiguration') ->select('value') ->where('setting', '=', 'Language') ->get(); $lla = $_SESSION['Language'] ? trim($_SESSION['Language']) : strtolower($langObj[0]->value); $slang = array(); include_once dirname(__FILE__).'/lang/'.$lla.'.php'; return array( 'tabOverviewReplacementTemplate' => 'clientarea', 'vars' => array_merge( array( 'url' => $seafileURL, 'mobile1' => $app, 'mobile2' => $google, 'drivewin' => $driveWin, 'winclient' => $winClient, 'macclient' => $macClient, 'drivemac' => $driveMac, 'linClient' => $linClient, ), $slang, $clientInfo ), ); } /** * Change the password for a SeaFile account. * * Called when a password change is requested. This can occur either due to a * client requesting it via the client area or an admin requesting it from the * admin side. * * This option is only available to client end users when the product is in an * active status. * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return string 'success' or an error message */ function seafile_ChangePassword($params) { $checkPassword = seafileCheckPassword($params['password']); if ($checkPassword != null) { return $checkPassword; } $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return 'Error: could not login to ' . $seafileURL; } $userAccount = $seafileAPI->getAccount($params['username']); if(isset($userAccount['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not find account for: ' . $params['username'], $userAccount ); return 'Error: could not find account for: ' . $params['username']; } $result = $seafileAPI->modifyAccount(array('email' => $userAccount['email'], 'password' => $params['password'])); if(isset($result['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not change password for: ' . $params['username'], $result ); return 'Error: could not change password for: ' . $params['username']; } return 'success'; } /** * Set a new quota of a SeaFile account. * * Called to apply a quota change of the service. It * is called to provision upgrade or downgrade orders, as well as being * able to be invoked manually by an admin user. * * This same function is called for upgrades and downgrades of both * products and configurable options. * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return string 'success' or an error message */ function seafile_ChangePackage($params) { $quota = $params['configoption1'] ? $params['configoption1'] : 1; $addonQuota = $params['configoptions']['addonQuota'] ? $params['configoptions']['addonQuota'] : 0; $newAddQuota = $params['configoptions']['newAddQuota'] ? $params['configoptions']['newAddQuota'] : 0; $accountQuota = ($quota + $addonQuota + $newAddQuota); $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return 'Error: could not login to ' . $seafileURL; } $userAccount = $seafileAPI->getAccount($params['username']); if(isset($userAccount['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not find account ' . $params['username'], $userAccount ); return 'Error: could not find account ' . $params['username']; } $result = $seafileAPI->modifyAccount(array('email' => $userAccount['email'], 'quota_total' => $accountQuota * 1024)); if(isset($result['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not update quota for ' . $userAccount['email'], $result ); return 'Error: could not update quota for ' . $userAccount['email']; } elseif ($result['quota_total'] != ($accountQuota * 1048576)) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: quota for ' . $userAccount['email'] . ' not updated', $result ); return 'Error: quota for ' . $userAccount['email'] . ' not updated'; } try { Capsule::table('tblhosting') ->where('id', '=', $params['serviceid']) ->update( array( 'disklimit' => $userAccount['quota_total'] / 1024, ) ); } catch (Exception $e) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not update quota in database', $e->getMessage() ); return 'Error: could not update quota in database'; } if(seafileUpdateQuota($params) != 'success') { return 'Error: could not update addonQuota in database'; }; return 'success'; } /** * Provision a new instance of a SeaFile account. * * Attempt to provision a new SeaFile account. This is * called any time provisioning is requested inside of WHMCS. Depending upon the * configuration, this can be any of: * * When a new order is placed * * When an invoice for a new order is paid * * Upon manual request by an admin user * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return string 'success' or an error message */ function seafile_CreateAccount($params) { $firstName = $params['customfields']['firstname']; $lastName = $params['customfields']['lastname']; $loginEMail = $params['customfields']['login']; $loginPassword = $params['customfields']['password']; $baseQuota = $params['configoption1']; $addonQuota = $params['configoptions']['addonQuota'] ? $params['configoptions']['addonQuota'] : 0; $newAddQuota = $params['configoptions']['newAddQuota'] ? $params['configoptions']['newAddQuota'] : 0; //error_log( print_r($params,true) ); $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return 'Error: could not login to ' . $seafileURL; } $existingAccount = $seafileAPI->getAccount($loginEMail); if(!isset($existingAccount['error_msg'])) { return 'Error: account already exists ' . $loginEMail; } $accountQuota = ($baseQuota + $addonQuota + $newAddQuota) * 1024; $newAccount = array(); $newAccount['email'] = $loginEMail; $newAccount['name'] = $firstName . ' ' . $lastName; $newAccount['isActive'] = 1; $newAccount['isStaff'] = 0; $newAccount['password'] = $loginPassword; $newAccount['note'] = 'Account created from WHCMS for client ' . $params['userid']; $newAccount['quota_total'] = $accountQuota; error_log("Seafile:Create Account for " . $loginEMail . " with " . $accountQuota . "MB"); $result = $seafileAPI->createAccount($newAccount); if(isset($result['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not create account ' . $loginEMail, $result ); return 'Error: could not create account ' . $loginEMail; } try { Capsule::table('tblhosting') ->where('id', '=', $params['serviceid']) ->update( array( 'username' => $loginEMail, 'password' => $params['customfields']['password'], 'disklimit' => $accountQuota, 'diskusage' => 0, 'domain' => $loginEMail, ) ); } catch (\Exception $e) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could save username & password in database', $e->getMessage() ); return 'Error: could save username & password in database'; } if(seafileUpdateQuota($params) != 'success') { return 'Error: could not update addonQuota in database'; }; return 'success'; } /** * Set a SeaFile account to status inactive. * * Called when a suspension is requested. This is invoked automatically by WHMCS * when a product becomes overdue on payment or can be called manually by admin * user. * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return string 'success' or an error message */ function seafile_SuspendAccount($params) { $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return 'Error: could not login to ' . $seafileURL; } $userAccount = $seafileAPI->getAccount($params['username']); if(isset($userAccount['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not find account ' . $params['username'], $userAccount ); return 'Error: could not find account ' . $params['username']; } $result = $seafileAPI->modifyAccount(array('email' => $userAccount['email'], 'is_active' => 0)); if(isset($result['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not suspend ' . $params['username'], $result ); return 'Error: could not suspend ' . $params['username']; } elseif ($result['update_status_tip'] != 'Edit succeeded') { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: ' . $params['username'] . ' not deactivated', $result ); return 'Error: ' . $params['username'] . ' not deactivated'; } return 'success'; } /** * Set a SeaFile account to status active. * * Called when an un-suspension is requested. This is invoked * automatically upon payment of an overdue invoice for a product, or * can be called manually by admin user. * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return string 'success' or an error message */ function seafile_UnsuspendAccount($params) { $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return 'Error: could not login to ' . $seafileURL; } $userAccount = $seafileAPI->getAccount($params['username']); if(isset($userAccount['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not find account ' . $params['username'], $userAccount ); return 'Error: could not find account ' . $params['username']; } $result = $seafileAPI->modifyAccount(array('email' => $userAccount['email'], 'is_active' => 1)); if(isset($result['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not suspend ' . $params['username'], $result ); return 'Error: could not suspend ' . $params['username']; } elseif ($result['update_status_tip'] != 'Edit succeeded') { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: ' . $params['username'] . ' not activated', $result ); return 'Error: ' . $params['username'] . ' not activated'; } return 'success'; } /** * Removes a SeaFile account. * * Called when a termination is requested. This can be invoked automatically for * overdue products if enabled, or requested manually by an admin user. * * @param array $params common module parameters * * @see https://developers.whmcs.com/provisioning-modules/module-parameters/ * * @return string 'success' or an error message */ function seafile_TerminateAccount($params) { $seafileURL = $params['serverhttpprefix'] . '://' . $params['serverhostname'] . ':' . $params['serverport']; $seafileAPI = new Sf_Admin($seafileURL,$params['serverusername'],$params['serverpassword']); $response = $seafileAPI->login(); if (isset($response['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not login to ' . $seafileURL, $response ); return 'Error: could not login to ' . $seafileURL; } $existingAccount = $seafileAPI->getAccount($params['username']); if(isset($existingAccount['error_msg'])) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not find account ' . $params['username'], '' ); return 'Error: could not find account ' . $params['username']; } //if ($existingAccount['is_active'] == 1) { // return 'Account '. $params['username'] . ' is active, suspend account first!'; //} $result = $seafileAPI->deleteAccount($params['username']); if($result != true) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Error: could not remove ' . $params['username'], $seafileAPI ); return 'Error: could not remove ' . $params['username']; } return 'success'; } /** * server side password check * * recheck the client side password check * in case that the client side check has been disabled * * @param string $pwd password * * @return string missing features or null if the password matches our needs */ function seafileCheckPassword($pwd) { if (strlen($pwd) < 8) { return 'Das das Passwort ist zu kurz. Es werden mind. 8 Zeichen benötigt'; } if (!preg_match('#[0-9]+#', $pwd)) { return 'Das Passwort muss mindestens eine Zahl enthalten'; } if (!preg_match('#[A-Z]+#', $pwd)) { return 'Das Passwort muss mindestens einen Grossbuchstaben (A-Z) enthalten'; } if (!preg_match('#[a-z]+#', $pwd)) { return 'Das Passwort muss mindestens einen Kleinbuchstaben (a-z) enthalten'; } if (!preg_match('#[^\w]+#', $pwd)) { return 'Das Passwort muss mindestens ein Sonderzeichen (.,-:=) enthalten'; } return null; } /** * Perform an update of customfields to prevent downgrades. * * Called in changePackage or createAccount functions. * * @param array $params common module parameters * * @return *success* or an error */ function seafileUpdateQuota($params) { if(isset($params['configoptions']['addonQuota'])) { $addonQuota = $params['configoptions']['addonQuota'] ? $params['configoptions']['addonQuota'] : 0 ; $newAddQuota = $params['configoptions']['newAddQuota'] ? $params['configoptions']['newAddQuota'] : 0; $addonQuota = $addonQuota + $newAddQuota; $addonQuotaFieldIDObj = Capsule::table('tblproductconfigoptions') ->join('tblhostingconfigoptions', 'tblproductconfigoptions.id', '=', 'tblhostingconfigoptions.configid') ->where('tblhostingconfigoptions.relid', '=', $params['serviceid']) ->where('tblproductconfigoptions.optionname', 'like', 'addonQuota%') ->select('tblhostingconfigoptions.id', 'tblproductconfigoptions.qtymaximum') ->get(); if($addonQuota > $addonQuotaFieldIDObj[0]->qtymaximum) { logModuleCall( 'seafile', __FUNCTION__, $params, 'Info: someone is trying to exceed the maximum size', '' ); $addonQuota = $addonQuotaFieldIDObj[0]->qtymaximum; } try { $updateAddonQuota = Capsule::table('tblhostingconfigoptions') ->where('id', $addonQuotaFieldIDObj[0]->id) ->update( [ 'qty' => $addonQuota, ] ); } catch (\Exception $e) { logModuleCall( 'seafile', __FUNCTION__, $updateAddonQuota, 'Error: could not save addonOuota in database.', $e->getMessage() ); return 'Error: could not save addonOuota in database.'; } $newAddQuotaFieldIDObj = Capsule::table('tblproductconfigoptions') ->join('tblhostingconfigoptions', 'tblproductconfigoptions.id', '=', 'tblhostingconfigoptions.configid') ->where('tblhostingconfigoptions.relid', '=', $params['serviceid']) ->where('tblproductconfigoptions.optionname', 'like', 'newAddQuota%') ->select('tblhostingconfigoptions.id') ->get(); try { $updateNewAddQuota = Capsule::table('tblhostingconfigoptions') ->where('id', $newAddQuotaFieldIDObj[0]->id) ->update( [ 'qty' => '0', ] ); } catch (\Exception $e) { logModuleCall( 'seafile', __FUNCTION__, $updateNewAddQuota, 'Error: could not reset newAddOuota in database.', $e->getMessage() ); return 'Error: could not reset newAddOuota in database.'; } } return 'success'; } ?>