🔥 remove remaining legacy saas code
This commit is contained in:
parent
e232469551
commit
0f5cb8b1f8
123 changed files with 10 additions and 10171 deletions
|
@ -1,439 +0,0 @@
|
|||
<?php
|
||||
|
||||
define("AIRTIME_PRO_FREE_TRIAL_PLAN_ID", 34);
|
||||
define("WHMCS_AIRTIME_GROUP_ID", 15);
|
||||
|
||||
class Billing
|
||||
{
|
||||
// TODO: remove this once all existing customers have bandwidth limits set
|
||||
public static $PLAN_TYPE_DEFAULTS = array(
|
||||
"trial" => array(
|
||||
"bandwidth_limit" => 3298534883328
|
||||
),
|
||||
"hobbyist" => array(
|
||||
"bandwidth_limit" => 1099511627776
|
||||
),
|
||||
"starter" => array(
|
||||
"bandwidth_limit" => 3298534883328
|
||||
),
|
||||
"starter2" => array(
|
||||
"bandwidth_limit" => 3298534883328
|
||||
),
|
||||
"plus" => array(
|
||||
"bandwidth_limit" => 10995116277760
|
||||
),
|
||||
"plus2" => array(
|
||||
"bandwidth_limit" => 10995116277760
|
||||
),
|
||||
"premium" => array(
|
||||
"bandwidth_limit" => 43980465111040
|
||||
),
|
||||
"premium2" => array(
|
||||
"bandwidth_limit" => 43980465111040
|
||||
),
|
||||
"enterprise" => array(
|
||||
"bandwidth_limit" => 164926744166400
|
||||
),
|
||||
"complimentary" => array(
|
||||
"bandwidth_limit" => 32985348833280
|
||||
),
|
||||
"sida" => array(
|
||||
"bandwidth_limit" => 32985348833280
|
||||
),
|
||||
"custom" => array(
|
||||
"bandwidth_limit" => 10995116277760
|
||||
),
|
||||
"awesome-hobbyist-2015" => array(
|
||||
"bandwidth_limit" => 1099511627776
|
||||
),
|
||||
"awesome-starter-2015" => array(
|
||||
"bandwidth_limit" => 3298534883328
|
||||
),
|
||||
"awesome-plus-2015" => array(
|
||||
"bandwidth_limit" => 10995116277760
|
||||
),
|
||||
"awesome-premium-2015" => array(
|
||||
"bandwidth_limit" => 43980465111040
|
||||
),
|
||||
);
|
||||
|
||||
public static function getAPICredentials()
|
||||
{
|
||||
return array(
|
||||
"username" => $_SERVER["WHMCS_USERNAME"],
|
||||
"password" => $_SERVER["WHMCS_PASSWORD"],
|
||||
"url" => "https://account.sourcefabric.com/includes/api.php?accesskey=".$_SERVER["WHMCS_ACCESS_KEY"],
|
||||
);
|
||||
}
|
||||
|
||||
/** Get the Airtime instance ID of the instance the customer is currently viewing. */
|
||||
public static function getClientInstanceId()
|
||||
{
|
||||
//$currentProduct = Billing::getClientCurrentAirtimeProduct();
|
||||
//return $currentProduct["id"];
|
||||
//XXX: Major hack attack. Since this function gets called often, rather than querying WHMCS
|
||||
// we're just going to extract it from airtime.conf since it's the same as the rabbitmq username.
|
||||
$CC_CONFIG = Config::getConfig();
|
||||
$instanceId = $CC_CONFIG['rabbitmq']['user'];
|
||||
if (!is_numeric($instanceId)) {
|
||||
throw new Exception("Invalid instance id in " . __FUNCTION__ . ": " . $instanceId);
|
||||
}
|
||||
return $instanceId;
|
||||
}
|
||||
|
||||
public static function getProducts()
|
||||
{
|
||||
//Making this static to cache the products during a single HTTP request.
|
||||
//This saves us roundtrips to WHMCS if getProducts() is called multiple times.
|
||||
static $products = array();
|
||||
if (!empty($products))
|
||||
{
|
||||
return $products;
|
||||
}
|
||||
|
||||
$credentials = self::getAPICredentials();
|
||||
|
||||
$postfields = array();
|
||||
$postfields["username"] = $credentials["username"];
|
||||
$postfields["password"] = md5($credentials["password"]);
|
||||
$postfields["action"] = "getproducts";
|
||||
$postfields["responsetype"] = "json";
|
||||
//gid is the Airtime product group id on whmcs
|
||||
$postfields["gid"] = WHMCS_AIRTIME_GROUP_ID;
|
||||
|
||||
$query_string = "";
|
||||
foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&";
|
||||
|
||||
$result = self::makeRequest($credentials["url"], $query_string);
|
||||
//Logging::info($result["products"]["product"]);
|
||||
$products = $result["products"]["product"];
|
||||
|
||||
//Blacklist all free plans
|
||||
//Hide the promo plans - we will tell the user if they are eligible for a promo plan
|
||||
foreach ($products as $k => $p) {
|
||||
if ($p["paytype"] === "free" || strpos($p["name"], "Awesome August 2015") !== false)
|
||||
{
|
||||
unset($products[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
public static function getProductPricesAndTypes()
|
||||
{
|
||||
$products = Billing::getProducts();
|
||||
$productPrices = array();
|
||||
$productTypes = array();
|
||||
|
||||
foreach ($products as $k => $p) {
|
||||
$productPrices[$p["name"]] = array(
|
||||
"monthly" => $p["pricing"]["USD"]["monthly"],
|
||||
"annually" => $p["pricing"]["USD"]["annually"]
|
||||
);
|
||||
$productTypes[$p["pid"]] = $p["name"] . " ($" . $productPrices[$p['name']]['monthly'] . "/mo)";
|
||||
}
|
||||
return array($productPrices, $productTypes);
|
||||
}
|
||||
|
||||
/** Get the plan (or product in WHMCS lingo) that the customer is currently on.
|
||||
* @return An associative array containing the fields for the product
|
||||
* */
|
||||
public static function getClientCurrentAirtimeProduct()
|
||||
{
|
||||
static $airtimeProduct = null;
|
||||
//Ghetto caching to avoid multiple round trips to WHMCS
|
||||
if ($airtimeProduct) {
|
||||
return $airtimeProduct;
|
||||
}
|
||||
$credentials = self::getAPICredentials();
|
||||
|
||||
$postfields = array();
|
||||
$postfields["username"] = $credentials["username"];
|
||||
$postfields["password"] = md5($credentials["password"]);
|
||||
$postfields["action"] = "getclientsproducts";
|
||||
$postfields["responsetype"] = "json";
|
||||
$postfields["clientid"] = Application_Model_Preference::GetClientId();
|
||||
|
||||
$query_string = "";
|
||||
foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&";
|
||||
|
||||
$result = self::makeRequest($credentials["url"], $query_string);
|
||||
|
||||
//XXX: Debugging / local testing
|
||||
if ($_SERVER['SERVER_NAME'] == "localhost") {
|
||||
$_SERVER['SERVER_NAME'] = "bananas.airtime.pro";
|
||||
}
|
||||
|
||||
//This code must run on airtime.pro for it to work... it's trying to match
|
||||
//the server's hostname with the client subdomain. Once it finds a match
|
||||
//between the product and the server's hostname/subdomain, then it
|
||||
//returns the ID of that product (aka. the service ID of an Airtime instance)
|
||||
foreach ($result["products"]["product"] as $product)
|
||||
{
|
||||
if (strpos($product["groupname"], "Airtime") === FALSE)
|
||||
{
|
||||
//Ignore non-Airtime products
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($product["status"] === "Active" ||
|
||||
$product["status"] === "Suspended") {
|
||||
$airtimeProduct = $product;
|
||||
$subdomain = '';
|
||||
|
||||
foreach ($airtimeProduct['customfields']['customfield'] as $customField) {
|
||||
if ($customField['name'] === SUBDOMAIN_WHMCS_CUSTOM_FIELD_NAME) {
|
||||
$subdomain = $customField['value'];
|
||||
if (($subdomain . ".airtime.pro") === $_SERVER['SERVER_NAME']) {
|
||||
return $airtimeProduct;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception("Unable to match subdomain to a service ID");
|
||||
}
|
||||
|
||||
public static function getClientDetails()
|
||||
{
|
||||
try {
|
||||
$credentials = self::getAPICredentials();
|
||||
|
||||
$postfields = array();
|
||||
$postfields["username"] = $credentials["username"];
|
||||
$postfields["password"] = md5($credentials["password"]);
|
||||
$postfields["action"] = "getclientsdetails";
|
||||
$postfields["stats"] = true;
|
||||
$postfields["clientid"] = Application_Model_Preference::GetClientId();
|
||||
$postfields["responsetype"] = "json";
|
||||
|
||||
$query_string = "";
|
||||
foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&";
|
||||
|
||||
$arr = self::makeRequest($credentials["url"], $query_string);
|
||||
return $arr["client"];
|
||||
} catch (Exception $e) {
|
||||
Logging::info($e->getMessage());
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public static function makeRequest($url, $query_string) {
|
||||
try {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 ); // WHMCS IP whitelist doesn't support IPv6
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5); //Aggressive 5 second timeout
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $query_string);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
$jsondata = curl_exec($ch);
|
||||
if (curl_error($ch)) {
|
||||
//die("Connection Error: ".curl_errno($ch).' - '.curl_error($ch));
|
||||
throw new Exception("WHMCS server down or invalid request.");
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
return json_decode($jsondata, true);
|
||||
} catch (Exception $e) {
|
||||
Logging::info($e->getMessage());
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public static function ensureClientIdIsValid()
|
||||
{
|
||||
if (Application_Model_Preference::GetClientId() == null)
|
||||
{
|
||||
throw new Exception("Invalid client ID: " . Application_Model_Preference::GetClientId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return True if VAT should be applied to the order, false otherwise.
|
||||
*/
|
||||
public static function checkIfVatShouldBeApplied($vatNumber, $countryCode)
|
||||
{
|
||||
if ($countryCode === 'UK') {
|
||||
$countryCode = 'GB'; //VIES database has it as GB
|
||||
}
|
||||
//We don't charge you VAT if you're not in the EU
|
||||
if (!Billing::isCountryInEU($countryCode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//So by here, we know you're in the EU.
|
||||
|
||||
//No VAT number? Then we charge you VAT.
|
||||
if (empty($vatNumber)) {
|
||||
return true;
|
||||
}
|
||||
//Check if VAT number is valid
|
||||
return Billing::validateVATNumber($vatNumber, $countryCode);
|
||||
}
|
||||
|
||||
public static function isCountryInEU($countryCode)
|
||||
{
|
||||
$euCountryCodes = array('BE', 'BG', 'CZ', 'DK', 'DE', 'EE', 'IE', 'EL', 'ES', 'FR',
|
||||
'HR', 'IT', 'CY', 'LV', 'LT', 'LU', 'HU', 'MT', 'NL', 'AT',
|
||||
'PL', 'PT', 'RO', 'SI', 'SK', 'FI', 'SE', 'UK', 'GB');
|
||||
|
||||
if (!in_array($countryCode, $euCountryCodes)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an EU VAT number is valid, using the EU VIES validation web API.
|
||||
*
|
||||
* @param string $vatNumber - A VAT identifier (number), with or without the two letter country code at the
|
||||
* start (either one works) .
|
||||
* @param string $countryCode - A two letter country code
|
||||
* @return boolean true if the VAT number is valid, false otherwise.
|
||||
*/
|
||||
public static function validateVATNumber($vatNumber, $countryCode)
|
||||
{
|
||||
$vatNumber = str_replace(array(' ', '.', '-', ',', ', '), '', trim($vatNumber));
|
||||
|
||||
//If the first two letters are a country code, use that as the country code and remove those letters.
|
||||
$firstTwoCharacters = substr($vatNumber, 0, 2);
|
||||
if (preg_match("/[a-zA-Z][a-zA-Z]/", $firstTwoCharacters) === 1) {
|
||||
$countryCode = strtoupper($firstTwoCharacters); //The country code from the VAT number overrides your country.
|
||||
$vatNumber = substr($vatNumber, 2);
|
||||
}
|
||||
$client = new SoapClient("http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl");
|
||||
|
||||
if($client){
|
||||
$params = array('countryCode' => $countryCode, 'vatNumber' => $vatNumber);
|
||||
try{
|
||||
$r = $client->checkVat($params);
|
||||
if($r->valid == true){
|
||||
// VAT-ID is valid
|
||||
return true;
|
||||
} else {
|
||||
// VAT-ID is NOT valid
|
||||
return false;
|
||||
}
|
||||
} catch(SoapFault $e) {
|
||||
Logging::error('VIES EU VAT validation error: '.$e->faultstring);
|
||||
if ($e->faultstring == "INVALID_INPUT") {
|
||||
return false;
|
||||
}
|
||||
//If there was another error with the VAT validation service, we allow
|
||||
//the VAT number to pass. (eg. SERVER_BUSY, MS_UNAVAILABLE, TIMEOUT, SERVICE_UNAVAILABLE)
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Connection to host not possible, europe.eu down?
|
||||
Logging::error('VIES EU VAT validation error: Host unreachable');
|
||||
//If there was an error with the VAT validation service, we allow
|
||||
//the VAT number to pass.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function addVatToInvoice($invoice_id)
|
||||
{
|
||||
$credentials = self::getAPICredentials();
|
||||
|
||||
//First we need to get the invoice details: sub total, and total
|
||||
//so we can calcuate the amount of VAT to add
|
||||
$invoicefields = array();
|
||||
$invoicefields["username"] = $credentials["username"];
|
||||
$invoicefields["password"] = md5($credentials["password"]);
|
||||
$invoicefields["action"] = "getinvoice";
|
||||
$invoicefields["invoiceid"] = $invoice_id;
|
||||
$invoicefields["responsetype"] = "json";
|
||||
|
||||
$invoice_query_string = "";
|
||||
foreach ($invoicefields as $k=>$v) $invoice_query_string .= "$k=".urlencode($v)."&";
|
||||
|
||||
//TODO: error checking
|
||||
$result = Billing::makeRequest($credentials["url"], $invoice_query_string);
|
||||
|
||||
$vat_amount = $result["subtotal"] * (VAT_RATE/100);
|
||||
$invoice_total = $result["total"] + $vat_amount;
|
||||
|
||||
//Second, update the invoice with the VAT amount and updated total
|
||||
$postfields = array();
|
||||
$postfields["username"] = $credentials["username"];
|
||||
$postfields["password"] = md5($credentials["password"]);
|
||||
$postfields["action"] = "updateinvoice";
|
||||
$postfields["invoiceid"] = $invoice_id;
|
||||
$postfields["tax"] = "$vat_amount";
|
||||
$postfields["taxrate"] = strval(VAT_RATE);
|
||||
$postfields["total"] = "$invoice_total";
|
||||
$postfields["responsetype"] = "json";
|
||||
|
||||
$query_string = "";
|
||||
foreach ($postfields as $k=>$v) $query_string .= "$k=".urlencode($v)."&";
|
||||
|
||||
//TODO: error checking
|
||||
$result = Billing::makeRequest($credentials["url"], $query_string);
|
||||
}
|
||||
|
||||
public static function getInvoices()
|
||||
{
|
||||
Billing::ensureClientIdIsValid();
|
||||
$credentials = Billing::getAPICredentials();
|
||||
|
||||
$postfields = array();
|
||||
$postfields["username"] = $credentials["username"];
|
||||
$postfields["password"] = md5($credentials["password"]);
|
||||
$postfields["action"] = "getinvoices";
|
||||
$postfields["responsetype"] = "json";
|
||||
$postfields["userid"] = Application_Model_Preference::GetClientId();
|
||||
|
||||
$query_string = "";
|
||||
foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&";
|
||||
|
||||
$result = Billing::makeRequest($credentials["url"], $query_string);
|
||||
|
||||
$invoices = array();
|
||||
if ($result["invoices"]) {
|
||||
$invoices = $result["invoices"]["invoice"];
|
||||
}
|
||||
return $invoices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the customer has any unpaid invoices and if so, returns
|
||||
* the ID of one of them. Returns 0 otherwise.
|
||||
*/
|
||||
public static function checkForUnpaidInvoice() {
|
||||
$invoices = self::getInvoices();
|
||||
$unpaidInvoice = 0;
|
||||
$unpaidInvoices = 0;
|
||||
foreach ($invoices as $invoice)
|
||||
{
|
||||
if ($invoice['status'] == 'Unpaid') {
|
||||
$unpaidInvoices += 1;
|
||||
$unpaidInvoice = $invoice;
|
||||
}
|
||||
}
|
||||
if ($unpaidInvoices > 0) {
|
||||
return $unpaidInvoice;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static function isStationPodcastAllowed() {
|
||||
$planLevel = Application_Model_Preference::GetPlanLevel();
|
||||
if ($planLevel == "hobbyist") {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
<?php
|
||||
|
||||
class Application_Common_GoogleAnalytics
|
||||
{
|
||||
|
||||
/** Returns a string containing the JavaScript code to pass some billing account info
|
||||
* into Google Tag Manager / Google Analytics, so we can track things like the plan type.
|
||||
*/
|
||||
public static function generateGoogleTagManagerDataLayerJavaScript()
|
||||
{
|
||||
$code = "";
|
||||
if (LIBRETIME_ENABLE_GOOGLE_ANALYTICS !== true) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
try {
|
||||
$clientId = Application_Model_Preference::GetClientId();
|
||||
|
||||
$plan = Application_Model_Preference::GetPlanLevel();
|
||||
$isTrial = ($plan == "trial");
|
||||
|
||||
//Figure out how long the customer has been around using a mega hack.
|
||||
//(I'm avoiding another round trip to WHMCS for now...)
|
||||
//We calculate it based on the trial end date...
|
||||
$trialEndDateStr = Application_Model_Preference::GetTrialEndingDate();
|
||||
if ($trialEndDateStr == '') {
|
||||
$accountDuration = 0;
|
||||
} else {
|
||||
$today = new DateTime();
|
||||
$trialEndDate = new DateTime($trialEndDateStr);
|
||||
$trialDuration = new DateInterval("P30D"); //30 day trial duration
|
||||
$accountCreationDate = $trialEndDate->sub($trialDuration);
|
||||
$interval = $today->diff($accountCreationDate);
|
||||
$accountDuration = $interval->days;
|
||||
}
|
||||
|
||||
$code = "$( document ).ready(function() {
|
||||
dataLayer.push({
|
||||
'UserID': '" . $clientId . "',
|
||||
'Customer': 'Customer',
|
||||
'PlanType': '" . $plan . "',
|
||||
'Trial': '" . $isTrial . "',
|
||||
'AccountDuration': '" . strval($accountDuration) . "'
|
||||
});
|
||||
});";
|
||||
//No longer sending these variables because we used to make a query to WHMCS
|
||||
//to fetch them, which was slow.
|
||||
// 'ZipCode': '" . $postcode . "',
|
||||
// 'Country': '" . $country . "',
|
||||
|
||||
} catch (Exception $e) {
|
||||
Logging::error($e);
|
||||
return "";
|
||||
}
|
||||
return $code;
|
||||
}
|
||||
|
||||
/** Generate the JavaScript snippet that logs a trial to paid conversion */
|
||||
public static function generateConversionTrackingJavaScript()
|
||||
{
|
||||
$code = "";
|
||||
if (LIBRETIME_ENABLE_GOOGLE_ANALYTICS !== true) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
$newPlan = Application_Model_Preference::GetPlanLevel();
|
||||
$oldPlan = Application_Model_Preference::GetOldPlanLevel();
|
||||
|
||||
$code = "dataLayer.push({'event': 'Conversion',
|
||||
'Conversion': 'Trial to Paid',
|
||||
'Old Plan' : '$oldPlan',
|
||||
'New Plan' : '$newPlan'});";
|
||||
return $code;
|
||||
}
|
||||
|
||||
/** Return true if the user used to be on a trial plan and was just converted to a paid plan. */
|
||||
public static function didPaidConversionOccur($request)
|
||||
{
|
||||
if (LIBRETIME_ENABLE_GOOGLE_ANALYTICS !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||
if ($userInfo) {
|
||||
$user = new Application_Model_User($userInfo->id);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldPlan = Application_Model_Preference::GetOldPlanLevel();
|
||||
|
||||
if ($user->isSuperAdmin() &&
|
||||
!$user->isSourcefabricAdmin() &&
|
||||
$request->getControllerKey() !== "thank-you")
|
||||
{
|
||||
//Only tracking trial->paid conversions for now.
|
||||
if ($oldPlan == "trial")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
// Global functions for translating domain-specific strings
|
||||
|
||||
function _pro($str) {
|
||||
return dgettext("pro", $str);
|
||||
}
|
||||
|
||||
class Application_Common_LocaleHelper {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,242 +0,0 @@
|
|||
<?php
|
||||
|
||||
/** This class provides the business logic for station provisioning. */
|
||||
class ProvisioningHelper
|
||||
{
|
||||
|
||||
/* @var $dbh PDO */
|
||||
static $dbh;
|
||||
|
||||
// Parameter values
|
||||
private $dbuser, $dbpass, $dbname, $dbhost, $dbowner, $apikey;
|
||||
private $instanceId;
|
||||
private $stationName, $description;
|
||||
private $defaultIcecastPassword;
|
||||
private $bandwidthLimit;
|
||||
|
||||
public function __construct($apikey)
|
||||
{
|
||||
$this->apikey = $apikey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint for setting up and installing the Airtime database. This all has to be done without Zend
|
||||
* which is why the code looks so old school (eg. http_response_code). (We can't currently bootstrap our
|
||||
* Zend app without the database unfortunately.)
|
||||
*/
|
||||
public function createAction()
|
||||
{
|
||||
$apikey = "";
|
||||
if (isset($_SERVER['PHP_AUTH_USER']))
|
||||
{
|
||||
$apikey = $_SERVER['PHP_AUTH_USER'];
|
||||
}
|
||||
if ($apikey != $this->apikey) {
|
||||
Logging::info("Invalid API Key: $apikey");
|
||||
http_response_code(403);
|
||||
echo "ERROR: Incorrect API key";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
$this->parsePostParams();
|
||||
|
||||
//For security, the Airtime Pro provisioning system creates the database for the user.
|
||||
if ($this->dbhost && !empty($this->dbhost)) {
|
||||
$this->setNewDatabaseConnection();
|
||||
|
||||
if (!$this->checkDatabaseExists()) {
|
||||
throw new DatabaseDoesNotExistException("ERROR: $this->dbname database does not exist.");
|
||||
}
|
||||
|
||||
//We really want to do this check because all the Propel-generated SQL starts with "DROP TABLE IF EXISTS".
|
||||
//If we don't check, then a second call to this API endpoint would wipe all the tables!
|
||||
if ($this->checkTablesExist()) {
|
||||
throw new DatabaseAlreadyExistsException();
|
||||
}
|
||||
|
||||
$this->createDatabaseTables();
|
||||
$this->initializeMusicDirsTable($this->instanceId);
|
||||
}
|
||||
|
||||
//$this->createDatabase();
|
||||
|
||||
//All we need to do is create the database tables.
|
||||
|
||||
$this->initializePrefs();
|
||||
} catch (DatabaseDoesNotExistException $e) {
|
||||
http_response_code(400);
|
||||
Logging::error($e->getMessage());
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
return;
|
||||
} catch (DatabaseAlreadyExistsException $e) {
|
||||
// When we recreate a terminated instance, the process will fail
|
||||
// if we return a 40x response here. In order to circumvent this,
|
||||
// just return a 200; we still avoid dropping the existing tables
|
||||
http_response_code(200);
|
||||
Logging::info($e->getMessage());
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
http_response_code(201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database settings and credentials given are valid
|
||||
* @return boolean true if the database given exists and the user is valid and can access it
|
||||
*/
|
||||
private function checkDatabaseExists()
|
||||
{
|
||||
$statement = self::$dbh->prepare("SELECT datname FROM pg_database WHERE datname = :dbname");
|
||||
$statement->execute(array(":dbname" => $this->dbname));
|
||||
$result = $statement->fetch();
|
||||
return isset($result[0]);
|
||||
}
|
||||
|
||||
private function checkTablesExist()
|
||||
{
|
||||
try {
|
||||
$result = self::$dbh->query("SELECT 1 FROM cc_files LIMIT 1");
|
||||
} catch (Exception $e) {
|
||||
// We got an exception == table not found
|
||||
echo($e . PHP_EOL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Result is either boolean FALSE (no table found) or PDOStatement Object (table found)
|
||||
return $result !== FALSE;
|
||||
}
|
||||
|
||||
private function parsePostParams()
|
||||
{
|
||||
$this->dbuser = $_POST['dbuser'];
|
||||
$this->dbpass = $_POST['dbpass'];
|
||||
$this->dbname = $_POST['dbname'];
|
||||
$this->dbhost = $_POST['dbhost'];
|
||||
$this->dbowner = $_POST['dbowner'];
|
||||
$this->instanceId = $_POST['instanceid'];
|
||||
|
||||
if (isset($_POST['station_name'])) {
|
||||
$this->stationName = $_POST['station_name'];
|
||||
}
|
||||
if (isset($_POST['description'])) {
|
||||
$this->description = $_POST['description'];
|
||||
}
|
||||
if (isset($_POST['icecast_pass'])) {
|
||||
$this->defaultIcecastPassword = $_POST['icecast_pass'];
|
||||
}
|
||||
if (isset($_POST['bandwidth_limit'])) {
|
||||
$this->bandwidthLimit = $_POST['bandwidth_limit'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a new database connection based on the parameters in the request
|
||||
* @throws PDOException upon failure to connect
|
||||
*/
|
||||
private function setNewDatabaseConnection()
|
||||
{
|
||||
self::$dbh = new PDO("pgsql:host=" . $this->dbhost
|
||||
. ";dbname=" . $this->dbname
|
||||
. ";port=5432" . ";user=" . $this->dbuser
|
||||
. ";password=" . $this->dbpass);
|
||||
//Turn on PDO exceptions because they're off by default.
|
||||
//self::$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$err = self::$dbh->errorInfo();
|
||||
if ($err[1] != null) {
|
||||
throw new PDOException("ERROR: Could not connect to database");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Airtime database using the given credentials
|
||||
* @throws Exception
|
||||
*/
|
||||
private function createDatabase()
|
||||
{
|
||||
Logging::info("Creating database...");
|
||||
$statement = self::$dbh->prepare("CREATE DATABASE " . pg_escape_string($this->dbname)
|
||||
. " WITH ENCODING 'UTF8' TEMPLATE template0"
|
||||
. " OWNER " . pg_escape_string($this->dbowner));
|
||||
if (!$statement->execute()) {
|
||||
throw new Exception("ERROR: Failed to create Airtime database");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the Airtime database
|
||||
* @throws Exception
|
||||
*/
|
||||
private function createDatabaseTables()
|
||||
{
|
||||
Logging::info("Creating database tables...");
|
||||
$sqlDir = dirname(APPLICATION_PATH) . "/build/sql/";
|
||||
$files = array("schema.sql", "sequences.sql", "views.sql", "triggers.sql", "defaultdata.sql");
|
||||
foreach ($files as $f) {
|
||||
/*
|
||||
* Unfortunately, we need to use exec here due to PDO's lack of support for importing
|
||||
* multi-line .sql files. PDO->exec() almost works, but any SQL errors stop the import,
|
||||
* so the necessary DROPs on non-existent tables make it unusable. Prepared statements
|
||||
* have multiple issues; they similarly die on any SQL errors, fail to read in multi-line
|
||||
* commands, and fail on any unescaped ? or $ characters.
|
||||
*/
|
||||
exec("PGPASSWORD=$this->dbpass psql -U $this->dbuser --dbname $this->dbname -h $this->dbhost -f $sqlDir$f", $out, $status);
|
||||
if ($status != 0) {
|
||||
throw new Exception("ERROR: Failed to create database tables");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function initializeMusicDirsTable($instanceId)
|
||||
{
|
||||
if (!is_string($instanceId) || empty($instanceId) || !is_numeric($instanceId))
|
||||
{
|
||||
throw new Exception("Invalid instance id: " . $instanceId);
|
||||
}
|
||||
|
||||
$instanceIdPrefix = $instanceId[0];
|
||||
|
||||
//Reinitialize Propel, just in case...
|
||||
Propel::init(__DIR__."/../configs/airtime-conf-production.php");
|
||||
|
||||
//Create the cc_music_dir entry
|
||||
$musicDir = new CcMusicDirs();
|
||||
$musicDir->setType("stor");
|
||||
$musicDir->setExists(true);
|
||||
$musicDir->setWatched(true);
|
||||
$musicDir->setDirectory("/mnt/airtimepro/instances/$instanceIdPrefix/$instanceId/srv/airtime/stor/");
|
||||
$musicDir->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize preference values passed from the dashboard (if any exist)
|
||||
*/
|
||||
private function initializePrefs() {
|
||||
if ($this->stationName) {
|
||||
Application_Model_Preference::SetStationName($this->stationName);
|
||||
}
|
||||
if ($this->description) {
|
||||
Application_Model_Preference::SetStationDescription($this->description);
|
||||
}
|
||||
if (isset($this->defaultIcecastPassword)) {
|
||||
Application_Model_Preference::setDefaultIcecastPassword($this->defaultIcecastPassword);
|
||||
}
|
||||
if (isset($this->bandwidthLimit)) {
|
||||
Application_Model_Preference::setBandwidthLimit($this->bandwidthLimit);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DatabaseAlreadyExistsException extends Exception {
|
||||
private static $_defaultMessage = "ERROR: airtime tables already exists";
|
||||
public function __construct($message = null, $code = 0, Exception $previous = null) {
|
||||
$message = _((is_null($message) ? self::$_defaultMessage : $message));
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseDoesNotExistException extends Exception {}
|
||||
|
|
@ -103,13 +103,6 @@ class Application_Common_UsabilityHints
|
|||
"<a href=\"/schedule\">",
|
||||
"</a>");
|
||||
}
|
||||
} else if (LIBRETIME_ENABLE_BILLING === true && $userIsOnShowbuilderPage && $userIsSuperAdmin) {
|
||||
$unpaidInvoice = Billing::checkForUnpaidInvoice();
|
||||
if ($unpaidInvoice != null) {
|
||||
$invoiceUrl = "/billing/invoice?invoiceid=" . $unpaidInvoice['id'];
|
||||
$amount = $unpaidInvoice['currencyprefix'] . $unpaidInvoice['total'];
|
||||
return _pro(sprintf("You have an unpaid invoice for %s due soon. <a href='%s'>Please pay it to keep your station on the air.</a>", $amount, $invoiceUrl));;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -225,4 +218,4 @@ class Application_Common_UsabilityHints
|
|||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue