diff --git a/airtime_mvc/application/common/CORSHelper.php b/airtime_mvc/application/common/CORSHelper.php index 0375a702a..86dc00c29 100644 --- a/airtime_mvc/application/common/CORSHelper.php +++ b/airtime_mvc/application/common/CORSHelper.php @@ -3,12 +3,9 @@ class CORSHelper { - public static function enableATProCrossOriginRequests(&$request, &$response) + public static function enableCrossOriginRequests(&$request, &$response) { - //Allow AJAX requests from www.airtime.pro. We use this to automatically login users - //after they sign up from the microsite. //Chrome sends the Origin header for all requests, so we whitelist the webserver's hostname as well. - $response = $response->setHeader('Access-Control-Allow-Origin', '*'); $origin = $request->getHeader('Origin'); if ((!(preg_match("/https?:\/\/localhost/", $origin) === 1)) && ($origin != "") && (!in_array($origin, self::getAllowedOrigins()))) @@ -16,15 +13,20 @@ class CORSHelper //Don't allow CORS from other domains to prevent XSS. throw new Zend_Controller_Action_Exception('Forbidden', 403); } + //Allow AJAX requests from configured websites. We use this to allow other pages to use LibreTimes API. + if ($origin) { + $response = $response->setHeader('Access-Control-Allow-Origin', $origin); + } } public static function getAllowedOrigins() { - return array("http://www.airtime.pro", - "https://www.airtime.pro", - "https://account.sourcefabric.com", - "https://account.sourcefabric.com:5001", + $allowedCorsUrls = array_map( + function($v) { return trim($v); }, + explode(PHP_EOL, Application_Model_Preference::GetAllowedCorsUrls()) + ); + return array_merge($allowedCorsUrls, array( "http://" . $_SERVER['SERVER_NAME'], - "https://" . $_SERVER['SERVER_NAME']); + "https://" . $_SERVER['SERVER_NAME'])); } } diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index e5545fc5d..d7803fed3 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -209,7 +209,7 @@ class ApiController extends Zend_Controller_Action $result["station_down"] = true; } - echo isset($_GET['callback']) ? $_GET['callback'].'('.json_encode($result).')' : json_encode($result); + $this->returnJsonOrJsonp($request, $result); } /** @@ -292,18 +292,11 @@ class ApiController extends Zend_Controller_Action // convert image paths to point to api endpoints WidgetHelper::findAndConvertPaths($result); - + // used by caller to determine if the airtime they are running or widgets in use is out of date. $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION; - header("Content-Type: application/json"); - - if (version_compare(phpversion(), '5.4.0', '<')) { - $js = json_encode($result); - } else { - $js = json_encode($result, JSON_PRETTY_PRINT); - } - // If a callback is not given, then just provide the raw JSON. - echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js; + + $this->returnJsonOrJsonp($request, $result); } else { header('HTTP/1.0 401 Unauthorized'); print _('You are not allowed to access this resource. '); @@ -368,15 +361,8 @@ class ApiController extends Zend_Controller_Action // used by caller to determine if the airtime they are running or widgets in use is out of date. $result["station"]["AIRTIME_API_VERSION"] = AIRTIME_API_VERSION; - header("Content-Type: application/json"); - - if (version_compare(phpversion(), '5.4.0', '<')) { - $js = json_encode($result); - } else { - $js = json_encode($result, JSON_PRETTY_PRINT); - } - // If a callback is not given, then just provide the raw JSON. - echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js; + + $this->returnJsonOrJsonp($request, $result); } else { header('HTTP/1.0 401 Unauthorized'); print _('You are not allowed to access this resource. '); @@ -443,15 +429,7 @@ class ApiController extends Zend_Controller_Action //used by caller to determine if the airtime they are running or widgets in use is out of date. $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION; - header("Content-type: text/javascript"); - - if (version_compare(phpversion(), '5.4.0', '<')) { - $js = json_encode($result); - } else { - $js = json_encode($result, JSON_PRETTY_PRINT); - } - // If a callback is not given, then just provide the raw JSON. - echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js; + $this->returnJsonOrJsonp($request, $result); } else { header('HTTP/1.0 401 Unauthorized'); print _('You are not allowed to access this resource. '); @@ -534,15 +512,8 @@ class ApiController extends Zend_Controller_Action // used by caller to determine if the airtime they are running or widgets in use is out of date. $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION; - header("Content-type: text/javascript"); - - if (version_compare(phpversion(), '5.4.0', '<')) { - $js = json_encode($result); - } else { - $js = json_encode($result, JSON_PRETTY_PRINT); - } - // If a callback is not given, then just provide the raw JSON. - echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js; + + $this->returnJsonOrJsonp($request, $result); } else { header('HTTP/1.0 401 Unauthorized'); print _('You are not allowed to access this resource. '); @@ -1599,4 +1570,21 @@ class ApiController extends Zend_Controller_Action } echo("Recalculated $total shows."); } + + private final function returnJsonOrJsonp($request, $result) { + $callback = $request->getParam('callback'); + $response = $this->getResponse(); + $response->setHeader('Content-Type', 'application/json'); + + $body = $this->_helper->json->encodeJson($result, false); + + if ($callback) { + $response->setHeader('Content-Type', 'application/javascript'); + $body = sprintf('%s(%s)', $callback, $body); + } + $response->setBody($body); + + // enable cors access from configured URLs + CORSHelper::enableCrossOriginRequests($request, $response); + } } diff --git a/airtime_mvc/application/controllers/LoginController.php b/airtime_mvc/application/controllers/LoginController.php index 6912f07c6..861cc6d35 100644 --- a/airtime_mvc/application/controllers/LoginController.php +++ b/airtime_mvc/application/controllers/LoginController.php @@ -24,7 +24,7 @@ class LoginController extends Zend_Controller_Action $stationLocale = Application_Model_Preference::GetDefaultLocale(); //Enable AJAX requests from www.airtime.pro for the sign-in process. - CORSHelper::enableATProCrossOriginRequests($request, $response); + CORSHelper::enableCrossOriginRequests($request, $response); Application_Model_Locale::configureLocalization($request->getcookie('airtime_locale', $stationLocale)); diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 5d00bd1a8..29e419965 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -46,6 +46,7 @@ class PreferenceController extends Zend_Controller_Action Application_Model_Preference::SetDefaultFadeIn($values["stationDefaultFadeIn"]); Application_Model_Preference::SetDefaultFadeOut($values["stationDefaultFadeOut"]); Application_Model_Preference::SetAllow3rdPartyApi($values["thirdPartyApi"]); + Application_Model_Preference::SetAllowedCorsUrls($values["allowedCorsUrls"]); Application_Model_Preference::SetDefaultLocale($values["locale"]); Application_Model_Preference::SetDefaultTimezone($values["timezone"]); Application_Model_Preference::SetWeekStartDay($values["weekStartDay"]); diff --git a/airtime_mvc/application/forms/GeneralPreferences.php b/airtime_mvc/application/forms/GeneralPreferences.php index f804ad7f3..a87b7fa12 100644 --- a/airtime_mvc/application/forms/GeneralPreferences.php +++ b/airtime_mvc/application/forms/GeneralPreferences.php @@ -118,6 +118,13 @@ class Application_Form_GeneralPreferences extends Zend_Form_SubForm )); $this->addElement($third_party_api); + $allowedCorsUrlsValue = Application_Model_Preference::GetAllowedCorsUrls(); + $allowedCorsUrls = new Zend_Form_Element_Textarea('allowedCorsUrls'); + $allowedCorsUrls->setLabel(_('Allowed CORS URLs')); + $allowedCorsUrls->setDescription(_('Remote URLs that are allowed to access this LibreTime instance in a browser. One URL per line.')); + $allowedCorsUrls->setValue($allowedCorsUrlsValue); + $this->addElement($allowedCorsUrls); + $locale = new Zend_Form_Element_Select("locale"); $locale->setLabel(_("Default Language")); $locale->setMultiOptions(Application_Model_Locale::getLocales()); diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 3789f51de..9f5225845 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1645,4 +1645,23 @@ class Application_Model_Preference public static function setBandwidthLimitUpdateTimer() { self::setValue("bandwidth_limit_update_timer", microtime(true)); } + + /** + * Getter for CORS URLs + * + * @return string + */ + public static function GetAllowedCorsUrls() { + return self::getValue('allowed_cors_urls'); + } + + /** + * Setter for CORS URLs + * + * @param string $value + * @return void + */ + public static function SetAllowedCorsUrls($value) { + self::setValue('allowed_cors_urls', $value); + } } diff --git a/airtime_mvc/application/views/scripts/form/preferences_general.phtml b/airtime_mvc/application/views/scripts/form/preferences_general.phtml index 205d6d65a..dbae3e2da 100644 --- a/airtime_mvc/application/views/scripts/form/preferences_general.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences_general.phtml @@ -32,6 +32,7 @@ element->getElement('stationDefaultCrossfadeDuration')->render() ?> element->getElement('thirdPartyApi')->render() ?> + element->getElement('allowedCorsUrls')->render() ?> element->getElement('radioPageLoginButton')->renderViewHelper() ?> element->getElement('radioPageLoginButton')->renderLabel() ?>