diff --git a/adm/config_form.php b/adm/config_form.php index 7c2c57ec8..46aaaa244 100644 --- a/adm/config_form.php +++ b/adm/config_form.php @@ -192,6 +192,49 @@ if(!sql_query(" select vi_browser from {$g5['visit_table']} limit 1 ")) { ADD `vi_device` varchar(255) NOT NULL DEFAULT '' AFTER `vi_os` ", true); } +//소셜 로그인 관련 필드 및 구글 리챕챠 필드 추가 +if(!isset($config['cf_social_login_use'])) { + sql_query("ALTER TABLE `{$g5['config_table']}` + ADD `cf_social_login_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `cf_googl_shorturl_apikey`, + ADD `cf_google_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_twitter_secret`, + ADD `cf_google_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_google_clientid`, + ADD `cf_naver_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_google_secret`, + ADD `cf_naver_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_naver_clientid`, + ADD `cf_kakao_rest_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_naver_secret`, + ADD `cf_social_servicelist` varchar(255) NOT NULL DEFAULT '' AFTER `cf_social_login_use`, + ADD `cf_payco_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_social_servicelist`, + ADD `cf_payco_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_payco_clientid`, + ADD `cf_captcha` varchar(100) NOT NULL DEFAULT '' AFTER `cf_kakao_js_apikey`, + ADD `cf_recaptcha_site_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_captcha`, + ADD `cf_recaptcha_secret_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_recaptcha_site_key` + ", true); +} + +// 소셜 로그인 관리 테이블 없을 경우 생성 +if(!sql_query(" DESC {$g5['social_profile_table']} ", false)) { + sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['social_profile_table']}` ( + `mp_no` int(11) NOT NULL AUTO_INCREMENT, + `mb_id` varchar(255) NOT NULL DEFAULT '', + `provider` varchar(50) NOT NULL DEFAULT '', + `object_sha` varchar(45) NOT NULL DEFAULT '', + `identifier` varchar(255) NOT NULL DEFAULT '', + `profileurl` varchar(255) NOT NULL DEFAULT '', + `photourl` varchar(255) NOT NULL DEFAULT '', + `displayname` varchar(150) NOT NULL DEFAULT '', + `description` varchar(255) NOT NULL DEFAULT '', + `gender` varchar(10) NOT NULL DEFAULT '', + `age` varchar(10) NOT NULL DEFAULT '', + `birthday` int(11) NOT NULL DEFAULT '0', + `birthmonth` int(11) NOT NULL DEFAULT '0', + `birthyear` int(11) NOT NULL DEFAULT '0', + `mp_latest_day` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + UNIQUE KEY `mp_no` (`mp_no`), + KEY `mb_id` (`mb_id`), + KEY `provider` (`provider`) + ) ", true); +} + + if(!$config['cf_faq_skin']) $config['cf_faq_skin'] = "basic"; if(!$config['cf_mobile_faq_skin']) $config['cf_mobile_faq_skin'] = "basic"; @@ -403,12 +446,22 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
+ > + +
/?hauth.done=Naver
++ > + +
+ + +
++ > + +
/?hauth.done=Facebook
++ > + +
/?hauth.done=Google
++ > + +
++ > + +
+| - - - | -게시판 | -
|---|---|
| - - - | -- - | -
';
+ $html .= '';
if (!is_mobile()) $html .= "\n".'';
$html .= "\n".'';
- $html .= '';
$html .= "\n".'자동등록방지 숫자를 순서대로 입력하세요.';
$html .= "\n".'';
return $html;
diff --git a/plugin/recaptcha/_common.php b/plugin/recaptcha/_common.php
new file mode 100644
index 000000000..14553420b
--- /dev/null
+++ b/plugin/recaptcha/_common.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/plugin/recaptcha/captcha.lib.php b/plugin/recaptcha/captcha.lib.php
new file mode 100644
index 000000000..e67ee596c
--- /dev/null
+++ b/plugin/recaptcha/captcha.lib.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/plugin/recaptcha/recaptcha.class.php b/plugin/recaptcha/recaptcha.class.php
new file mode 100644
index 000000000..781c13d3c
--- /dev/null
+++ b/plugin/recaptcha/recaptcha.class.php
@@ -0,0 +1,147 @@
+secret = $secret;
+ }
+
+ public function get_content($url, $data=array()) {
+
+ $curlsession = curl_init();
+ curl_setopt ($curlsession, CURLOPT_URL, $url);
+ curl_setopt ($curlsession, CURLOPT_POST, 1);
+ curl_setopt ($curlsession, CURLOPT_POSTFIELDS, http_build_query($data, '', '&'));
+ curl_setopt ($curlsession, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
+ curl_setopt ($curlsession, CURLINFO_HEADER_OUT, false);
+ curl_setopt ($curlsession, CURLOPT_HEADER, false);
+ curl_setopt ($curlsession, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt ($curlsession, CURLOPT_SSL_VERIFYPEER, 1);
+ curl_setopt ($curlsession, CURLOPT_TIMEOUT, 3);
+
+ $response = curl_exec($curlsession);
+ $cinfo = curl_getinfo($curlsession);
+ curl_close($curlsession);
+
+ if ($cinfo['http_code'] != 200){
+ return '';
+ }
+ return $response;
+ }
+
+ /**
+ * Submits an HTTP GET to a reCAPTCHA server.
+ *
+ * @param string $path url path to recaptcha server.
+ * @param array $data array of parameters to be sent.
+ *
+ * @return array response
+ */
+ private function submit($url, $data)
+ {
+ $response = $this->get_content($url, $data);
+ return $response;
+ }
+
+ /**
+ * Calls the reCAPTCHA siteverify API to verify whether the user passes
+ * CAPTCHA test.
+ *
+ * @param string $remoteIp IP address of end user.
+ * @param string $response response string from recaptcha verification.
+ *
+ * @return ReCaptchaResponse_v
+ */
+ public function verify($response, $remoteIp = null)
+ {
+ // Discard empty solution submissions
+ if ($response == null || strlen($response) == 0) {
+ $recaptchaResponse = new ReCaptchaResponse_v();
+ $recaptchaResponse->success = false;
+ $recaptchaResponse->errorCodes = 'missing-input';
+ return $recaptchaResponse;
+ }
+ $getResponse = $this->submit(
+ self::$_siteVerifyUrl,
+ array (
+ 'secret' => $this->secret,
+ 'remoteip' => $remoteIp,
+ 'version' => self::VERSION,
+ 'response' => $response
+ )
+ );
+ $answers = $getResponse ? json_decode($getResponse, true) : array();
+ $recaptchaResponse = new ReCaptchaResponse_v();
+ if (isset($answers['success']) && $answers['success'] == true) {
+ $recaptchaResponse->success = true;
+ } else {
+ $recaptchaResponse->success = false;
+ $recaptchaResponse->errorCodes = isset($answers['error-codes']) ? $answers['error-codes'] : 'http_error';
+ }
+ return $recaptchaResponse;
+ }
+}
+?>
\ No newline at end of file
diff --git a/plugin/recaptcha/recaptcha.js b/plugin/recaptcha/recaptcha.js
new file mode 100644
index 000000000..76dfc32b5
--- /dev/null
+++ b/plugin/recaptcha/recaptcha.js
@@ -0,0 +1,9 @@
+function chk_captcha()
+{
+ if ( ! jQuery('#g-recaptcha-response').val()) {
+ alert("자동등록방지를 반드시 체크해 주세요.");
+ return false;
+ }
+
+ return true;
+}
\ No newline at end of file
diff --git a/plugin/recaptcha/recaptcha.user.lib.php b/plugin/recaptcha/recaptcha.user.lib.php
new file mode 100644
index 000000000..8a5c631ba
--- /dev/null
+++ b/plugin/recaptcha/recaptcha.user.lib.php
@@ -0,0 +1,51 @@
+';
+ $html .= '';
+ $html .= '';
+ $html .= '';
+ $html .= '';
+
+ return $html;
+}
+
+// 캡챠 사용시 자바스크립트에서 입력된 캡챠를 검사함
+function chk_captcha_js()
+{
+ return "if (!chk_captcha()) return false;\n";
+}
+
+function chk_captcha(){
+
+ global $config;
+
+ $resp = null;
+
+ if ( isset($_POST["g-recaptcha-response"]) && !empty($_POST["g-recaptcha-response"]) ) {
+
+ $reCaptcha = new ReCaptcha_GNU( $config['cf_recaptcha_secret_key'] );
+
+ $resp = $reCaptcha->verify($_POST["g-recaptcha-response"], $_SERVER["REMOTE_ADDR"]);
+ }
+
+ if( ! $resp ){
+ return false;
+ }
+
+ if ($resp != null && $resp->success) {
+ return true;
+ }
+
+ return false;
+}
+?>
\ No newline at end of file
diff --git a/plugin/sns/icon/facebook.png b/plugin/sns/icon/facebook.png
index b9d733963..cf737d633 100644
Binary files a/plugin/sns/icon/facebook.png and b/plugin/sns/icon/facebook.png differ
diff --git a/plugin/sns/icon/gplus.png b/plugin/sns/icon/gplus.png
index 993cce991..8ab2fa6bc 100644
Binary files a/plugin/sns/icon/gplus.png and b/plugin/sns/icon/gplus.png differ
diff --git a/plugin/sns/icon/kakaotalk.png b/plugin/sns/icon/kakaotalk.png
index 78fe26ca7..07de029ef 100644
Binary files a/plugin/sns/icon/kakaotalk.png and b/plugin/sns/icon/kakaotalk.png differ
diff --git a/plugin/sns/icon/twitter.png b/plugin/sns/icon/twitter.png
index 9962ded1e..8fbf0914b 100644
Binary files a/plugin/sns/icon/twitter.png and b/plugin/sns/icon/twitter.png differ
diff --git a/plugin/sns/view.sns.skin.php b/plugin/sns/view.sns.skin.php
index aa1cc0ba4..a798cfd0e 100644
--- a/plugin/sns/view.sns.skin.php
+++ b/plugin/sns/view.sns.skin.php
@@ -30,12 +30,15 @@ $gplus_url = $sns_send.'&sns=gplus';
Kakao.init("");
+
diff --git a/plugin/sns/view_comment_write.sns.skin.php b/plugin/sns/view_comment_write.sns.skin.php
index 75584ba35..d58b3d08b 100644
--- a/plugin/sns/view_comment_write.sns.skin.php
+++ b/plugin/sns/view_comment_write.sns.skin.php
@@ -30,17 +30,16 @@ if ($config['cf_facebook_appid']) {
}
}
- echo '
+ * array(
+ * 'Facebook' => array(
+ * 'connected' => true
+ * )
+ * )
+ *
+ * @return array
+ */
+ public static function getProviders() {
+ $idps = array();
+
+ foreach (Hybrid_Auth::$config["providers"] as $idpid => $params) {
+ if ($params['enabled']) {
+ $idps[$idpid] = array('connected' => false);
+
+ if (Hybrid_Auth::isConnectedWith($idpid)) {
+ $idps[$idpid]['connected'] = true;
+ }
+ }
+ }
+
+ return $idps;
+ }
+
+ /**
+ * A generic function to logout all connected provider at once
+ * @return void
+ */
+ public static function logoutAllProviders() {
+ $idps = Hybrid_Auth::getConnectedProviders();
+
+ foreach ($idps as $idp) {
+ $adapter = Hybrid_Auth::getAdapter($idp);
+ $adapter->logout();
+ }
+ }
+
+ /**
+ * Utility function, redirect to a given URL with php header or using javascript location.href
+ *
+ * @param string $url URL to redirect to
+ * @param string $mode PHP|JS
+ */
+ public static function redirect($url, $mode = "PHP") {
+ if(!$mode){
+ $mode = 'PHP';
+ }
+ Hybrid_Logger::info("Enter Hybrid_Auth::redirect( $url, $mode )");
+
+ // Ensure session is saved before sending response, see https://github.com/symfony/symfony/pull/12341
+ if ((PHP_VERSION_ID >= 50400 && PHP_SESSION_ACTIVE === session_status()) || (PHP_VERSION_ID < 50400 && isset($_SESSION) && session_id())) {
+ session_write_close();
+ }
+
+ if ($mode == "PHP") {
+ header("Location: $url");
+ } elseif ($mode == "JS") {
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+ echo 'Redirecting, please wait...';
+ echo '';
+ echo '';
+ }
+
+ die();
+ }
+
+ /**
+ * Utility function, return the current url
+ *
+ * @param bool $request_uri true to get $_SERVER['REQUEST_URI'], false for $_SERVER['PHP_SELF']
+ * @return string
+ */
+ public static function getCurrentUrl($request_uri = true) {
+ if (php_sapi_name() == 'cli') {
+ return '';
+ }
+
+ $protocol = 'http://';
+
+ if ((isset($_SERVER['HTTPS']) && ( $_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1 ))
+ || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'))
+ {
+ $protocol = 'https://';
+ }
+
+ $url = $protocol . $_SERVER['HTTP_HOST'];
+
+ if ($request_uri) {
+ $url .= $_SERVER['REQUEST_URI'];
+ } else {
+ $url .= $_SERVER['PHP_SELF'];
+ }
+
+ // return current url
+ return $url;
+ }
+
+}
diff --git a/plugin/social/Hybrid/Endpoint.php b/plugin/social/Hybrid/Endpoint.php
new file mode 100644
index 000000000..7813fee05
--- /dev/null
+++ b/plugin/social/Hybrid/Endpoint.php
@@ -0,0 +1,222 @@
+here we need to parse $_SERVER[QUERY_STRING]
+ $request = $_REQUEST;
+ if (isset($_SERVER["QUERY_STRING"]) && strrpos($_SERVER["QUERY_STRING"], '?')) {
+ $_SERVER["QUERY_STRING"] = str_replace("?", "&", $_SERVER["QUERY_STRING"]);
+ parse_str($_SERVER["QUERY_STRING"], $request);
+ }
+ }
+
+ // Setup request variable
+ $this->request = $request;
+
+ // If openid_policy requested, we return our policy document
+ if (isset($this->request["get"]) && $this->request["get"] == "openid_policy") {
+ $this->processOpenidPolicy();
+ }
+
+ // If openid_xrds requested, we return our XRDS document
+ if (isset($this->request["get"]) && $this->request["get"] == "openid_xrds") {
+ $this->processOpenidXRDS();
+ }
+
+ // If we get a hauth.start
+ if (isset($this->request["hauth_start"]) && $this->request["hauth_start"]) {
+ $this->processAuthStart();
+ }
+ // Else if hauth.done
+ elseif (isset($this->request["hauth_done"]) && $this->request["hauth_done"]) {
+ $this->processAuthDone();
+ }
+ // Else we advertise our XRDS document, something supposed to be done from the Realm URL page
+ else {
+ $this->processOpenidRealm();
+ }
+ }
+
+ /**
+ * Process the current request
+ *
+ * @param array $request The current request parameters. Leave as null to default to use $_REQUEST.
+ * @return Hybrid_Endpoint
+ */
+ public static function process($request = null) {
+ // Trick for PHP 5.2, because it doesn't support late static binding
+ $class = function_exists('get_called_class') ? get_called_class() : __CLASS__;
+ new $class($request);
+ }
+
+ /**
+ * Process OpenID policy request
+ * @return void
+ */
+ protected function processOpenidPolicy() {
+ $output = file_get_contents(dirname(__FILE__) . "/resources/openid_policy.html");
+ print $output;
+ die();
+ }
+
+ /**
+ * Process OpenID XRDS request
+ * @return void
+ */
+ protected function processOpenidXRDS() {
+ header("Content-Type: application/xrds+xml");
+
+ $output = str_replace("{RETURN_TO_URL}", str_replace(
+ array("<", ">", "\"", "'", "&"), array("<", ">", """, "'", "&"), Hybrid_Auth::getCurrentUrl(false)
+ ), file_get_contents(dirname(__FILE__) . "/resources/openid_xrds.xml"));
+ print $output;
+ die();
+ }
+
+ /**
+ * Process OpenID realm request
+ * @return void
+ */
+ protected function processOpenidRealm() {
+ $output = str_replace("{X_XRDS_LOCATION}", htmlentities(Hybrid_Auth::getCurrentUrl(false), ENT_QUOTES, 'UTF-8')
+ . "?get=openid_xrds&v="
+ . Hybrid_Auth::$version, file_get_contents(dirname(__FILE__) . "/resources/openid_realm.html"));
+ print $output;
+ die();
+ }
+
+ /**
+ * Define: endpoint step 3
+ * @return void
+ * @throws Hybrid_Exception
+ */
+ protected function processAuthStart() {
+ $this->authInit();
+
+ $provider_id = trim(strip_tags($this->request["hauth_start"]));
+
+ // check if page accessed directly
+ if (!Hybrid_Auth::storage()->get("hauth_session.$provider_id.hauth_endpoint")) {
+ Hybrid_Logger::error("Endpoint: hauth_endpoint parameter is not defined on hauth_start, halt login process!");
+
+ throw new Hybrid_Exception("You cannot access this page directly.");
+ }
+
+ // define:hybrid.endpoint.php step 2.
+ $hauth = Hybrid_Auth::setup($provider_id);
+
+ // if REQUESTed hauth_idprovider is wrong, session not created, etc.
+ if (!$hauth) {
+ Hybrid_Logger::error("Endpoint: Invalid parameter on hauth_start!");
+ throw new Hybrid_Exception("Invalid parameter! Please return to the login page and try again.");
+ }
+
+ try {
+ Hybrid_Logger::info("Endpoint: call adapter [{$provider_id}] loginBegin()");
+
+ $hauth->adapter->loginBegin();
+ } catch (Exception $e) {
+ Hybrid_Logger::error("Exception:" . $e->getMessage(), $e);
+ Hybrid_Error::setError($e->getMessage(), $e->getCode(), $e->getTraceAsString(), $e->getPrevious());
+
+ $hauth->returnToCallbackUrl();
+ }
+
+ die();
+ }
+
+ /**
+ * Define: endpoint step 3.1 and 3.2
+ * @return void
+ * @throws Hybrid_Exception
+ */
+ protected function processAuthDone() {
+ $this->authInit();
+
+ $provider_id = trim(strip_tags($this->request["hauth_done"]));
+
+ $hauth = Hybrid_Auth::setup($provider_id);
+
+ if (!$hauth) {
+ Hybrid_Logger::error("Endpoint: Invalid parameter on hauth_done!");
+
+ $hauth->adapter->setUserUnconnected();
+
+ throw new Hybrid_Exception("Invalid parameter! Please return to the login page and try again.");
+ }
+
+ try {
+ Hybrid_Logger::info("Endpoint: call adapter [{$provider_id}] loginFinish() ");
+ $hauth->adapter->loginFinish();
+ } catch (Exception $e) {
+ Hybrid_Logger::error("Exception:" . $e->getMessage(), $e);
+ Hybrid_Error::setError($e->getMessage(), $e->getCode(), $e->getTraceAsString(), $e->getPrevious());
+
+ $hauth->adapter->setUserUnconnected();
+ }
+
+ Hybrid_Logger::info("Endpoint: job done. return to callback url.");
+
+ $hauth->returnToCallbackUrl();
+ die();
+ }
+
+ /**
+ * Initializes authentication
+ * @throws Hybrid_Exception
+ */
+ protected function authInit() {
+ if (!$this->initDone) {
+ $this->initDone = true;
+
+ // Init Hybrid_Auth
+ try {
+ if (!class_exists("Hybrid_Storage", false)) {
+ require_once realpath(dirname(__FILE__)) . "/Storage.php";
+ }
+ if (!class_exists("Hybrid_Exception", false)) {
+ require_once realpath(dirname(__FILE__)) . "/Exception.php";
+ }
+ if (!class_exists("Hybrid_Logger", false)) {
+ require_once realpath(dirname(__FILE__)) . "/Logger.php";
+ }
+
+ $storage = new Hybrid_Storage();
+
+ // Check if Hybrid_Auth session already exist
+ if (!$storage->config("CONFIG")) {
+ throw new Hybrid_Exception("You cannot access this page directly.");
+ }
+
+ Hybrid_Auth::initialize($storage->config("CONFIG"));
+ } catch (Exception $e) {
+ Hybrid_Logger::error("Endpoint: Error while trying to init Hybrid_Auth: " . $e->getMessage());
+ throw new Hybrid_Exception( "Endpoint: Error while trying to init Hybrid_Auth: " . $e->getMessage(), $e->getCode(), $e );
+ }
+ }
+ }
+
+}
diff --git a/plugin/social/Hybrid/Error.php b/plugin/social/Hybrid/Error.php
new file mode 100644
index 000000000..e9c319c68
--- /dev/null
+++ b/plugin/social/Hybrid/Error.php
@@ -0,0 +1,88 @@
+set("hauth_session.error.status", 1);
+ Hybrid_Auth::storage()->set("hauth_session.error.message", $message);
+ Hybrid_Auth::storage()->set("hauth_session.error.code", $code);
+ Hybrid_Auth::storage()->set("hauth_session.error.trace", $trace);
+ Hybrid_Auth::storage()->set("hauth_session.error.previous", $previous);
+ }
+
+ /**
+ * Clear the last error
+ * @return void
+ */
+ public static function clearError() {
+ Hybrid_Logger::info("Enter Hybrid_Error::clearError()");
+
+ Hybrid_Auth::storage()->delete("hauth_session.error.status");
+ Hybrid_Auth::storage()->delete("hauth_session.error.message");
+ Hybrid_Auth::storage()->delete("hauth_session.error.code");
+ Hybrid_Auth::storage()->delete("hauth_session.error.trace");
+ Hybrid_Auth::storage()->delete("hauth_session.error.previous");
+ }
+
+ /**
+ * Checks to see if there is a an error.
+ * @return boolean true if there is an error.
+ */
+ public static function hasError() {
+ return (bool) Hybrid_Auth::storage()->get("hauth_session.error.status");
+ }
+
+ /**
+ * Return error message
+ * @return string
+ */
+ public static function getErrorMessage() {
+ return Hybrid_Auth::storage()->get("hauth_session.error.message");
+ }
+
+ /**
+ * Return error code
+ * @return int
+ */
+ public static function getErrorCode() {
+ return Hybrid_Auth::storage()->get("hauth_session.error.code");
+ }
+
+ /**
+ * Return string detailed error backtrace as string
+ * @return string
+ */
+ public static function getErrorTrace() {
+ return Hybrid_Auth::storage()->get("hauth_session.error.trace");
+ }
+
+ /**
+ * Detailed error backtrace as string
+ * @return string
+ */
+ public static function getErrorPrevious() {
+ return Hybrid_Auth::storage()->get("hauth_session.error.previous");
+ }
+
+}
diff --git a/plugin/social/Hybrid/Exception.php b/plugin/social/Hybrid/Exception.php
new file mode 100644
index 000000000..8c8c2d1fa
--- /dev/null
+++ b/plugin/social/Hybrid/Exception.php
@@ -0,0 +1,17 @@
+format(DATE_ATOM),
+ $message,
+ print_r($object, true) . PHP_EOL,
+ )), FILE_APPEND
+ );
+ }
+ }
+
+ /**
+ * Logs an info message
+ *
+ * @param string $message Info message
+ * @return void
+ */
+ public static function info($message) {
+ if (in_array(Hybrid_Auth::$config["debug_mode"], array(true, 'info'), true)) {
+ $dt = new DateTime('now', new DateTimeZone( 'UTC' ));
+ file_put_contents(Hybrid_Auth::$config["debug_file"], implode(' -- ', array(
+ "INFO",
+ $_SERVER['REMOTE_ADDR'],
+ $dt->format(DATE_ATOM),
+ $message . PHP_EOL,
+ )), FILE_APPEND);
+ }
+ }
+
+ /**
+ * Logs an error message with an object dump
+ *
+ * @param string $message Error message
+ * @param stdClass $object Object being debugged
+ * @return void
+ */
+ public static function error($message, $object = null) {
+ if (isset(Hybrid_Auth::$config["debug_mode"]) && in_array(Hybrid_Auth::$config["debug_mode"], array(true, 'info', 'error'), true)) {
+ $dt = new DateTime('now', new DateTimeZone( 'UTC' ));
+ file_put_contents(Hybrid_Auth::$config["debug_file"], implode(' -- ', array(
+ 'ERROR',
+ $_SERVER['REMOTE_ADDR'],
+ $dt->format(DATE_ATOM),
+ $message,
+ print_r($object, true) . PHP_EOL
+ )), FILE_APPEND);
+ }
+ }
+
+ /**
+ * Dumps the data in the way suitable to be output in log files for debug purposes
+ *
+ * @param mixed $data
+ *
+ * @return string
+ */
+ public static function dumpData($data) {
+ return var_export($data, true);
+ }
+
+}
diff --git a/plugin/social/Hybrid/Provider_Adapter.php b/plugin/social/Hybrid/Provider_Adapter.php
new file mode 100644
index 000000000..aeeb30bb2
--- /dev/null
+++ b/plugin/social/Hybrid/Provider_Adapter.php
@@ -0,0 +1,340 @@
+id = $id;
+ $this->params = $params;
+ $this->id = $this->getProviderCiId($this->id);
+ $this->config = $this->getConfigById($this->id);
+
+ # check the IDp id
+ if (!$this->id) {
+ throw new Exception("No provider ID specified.", 2);
+ }
+
+ # check the IDp config
+ if (!$this->config) {
+ throw new Exception("Unknown Provider ID, check your configuration file.", 3);
+ }
+
+ # check the IDp adapter is enabled
+ if (!$this->config["enabled"]) {
+ throw new Exception("The provider '{$this->id}' is not enabled.", 3);
+ }
+
+ # include the adapter wrapper
+ if (isset($this->config["wrapper"]) && is_array($this->config["wrapper"])) {
+ if (isset($this->config["wrapper"]["path"])) {
+ require_once $this->config["wrapper"]["path"];
+ }
+
+ if (!class_exists($this->config["wrapper"]["class"])) {
+ throw new Exception("Unable to load the adapter class.", 3);
+ }
+
+ $this->wrapper = $this->config["wrapper"]["class"];
+ } else {
+ require_once Hybrid_Auth::$config["path_providers"] . $this->id . ".php";
+
+ $this->wrapper = "Hybrid_Providers_" . $this->id;
+ }
+
+ # create the adapter instance, and pass the current params and config
+ $this->adapter = new $this->wrapper($this->id, $this->config, $this->params);
+
+ return $this;
+ }
+
+ /**
+ * Hybrid_Provider_Adapter::login(), prepare the user session and the authentication request
+ * for index.php
+ * @return void
+ * @throw Exception
+ */
+ function login() {
+ Hybrid_Logger::info("Enter Hybrid_Provider_Adapter::login( {$this->id} ) ");
+
+ if (!$this->adapter) {
+ throw new Exception("Hybrid_Provider_Adapter::login() should not directly used.");
+ }
+
+ // clear all unneeded params
+ foreach (Hybrid_Auth::$config["providers"] as $idpid => $params) {
+ Hybrid_Auth::storage()->delete("hauth_session.{$idpid}.hauth_return_to");
+ Hybrid_Auth::storage()->delete("hauth_session.{$idpid}.hauth_endpoint");
+ Hybrid_Auth::storage()->delete("hauth_session.{$idpid}.id_provider_params");
+ }
+
+ // make a fresh start
+ $this->logout();
+
+ # get hybridauth base url
+ if (empty(Hybrid_Auth::$config["base_url"])) {
+ // the base url wasn't provide, so we must use the current
+ // url (which makes sense actually)
+ $url = empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off' ? 'http' : 'https';
+ $url .= '://' . $_SERVER['HTTP_HOST'];
+ $url .= $_SERVER['REQUEST_URI'];
+ $HYBRID_AUTH_URL_BASE = $url;
+ } else {
+ $HYBRID_AUTH_URL_BASE = Hybrid_Auth::$config["base_url"];
+ }
+
+ // make sure params is array
+ if (!is_array($this->params)) {
+ $this->params = array();
+ }
+
+ # we make use of session_id() as storage hash to identify the current user
+ # using session_regenerate_id() will be a problem, but ..
+ $this->params["hauth_token"] = session_id();
+
+ # set request timestamp
+ $this->params["hauth_time"] = time();
+
+ # for default HybridAuth endpoint url hauth_login_start_url
+ # auth.start required the IDp ID
+ # auth.time optional login request timestamp
+ if (!isset($this->params["login_start"]) ) {
+ $this->params["login_start"] = $HYBRID_AUTH_URL_BASE . ( strpos($HYBRID_AUTH_URL_BASE, '?') ? '&' : '?' ) . "hauth.start={$this->id}&hauth.time={$this->params["hauth_time"]}";
+ }
+
+ # for default HybridAuth endpoint url hauth_login_done_url
+ # auth.done required the IDp ID
+ if (!isset($this->params["login_done"]) ) {
+ $this->params["login_done"] = $HYBRID_AUTH_URL_BASE . ( strpos($HYBRID_AUTH_URL_BASE, '?') ? '&' : '?' ) . "hauth.done={$this->id}";
+ }
+
+ # workaround to solve windows live authentication since microsoft disallowed redirect urls to contain any parameters
+ # http://mywebsite.com/path_to_hybridauth/?hauth.done=Live will not work
+ if ($this->id=="Live") {
+ $this->params["login_done"] = $HYBRID_AUTH_URL_BASE."live.php";
+ }
+
+ # Workaround to fix broken callback urls for the Facebook OAuth client
+ if ($this->adapter->useSafeUrls) {
+ $this->params['login_done'] = str_replace('hauth.done', 'hauth_done', $this->params['login_done']);
+ }
+
+ if (isset($this->params["hauth_return_to"])) {
+ Hybrid_Auth::storage()->set("hauth_session.{$this->id}.hauth_return_to", $this->params["hauth_return_to"]);
+ }
+ if (isset($this->params["login_done"])) {
+ Hybrid_Auth::storage()->set("hauth_session.{$this->id}.hauth_endpoint", $this->params["login_done"]);
+ }
+ Hybrid_Auth::storage()->set("hauth_session.{$this->id}.id_provider_params", $this->params);
+
+ // store config to be used by the end point
+ Hybrid_Auth::storage()->config("CONFIG", Hybrid_Auth::$config);
+
+ // move on
+ Hybrid_Logger::debug("Hybrid_Provider_Adapter::login( {$this->id} ), redirect the user to login_start URL.");
+
+ // redirect
+ if (empty($this->params["redirect_mode"])) {
+ Hybrid_Auth::redirect($this->params["login_start"]);
+ } else {
+ Hybrid_Auth::redirect($this->params["login_start"],$this->params["redirect_mode"]);
+ }
+ }
+
+ /**
+ * Let hybridauth forget all about the user for the current provider
+ * @return bool
+ */
+ function logout() {
+ $this->adapter->logout();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Return true if the user is connected to the current provider
+ * @return bool
+ */
+ public function isUserConnected() {
+ return $this->adapter->isUserConnected();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Call adapter methods defined in the adapter model:
+ * getUserProfile()
+ * getUserContacts()
+ * getUserActivity()
+ * setUserStatus()
+ *
+ * @param string $name Method name
+ * @param array $arguments Call arguments
+ * @return mixed
+ * @throws Exception
+ */
+ public function __call($name, $arguments) {
+ Hybrid_Logger::info("Enter Hybrid_Provider_Adapter::$name(), Provider: {$this->id}");
+
+ if (!$this->isUserConnected()) {
+ throw new Exception("User not connected to the provider {$this->id}.", 7);
+ }
+
+ if (!method_exists($this->adapter, $name)) {
+ throw new Exception("Call to undefined function Hybrid_Providers_{$this->id}::$name().");
+ }
+
+ return call_user_func_array(array($this->adapter, $name), $arguments);
+ }
+
+ /**
+ * If the user is connected, then return the access_token and access_token_secret
+ * if the provider api use oauth
+ *
+ *
+ * array(
+ * 'access_token' => '',
+ * 'access_token_secret' => '',
+ * 'refresh_token' => '',
+ * 'expires_in' => '',
+ * 'expires_at' => '',
+ * )
+ *
+ * @return array
+ */
+ public function getAccessToken() {
+ if (!$this->adapter->isUserConnected()) {
+ Hybrid_Logger::error("User not connected to the provider.");
+ throw new Exception("User not connected to the provider.", 7);
+ }
+
+ return array(
+ "access_token" => $this->adapter->token("access_token"), // OAuth access token
+ "access_token_secret" => $this->adapter->token("access_token_secret"), // OAuth access token secret
+ "refresh_token" => $this->adapter->token("refresh_token"), // OAuth refresh token
+ "expires_in" => $this->adapter->token("expires_in"), // OPTIONAL. The duration in seconds of the access token lifetime
+ "expires_at" => $this->adapter->token("expires_at"), // OPTIONAL. Timestamp when the access_token expire. if not provided by the social api, then it should be calculated: expires_at = now + expires_in
+ );
+ }
+
+ /**
+ * Naive getter of the current connected IDp API client
+ * @return stdClass
+ * @throws Exception
+ */
+ function api() {
+ if (!$this->adapter->isUserConnected()) {
+ Hybrid_Logger::error("User not connected to the provider.");
+
+ throw new Exception("User not connected to the provider.", 7);
+ }
+ return $this->adapter->api;
+ }
+
+ /**
+ * Redirect the user to hauth_return_to (the callback url)
+ * @return void
+ */
+ function returnToCallbackUrl() {
+ // get the stored callback url
+ $callback_url = Hybrid_Auth::storage()->get("hauth_session.{$this->id}.hauth_return_to");
+
+ // if the user presses the back button in the browser and we already deleted the hauth_return_to from
+ // the session in the previous request, we will redirect to '/' instead of displaying a blank page.
+ if (!$callback_url) {
+ $callback_url = '/';
+ }
+
+ // remove some unneeded stored data
+ Hybrid_Auth::storage()->delete("hauth_session.{$this->id}.hauth_return_to");
+ Hybrid_Auth::storage()->delete("hauth_session.{$this->id}.hauth_endpoint");
+ Hybrid_Auth::storage()->delete("hauth_session.{$this->id}.id_provider_params");
+
+ // back to home
+ Hybrid_Auth::redirect($callback_url);
+ }
+
+ /**
+ * Return the provider config by id
+ *
+ * @param string $id Config key
+ * @return mixed
+ */
+ function getConfigById($id) {
+ if (isset(Hybrid_Auth::$config["providers"][$id])) {
+ return Hybrid_Auth::$config["providers"][$id];
+ }
+ return null;
+ }
+
+ /**
+ * Return the provider config by id; case insensitive
+ *
+ * @param string $id Provider id
+ * @return mixed
+ */
+ function getProviderCiId($id) {
+ foreach (Hybrid_Auth::$config["providers"] as $idpid => $params) {
+ if (strtolower($idpid) == strtolower($id)) {
+ return $idpid;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/plugin/social/Hybrid/Provider_Model.php b/plugin/social/Hybrid/Provider_Model.php
new file mode 100644
index 000000000..320cee9e1
--- /dev/null
+++ b/plugin/social/Hybrid/Provider_Model.php
@@ -0,0 +1,247 @@
+params = Hybrid_Auth::storage()->get("hauth_session.$providerId.id_provider_params");
+ } else {
+ $this->params = $params;
+ }
+
+ // idp id
+ $this->providerId = $providerId;
+
+ // set HybridAuth endpoint for this provider
+ $this->endpoint = Hybrid_Auth::storage()->get("hauth_session.$providerId.hauth_endpoint");
+
+ // idp config
+ $this->config = $config;
+
+ // new user instance
+ $this->user = new Hybrid_User();
+ $this->user->providerId = $providerId;
+
+ // initialize the current provider adapter
+ $this->initialize();
+
+ Hybrid_Logger::debug("Hybrid_Provider_Model::__construct( $providerId ) initialized. dump current adapter instance: ", serialize($this));
+ }
+
+ /**
+ * IDp wrappers initializer
+ *
+ * The main job of wrappers initializer is to performs (depend on the IDp api client it self):
+ * - include some libs needed by this provider,
+ * - check IDp key and secret,
+ * - set some needed parameters (stored in $this->params) by this IDp api client
+ * - create and setup an instance of the IDp api client on $this->api
+ *
+ * @return void
+ * @throws Exception
+ */
+ abstract protected function initialize();
+
+ /**
+ * Begin login
+ *
+ * @return void
+ * @throws Exception
+ */
+ abstract public function loginBegin();
+
+ /**
+ * Finish login
+ * @return void
+ * @throws Exception
+ */
+ abstract public function loginFinish();
+
+ /**
+ * Generic logout, just erase current provider adapter stored data to let Hybrid_Auth all forget about it
+ * @return bool
+ */
+ function logout() {
+ Hybrid_Logger::info("Enter [{$this->providerId}]::logout()");
+ $this->clearTokens();
+ return true;
+ }
+
+ /**
+ * Grab the user profile from the IDp api client
+ * @return Hybrid_User_Profile
+ * @throws Exception
+ */
+ function getUserProfile() {
+ Hybrid_Logger::error("HybridAuth do not provide users contacts list for {$this->providerId} yet.");
+ throw new Exception("Provider does not support this feature.", 8);
+ }
+
+ /**
+ * Load the current logged in user contacts list from the IDp api client
+ * @return Hybrid_User_Contact[]
+ * @throws Exception
+ */
+ function getUserContacts() {
+ Hybrid_Logger::error("HybridAuth do not provide users contacts list for {$this->providerId} yet.");
+ throw new Exception("Provider does not support this feature.", 8);
+ }
+
+ /**
+ * Return the user activity stream
+ * @return Hybrid_User_Activity[]
+ * @throws Exception
+ */
+ function getUserActivity($stream) {
+ Hybrid_Logger::error("HybridAuth do not provide user's activity stream for {$this->providerId} yet.");
+ throw new Exception("Provider does not support this feature.", 8);
+ }
+
+ /**
+ * Set user status
+ * @return mixed Provider response
+ * @throws Exception
+ */
+ function setUserStatus($status) {
+ Hybrid_Logger::error("HybridAuth do not provide user's activity stream for {$this->providerId} yet.");
+ throw new Exception("Provider does not support this feature.", 8);
+ }
+
+ /**
+ * Return the user status
+ * @return mixed Provider response
+ * @throws Exception
+ */
+ function getUserStatus($statusid) {
+ Hybrid_Logger::error("HybridAuth do not provide user's status for {$this->providerId} yet.");
+ throw new Exception("Provider does not support this feature.", 8);
+ }
+
+ /**
+ * Return true if the user is connected to the current provider
+ * @return bool
+ */
+ public function isUserConnected() {
+ return (bool) Hybrid_Auth::storage()->get("hauth_session.{$this->providerId}.is_logged_in");
+ }
+
+ /**
+ * Set user to connected
+ * @return void
+ */
+ public function setUserConnected() {
+ Hybrid_Logger::info("Enter [{$this->providerId}]::setUserConnected()");
+ Hybrid_Auth::storage()->set("hauth_session.{$this->providerId}.is_logged_in", 1);
+ }
+
+ /**
+ * Set user to unconnected
+ * @return void
+ */
+ public function setUserUnconnected() {
+ Hybrid_Logger::info("Enter [{$this->providerId}]::setUserUnconnected()");
+ Hybrid_Auth::storage()->set("hauth_session.{$this->providerId}.is_logged_in", 0);
+ }
+
+ /**
+ * Get or set a token
+ * @return string
+ */
+ public function token($token, $value = null) {
+ if ($value === null) {
+ return Hybrid_Auth::storage()->get("hauth_session.{$this->providerId}.token.$token");
+ } else {
+ Hybrid_Auth::storage()->set("hauth_session.{$this->providerId}.token.$token", $value);
+ }
+ }
+
+ /**
+ * Delete a stored token
+ * @return void
+ */
+ public function deleteToken($token) {
+ Hybrid_Auth::storage()->delete("hauth_session.{$this->providerId}.token.$token");
+ }
+
+ /**
+ * Clear all existent tokens for this provider
+ * @return void
+ */
+ public function clearTokens() {
+ Hybrid_Auth::storage()->deleteMatch("hauth_session.{$this->providerId}.");
+ }
+
+}
diff --git a/plugin/social/Hybrid/Provider_Model_OAuth1.php b/plugin/social/Hybrid/Provider_Model_OAuth1.php
new file mode 100644
index 000000000..6f5f239ae
--- /dev/null
+++ b/plugin/social/Hybrid/Provider_Model_OAuth1.php
@@ -0,0 +1,174 @@
+ "OK: Success!",
+ 304 => "Not Modified: There was no new data to return.",
+ 400 => "Bad Request: The request was invalid.",
+ 401 => "Unauthorized.",
+ 403 => "Forbidden: The request is understood, but it has been refused.",
+ 404 => "Not Found: The URI requested is invalid or the resource requested does not exists.",
+ 406 => "Not Acceptable.",
+ 500 => "Internal Server Error: Something is broken.",
+ 502 => "Bad Gateway.",
+ 503 => "Service Unavailable."
+ );
+
+ if (!$code && $this->api) {
+ $code = $this->api->http_code;
+ }
+
+ if (isset($http_status_codes[$code])) {
+ return $code . " " . $http_status_codes[$code];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function initialize() {
+ // 1 - check application credentials
+ if (!$this->config["keys"]["key"] || !$this->config["keys"]["secret"]) {
+ throw new Exception("Your application key and secret are required in order to connect to {$this->providerId}.", 4);
+ }
+
+ // 2 - include OAuth lib and client
+ if (! class_exists('OAuthConsumer') ) {
+ require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth.php";
+ }
+ require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth1Client.php";
+
+ // 3.1 - setup access_token if any stored
+ if ($this->token("access_token")) {
+ $this->api = new OAuth1Client(
+ $this->config["keys"]["key"], $this->config["keys"]["secret"], $this->token("access_token"), $this->token("access_token_secret")
+ );
+ }
+
+ // 3.2 - setup request_token if any stored, in order to exchange with an access token
+ elseif ($this->token("request_token")) {
+ $this->api = new OAuth1Client(
+ $this->config["keys"]["key"], $this->config["keys"]["secret"], $this->token("request_token"), $this->token("request_token_secret")
+ );
+ }
+
+ // 3.3 - instanciate OAuth client with client credentials
+ else {
+ $this->api = new OAuth1Client($this->config["keys"]["key"], $this->config["keys"]["secret"]);
+ }
+
+ // Set curl proxy if exist
+ if (isset(Hybrid_Auth::$config["proxy"])) {
+ $this->api->curl_proxy = Hybrid_Auth::$config["proxy"];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginBegin() {
+ $tokens = $this->api->requestToken($this->endpoint);
+
+ // request tokens as received from provider
+ $this->request_tokens_raw = $tokens;
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code), 5);
+ }
+
+ if (!isset($tokens["oauth_token"])) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an invalid oauth token.", 5);
+ }
+
+ $this->token("request_token", $tokens["oauth_token"]);
+ $this->token("request_token_secret", $tokens["oauth_token_secret"]);
+
+ # redirect the user to the provider authentication url
+ Hybrid_Auth::redirect($this->api->authorizeUrl($tokens));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginFinish() {
+ $oauth_token = (array_key_exists('oauth_token', $_REQUEST)) ? $_REQUEST['oauth_token'] : "";
+ $oauth_verifier = (array_key_exists('oauth_verifier', $_REQUEST)) ? $_REQUEST['oauth_verifier'] : "";
+
+ if (!$oauth_token || !$oauth_verifier) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an invalid oauth verifier.", 5);
+ }
+
+ // request an access token
+ $tokens = $this->api->accessToken($oauth_verifier);
+
+ // access tokens as received from provider
+ $this->access_tokens_raw = $tokens;
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code), 5);
+ }
+
+ // we should have an access_token, or else, something has gone wrong
+ if (!isset($tokens["oauth_token"])) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an invalid access token.", 5);
+ }
+
+ // we no more need to store request tokens
+ $this->deleteToken("request_token");
+ $this->deleteToken("request_token_secret");
+
+ // store access_token for later user
+ $this->token("access_token", $tokens['oauth_token']);
+ $this->token("access_token_secret", $tokens['oauth_token_secret']);
+
+ // set user as logged in to the current provider
+ $this->setUserConnected();
+ }
+
+}
diff --git a/plugin/social/Hybrid/Provider_Model_OAuth2.php b/plugin/social/Hybrid/Provider_Model_OAuth2.php
new file mode 100644
index 000000000..17a2103bd
--- /dev/null
+++ b/plugin/social/Hybrid/Provider_Model_OAuth2.php
@@ -0,0 +1,184 @@
+ "OK: Success!",
+ 304 => "Not Modified: There was no new data to return.",
+ 400 => "Bad Request: The request was invalid.",
+ 401 => "Unauthorized.",
+ 403 => "Forbidden: The request is understood, but it has been refused.",
+ 404 => "Not Found: The URI requested is invalid or the resource requested does not exists.",
+ 406 => "Not Acceptable.",
+ 500 => "Internal Server Error: Something is broken.",
+ 502 => "Bad Gateway.",
+ 503 => "Service Unavailable."
+ );
+
+ if (!$code && $this->api) {
+ $code = $this->api->http_code;
+ }
+
+ if (isset($http_status_codes[$code])) {
+ return $code . " " . $http_status_codes[$code];
+ }
+ }
+
+ /**
+ * Adapter initializer
+ */
+ function initialize() {
+ if (!$this->config["keys"]["id"] || !$this->config["keys"]["secret"]) {
+ throw new Exception("Your application id and secret are required in order to connect to {$this->providerId}.", 4);
+ }
+
+ // override requested scope
+ if (isset($this->config["scope"]) && !empty($this->config["scope"])) {
+ $this->scope = $this->config["scope"];
+ }
+
+ // include OAuth2 client
+ require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth2Client.php";
+
+ // create a new OAuth2 client instance
+ $this->api = new OAuth2Client($this->config["keys"]["id"], $this->config["keys"]["secret"], $this->endpoint, $this->compressed);
+
+ // If we have an access token, set it
+ if ($this->token("access_token")) {
+ $this->api->access_token = $this->token("access_token");
+ $this->api->refresh_token = $this->token("refresh_token");
+ $this->api->access_token_expires_in = $this->token("expires_in");
+ $this->api->access_token_expires_at = $this->token("expires_at");
+ }
+
+ // Set curl proxy if exist
+ if (isset(Hybrid_Auth::$config["proxy"])) {
+ $this->api->curl_proxy = Hybrid_Auth::$config["proxy"];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginBegin() {
+ // redirect the user to the provider authentication url
+ Hybrid_Auth::redirect($this->api->authorizeUrl(array("scope" => $this->scope)));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginFinish() {
+ $error = (array_key_exists('error', $_REQUEST)) ? $_REQUEST['error'] : "";
+
+ // check for errors
+ if ($error) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an error: $error", 5);
+ }
+
+ // try to authenticate user
+ $code = (array_key_exists('code', $_REQUEST)) ? $_REQUEST['code'] : "";
+
+ try {
+ $this->api->authenticate($code);
+ } catch (Exception $e) {
+ throw new Exception("User profile request failed! {$this->providerId} returned an error: " . $e->getMessage(), 6);
+ }
+
+ // check if authenticated
+ if (!$this->api->access_token) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an invalid access token.", 5);
+ }
+
+ // store tokens
+ $this->token("access_token", $this->api->access_token);
+ $this->token("refresh_token", $this->api->refresh_token);
+ $this->token("expires_in", $this->api->access_token_expires_in);
+ $this->token("expires_at", $this->api->access_token_expires_at);
+
+ // set user connected locally
+ $this->setUserConnected();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function refreshToken() {
+ // have an access token?
+ if ($this->api->access_token) {
+
+ // have to refresh?
+ if ($this->api->refresh_token && $this->api->access_token_expires_at) {
+
+ // expired?
+ if ($this->api->access_token_expires_at <= time()) {
+ $response = $this->api->refreshToken(array("refresh_token" => $this->api->refresh_token));
+
+ if (!isset($response->access_token) || !$response->access_token) {
+ // set the user as disconnected at this point and throw an exception
+ $this->setUserUnconnected();
+
+ throw new Exception("The Authorization Service has return an invalid response while requesting a new access token. " . (string) $response->error);
+ }
+
+ // set new access_token
+ $this->api->access_token = $response->access_token;
+
+ if (isset($response->refresh_token))
+ $this->api->refresh_token = $response->refresh_token;
+
+ if (isset($response->expires_in)) {
+ $this->api->access_token_expires_in = $response->expires_in;
+
+ // even given by some idp, we should calculate this
+ $this->api->access_token_expires_at = time() + $response->expires_in;
+ }
+ }
+ }
+
+ // re store tokens
+ $this->token("access_token", $this->api->access_token);
+ $this->token("refresh_token", $this->api->refresh_token);
+ $this->token("expires_in", $this->api->access_token_expires_in);
+ $this->token("expires_at", $this->api->access_token_expires_at);
+ }
+ }
+
+}
diff --git a/plugin/social/Hybrid/Provider_Model_OpenID.php b/plugin/social/Hybrid/Provider_Model_OpenID.php
new file mode 100644
index 000000000..d3ec7cab5
--- /dev/null
+++ b/plugin/social/Hybrid/Provider_Model_OpenID.php
@@ -0,0 +1,170 @@
+public $openidIdentifier = "";
+ *
+ * Hybrid_Provider_Model_OpenID use LightOpenID lib which can be found on
+ * Hybrid/thirdparty/OpenID/LightOpenID.php
+ */
+class Hybrid_Provider_Model_OpenID extends Hybrid_Provider_Model {
+
+ /**
+ * Provider API client
+ * @var LightOpenID
+ */
+ public $api = null;
+
+ /**
+ * Openid provider identifier
+ * @var string
+ */
+ public $openidIdentifier = "";
+
+ /**
+ * {@inheritdoc}
+ */
+ function initialize() {
+ if (isset($this->params["openid_identifier"])) {
+ $this->openidIdentifier = $this->params["openid_identifier"];
+ }
+
+ // include LightOpenID lib
+ require_once Hybrid_Auth::$config["path_libraries"] . "OpenID/LightOpenID.php";
+
+ // An error was occurring when proxy wasn't set. Not sure where proxy was meant to be set/initialized.
+ Hybrid_Auth::$config['proxy'] = isset(Hybrid_Auth::$config['proxy']) ? Hybrid_Auth::$config['proxy'] : '';
+
+ $hostPort = parse_url(Hybrid_Auth::$config["base_url"], PHP_URL_PORT);
+ $hostUrl = parse_url(Hybrid_Auth::$config["base_url"], PHP_URL_HOST);
+
+ // Check for port on url
+ if ($hostPort) {
+ $hostUrl .= ':' . $hostPort;
+ }
+
+ $this->api = new LightOpenID($hostUrl, Hybrid_Auth::$config["proxy"]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginBegin() {
+ if (empty($this->openidIdentifier)) {
+ throw new Exception("OpenID adapter require the identity provider identifier 'openid_identifier' as an extra parameter.", 4);
+ }
+
+ $this->api->identity = $this->openidIdentifier;
+ $this->api->returnUrl = $this->endpoint;
+ $this->api->required = array(
+ 'namePerson/first',
+ 'namePerson/last',
+ 'namePerson/friendly',
+ 'namePerson',
+ 'contact/email',
+ 'birthDate',
+ 'birthDate/birthDay',
+ 'birthDate/birthMonth',
+ 'birthDate/birthYear',
+ 'person/gender',
+ 'pref/language',
+ 'contact/postalCode/home',
+ 'contact/city/home',
+ 'contact/country/home',
+ 'media/image/default',
+ );
+
+ # redirect the user to the provider authentication url
+ Hybrid_Auth::redirect($this->api->authUrl());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginFinish() {
+ # if user don't grant access of their data to your site, halt with an Exception
+ if ($this->api->mode == 'cancel') {
+ throw new Exception("Authentication failed! User has canceled authentication!", 5);
+ }
+
+ # if something goes wrong
+ if (!$this->api->validate()) {
+ throw new Exception("Authentication failed. Invalid request received!", 5);
+ }
+
+ # fetch received user data
+ $response = $this->api->getAttributes();
+
+ # store the user profile
+ $this->user->profile->identifier = $this->api->identity;
+
+ $this->user->profile->firstName = (array_key_exists("namePerson/first", $response)) ? $response["namePerson/first"] : "";
+ $this->user->profile->lastName = (array_key_exists("namePerson/last", $response)) ? $response["namePerson/last"] : "";
+ $this->user->profile->displayName = (array_key_exists("namePerson", $response)) ? $response["namePerson"] : "";
+ $this->user->profile->email = (array_key_exists("contact/email", $response)) ? $response["contact/email"] : "";
+ $this->user->profile->language = (array_key_exists("pref/language", $response)) ? $response["pref/language"] : "";
+ $this->user->profile->country = (array_key_exists("contact/country/home", $response)) ? $response["contact/country/home"] : "";
+ $this->user->profile->zip = (array_key_exists("contact/postalCode/home", $response)) ? $response["contact/postalCode/home"] : "";
+ $this->user->profile->gender = (array_key_exists("person/gender", $response)) ? $response["person/gender"] : "";
+ $this->user->profile->photoURL = (array_key_exists("media/image/default", $response)) ? $response["media/image/default"] : "";
+
+ $this->user->profile->birthDay = (array_key_exists("birthDate/birthDay", $response)) ? $response["birthDate/birthDay"] : "";
+ $this->user->profile->birthMonth = (array_key_exists("birthDate/birthMonth", $response)) ? $response["birthDate/birthMonth"] : "";
+ $this->user->profile->birthYear = (array_key_exists("birthDate/birthDate", $response)) ? $response["birthDate/birthDate"] : "";
+
+ if (isset($response['namePerson/friendly']) && !empty($response['namePerson/friendly']) && !$this->user->profile->displayName) {
+ $this->user->profile->displayName = $response["namePerson/friendly"];
+ }
+
+ if (isset($response['birthDate']) && !empty($response['birthDate']) && !$this->user->profile->birthDay) {
+ list( $birthday_year, $birthday_month, $birthday_day ) = $response['birthDate'];
+
+ $this->user->profile->birthDay = (int) $birthday_day;
+ $this->user->profile->birthMonth = (int) $birthday_month;
+ $this->user->profile->birthYear = (int) $birthday_year;
+ }
+
+ if (!$this->user->profile->displayName) {
+ $this->user->profile->displayName = trim($this->user->profile->firstName . " " . $this->user->profile->lastName);
+ }
+
+ if ($this->user->profile->gender == "f") {
+ $this->user->profile->gender = "female";
+ }
+
+ if ($this->user->profile->gender == "m") {
+ $this->user->profile->gender = "male";
+ }
+
+ // set user as logged in
+ $this->setUserConnected();
+
+ // with openid providers we get the user profile only once, so store it
+ Hybrid_Auth::storage()->set("hauth_session.{$this->providerId}.user", $this->user);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function getUserProfile() {
+ // try to get the user profile from stored data
+ $this->user = Hybrid_Auth::storage()->get("hauth_session.{$this->providerId}.user");
+
+ // if not found
+ if (!is_object($this->user)) {
+ throw new Exception("User profile request failed! User is not connected to {$this->providerId} or his session has expired.", 6);
+ }
+
+ return $this->user->profile;
+ }
+
+}
diff --git a/plugin/social/Hybrid/Providers/Facebook.php b/plugin/social/Hybrid/Providers/Facebook.php
new file mode 100644
index 000000000..e373476e0
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Facebook.php
@@ -0,0 +1,330 @@
+api->api_base_url = 'https://graph.facebook.com/';
+ $this->api->authorize_url = 'https://www.facebook.com/dialog/oauth';
+ $this->api->token_url = 'https://graph.facebook.com/v2.3/oauth/access_token';
+ $this->api->token_debug = 'https://graph.facebook.com/debug_token';
+
+ if (!$this->config["keys"]["id"] || !$this->config["keys"]["secret"]) {
+ throw new Exception("Your application id and secret are required in order to connect to {$this->providerId}.", 4);
+ }
+
+ }
+ /**
+ * {@inheritdoc}
+ */
+ function loginBegin() {
+
+ $token = md5(uniqid(mt_rand(), true));
+ Hybrid_Auth::storage()->set('fb_auth_nonce', $token);
+
+ $parameters = array(
+ "response_type" => "code",
+ "client_id" => $this->api->client_id,
+ "redirect_uri" => $this->api->redirect_uri,
+ "state" => $token,
+ );
+
+ Hybrid_Auth::redirect($this->api->authorizeUrl($parameters));
+ }
+ /**
+ * {@inheritdoc}
+ */
+ function loginFinish() {
+
+ // in case we get error_reason=user_denied&error=access_denied
+ if (isset($_REQUEST['error']) && $_REQUEST['error'] == "access_denied") {
+ throw new Exception("Authentication failed! The user denied your request.", 5);
+ }
+
+ // try to authenicate user
+ $code = (array_key_exists('code', $_REQUEST)) ? $_REQUEST['code'] : "";
+ try{
+ $response = $this->api->authenticate( $code );
+ }
+ catch( Exception $e ){
+ throw new Exception( "User profile request failed! {$this->providerId} returned an error: $e", 6 );
+ }
+
+ // check if authenticated
+ if ( ! $this->api->authenticated() ){
+ throw new Exception( "Authentication failed! {$this->providerId} returned an invalid access token.", 5 );
+ }
+ // store tokens
+ $this->token("access_token", $this->api->access_token);
+ $this->token("refresh_token", $this->api->refresh_token);
+ $this->token("expires_in", $this->api->access_token_expires_in);
+ $this->token("expires_at", $this->api->access_token_expires_at);
+ // set user connected locally
+ $this->setUserConnected();
+
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ function logout() {
+ parent::logout();
+ }
+ /**
+ * {@inheritdoc}
+ */
+ function getUserProfile() {
+ // request user profile from fb api
+ try {
+ $fields = array(
+ 'id', 'name', 'first_name', 'last_name', 'link', 'website',
+ 'gender', 'locale', 'about', 'email', 'hometown', 'location',
+ 'birthday'
+ );
+ $data = $this->api->api('/me?fields=' . implode(',', $fields).'&access_token='.$this->token('access_token'));
+ } catch (Exception $e) {
+ $this->logout();
+ throw new Exception("User profile request failed! {$this->providerId} returned an error: {$e->getMessage()}", 6, $e);
+ }
+
+ if( is_object($data) ){
+ $data = json_decode(json_encode($data), true);
+ }
+
+ // if the provider identifier is not received, we assume the auth has failed
+ if (!isset($data["id"])) {
+ $this->logout();
+ throw new Exception("User profile request failed! {$this->providerId} api returned an invalid response: " . Hybrid_Logger::dumpData( $data ), 6);
+ }
+ # store the user profile.
+ $this->user->profile->identifier = (array_key_exists('id', $data)) ? $data['id'] : "";
+ $this->user->profile->username = (array_key_exists('username', $data)) ? $data['username'] : "";
+ $this->user->profile->displayName = (array_key_exists('name', $data)) ? $data['name'] : "";
+ $this->user->profile->firstName = (array_key_exists('first_name', $data)) ? $data['first_name'] : "";
+ $this->user->profile->lastName = (array_key_exists('last_name', $data)) ? $data['last_name'] : "";
+ $this->user->profile->photoURL = "https://graph.facebook.com/" . $this->user->profile->identifier . "/picture?width=150&height=150";
+ $this->user->profile->coverInfoURL = "https://graph.facebook.com/" . $this->user->profile->identifier . "?fields=cover&access_token=" . $this->token('access_token');
+ $this->user->profile->profileURL = (array_key_exists('link', $data)) ? $data['link'] : "";
+ $this->user->profile->webSiteURL = (array_key_exists('website', $data)) ? $data['website'] : "";
+ $this->user->profile->gender = (array_key_exists('gender', $data)) ? $data['gender'] : "";
+ $this->user->profile->language = (array_key_exists('locale', $data)) ? $data['locale'] : "";
+ $this->user->profile->description = (array_key_exists('about', $data)) ? $data['about'] : "";
+ $this->user->profile->email = (array_key_exists('email', $data)) ? $data['email'] : "";
+ $this->user->profile->emailVerified = (array_key_exists('email', $data)) ? $data['email'] : "";
+ $this->user->profile->region = (array_key_exists("location", $data) && array_key_exists("name", $data['location'])) ? $data['location']["name"] : "";
+ if (!empty($this->user->profile->region)) {
+ $regionArr = explode(',', $this->user->profile->region);
+ if (count($regionArr) > 1) {
+ $this->user->profile->city = trim($regionArr[0]);
+ $this->user->profile->country = trim($regionArr[1]);
+ }
+ }
+ if (array_key_exists('birthday', $data)) {
+ list($birthday_month, $birthday_day, $birthday_year) = explode("/", $data['birthday']);
+ $this->user->profile->birthDay = (int) $birthday_day;
+ $this->user->profile->birthMonth = (int) $birthday_month;
+ $this->user->profile->birthYear = (int) $birthday_year;
+ }
+
+ $this->user->profile->sid = get_social_convert_id( $this->user->profile->identifier, $this->providerId );
+
+ return $this->user->profile;
+ }
+ /**
+ * Attempt to retrieve the url to the cover image given the coverInfoURL
+ *
+ * @param string $coverInfoURL coverInfoURL variable
+ * @return string url to the cover image OR blank string
+ */
+ function getCoverURL($coverInfoURL) {
+ try {
+ $headers = get_headers($coverInfoURL);
+ if (substr($headers[0], 9, 3) != "404") {
+ $coverOBJ = json_decode(file_get_contents($coverInfoURL));
+ if (array_key_exists('cover', $coverOBJ)) {
+ return $coverOBJ->cover->source;
+ }
+ }
+ } catch (Exception $e) {
+ }
+ return "";
+ }
+ /**
+ * {@inheritdoc}
+ */
+ function getUserContacts() {
+ $apiCall = '?fields=link,name';
+ $returnedContacts = array();
+ $pagedList = false;
+ do {
+ try {
+ $response = $this->api->api('/me/friends' . $apiCall);
+ } catch (Exception $e) {
+ throw new Exception("User contacts request failed! {$this->providerId} returned an error {$e->getMessage()}", 0, $e);
+ }
+ // Prepare the next call if paging links have been returned
+ if (array_key_exists('paging', $response) && array_key_exists('next', $response['paging'])) {
+ $pagedList = true;
+ $next_page = explode('friends', $response['paging']['next']);
+ $apiCall = $next_page[1];
+ } else {
+ $pagedList = false;
+ }
+ // Add the new page contacts
+ $returnedContacts = array_merge($returnedContacts, $response['data']);
+ } while ($pagedList == true);
+ $contacts = array();
+ foreach ($returnedContacts as $item) {
+ $uc = new Hybrid_User_Contact();
+ $uc->identifier = (array_key_exists("id", $item)) ? $item["id"] : "";
+ $uc->displayName = (array_key_exists("name", $item)) ? $item["name"] : "";
+ $uc->profileURL = (array_key_exists("link", $item)) ? $item["link"] : "https://www.facebook.com/profile.php?id=" . $uc->identifier;
+ $uc->photoURL = "https://graph.facebook.com/" . $uc->identifier . "/picture?width=150&height=150";
+ $contacts[] = $uc;
+ }
+ return $contacts;
+ }
+ /**
+ * Update user status
+ *
+ * @param mixed $status An array describing the status, or string
+ * @param string $pageid (optional) User page id
+ * @return array
+ * @throw Exception
+ */
+ function setUserStatus($status, $pageid = null) {
+ if (!is_array($status)) {
+ $status = array('message' => $status);
+ }
+ if (is_null($pageid)) {
+ $pageid = 'me';
+ // if post on page, get access_token page
+ } else {
+ $access_token = null;
+ foreach ($this->getUserPages(true) as $p) {
+ if (isset($p['id']) && intval($p['id']) == intval($pageid)) {
+ $access_token = $p['access_token'];
+ break;
+ }
+ }
+ if (is_null($access_token)) {
+ throw new Exception("Update user page failed, page not found or not writable!");
+ }
+ $status['access_token'] = $access_token;
+ }
+ try {
+ $response = $this->api->api('/' . $pageid . '/feed', 'post', $status);
+ } catch (Exception $e) {
+ throw new Exception("Update user status failed! {$this->providerId} returned an error {$e->getMessage()}", 0, $e);
+ }
+ return $response;
+ }
+ /**
+ * {@inheridoc}
+ */
+ function getUserStatus($postid) {
+ try {
+ $postinfo = $this->api->api("/" . $postid);
+ } catch (Exception $e) {
+ throw new Exception("Cannot retrieve user status! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
+ }
+ return $postinfo;
+ }
+ /**
+ * {@inheridoc}
+ */
+ function getUserPages($writableonly = false) {
+ if (( isset($this->config['scope']) && strpos($this->config['scope'], 'manage_pages') === false ) || (!isset($this->config['scope']) && strpos($this->scope, 'manage_pages') === false ))
+ throw new Exception("User status requires manage_page permission!");
+ try {
+ $pages = $this->api->api("/me/accounts", 'get');
+ } catch (Exception $e) {
+ throw new Exception("Cannot retrieve user pages! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
+ }
+ if (!isset($pages['data'])) {
+ return array();
+ }
+ if (!$writableonly) {
+ return $pages['data'];
+ }
+ $wrpages = array();
+ foreach ($pages['data'] as $p) {
+ if (isset($p['perms']) && in_array('CREATE_CONTENT', $p['perms'])) {
+ $wrpages[] = $p;
+ }
+ }
+ return $wrpages;
+ }
+ /**
+ * load the user latest activity
+ * - timeline : all the stream
+ * - me : the user activity only
+ * {@inheritdoc}
+ */
+ function getUserActivity($stream) {
+ try {
+ if ($stream == "me") {
+ $response = $this->api->api('/me/feed');
+ } else {
+ $response = $this->api->api('/me/home');
+ }
+ } catch (Exception $e) {
+ throw new Exception("User activity stream request failed! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
+ }
+ if (!$response || !count($response['data'])) {
+ return array();
+ }
+ $activities = array();
+ foreach ($response['data'] as $item) {
+ if ($stream == "me" && $item["from"]["id"] != $this->api->getUser()) {
+ continue;
+ }
+ $ua = new Hybrid_User_Activity();
+ $ua->id = (array_key_exists("id", $item)) ? $item["id"] : "";
+ $ua->date = (array_key_exists("created_time", $item)) ? strtotime($item["created_time"]) : "";
+ if ($item["type"] == "video") {
+ $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
+ }
+ if ($item["type"] == "link") {
+ $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
+ }
+ if (empty($ua->text) && isset($item["story"])) {
+ $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
+ }
+ if (empty($ua->text) && isset($item["message"])) {
+ $ua->text = (array_key_exists("message", $item)) ? $item["message"] : "";
+ }
+ if (!empty($ua->text)) {
+ $ua->user->identifier = (array_key_exists("id", $item["from"])) ? $item["from"]["id"] : "";
+ $ua->user->displayName = (array_key_exists("name", $item["from"])) ? $item["from"]["name"] : "";
+ $ua->user->profileURL = "https://www.facebook.com/profile.php?id=" . $ua->user->identifier;
+ $ua->user->photoURL = "https://graph.facebook.com/" . $ua->user->identifier . "/picture?type=square";
+ $activities[] = $ua;
+ }
+ }
+ return $activities;
+ }
+}
\ No newline at end of file
diff --git a/plugin/social/Hybrid/Providers/Google.php b/plugin/social/Hybrid/Providers/Google.php
new file mode 100644
index 000000000..adb2d5a22
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Google.php
@@ -0,0 +1,308 @@
+ more infos on google APIs: http://developer.google.com (official site)
+ * or here: http://discovery-check.appspot.com/ (unofficial but up to date)
+ * default permissions
+ * {@inheritdoc}
+ */
+ public $scope = "https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/plus.profile.emails.read https://www.google.com/m8/feeds/";
+
+ /**
+ * {@inheritdoc}
+ */
+ function initialize() {
+ parent::initialize();
+
+ // Provider api end-points
+ $this->api->authorize_url = "https://accounts.google.com/o/oauth2/auth";
+ $this->api->token_url = "https://accounts.google.com/o/oauth2/token";
+ $this->api->token_info_url = "https://www.googleapis.com/oauth2/v2/tokeninfo";
+
+ // Google POST methods require an access_token in the header
+ $this->api->curl_header = array("Authorization: OAuth " . $this->api->access_token);
+
+ // Override the redirect uri when it's set in the config parameters. This way we prevent
+ // redirect uri mismatches when authenticating with Google.
+ if (isset($this->config['redirect_uri']) && !empty($this->config['redirect_uri'])) {
+ $this->api->redirect_uri = $this->config['redirect_uri'];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginBegin() {
+ $parameters = array("scope" => $this->scope, "access_type" => "offline");
+ $optionals = array("scope", "access_type", "redirect_uri", "approval_prompt", "hd", "state");
+
+ foreach ($optionals as $parameter) {
+ if (isset($this->config[$parameter]) && !empty($this->config[$parameter])) {
+ $parameters[$parameter] = $this->config[$parameter];
+ }
+ if (isset($this->config["scope"]) && !empty($this->config["scope"])) {
+ $this->scope = $this->config["scope"];
+ }
+ }
+
+ if (isset($this->config['force']) && $this->config['force'] === true) {
+ $parameters['approval_prompt'] = 'force';
+ }
+
+ Hybrid_Auth::redirect($this->api->authorizeUrl($parameters));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function getUserProfile() {
+ // refresh tokens if needed
+ $this->refreshToken();
+
+ // ask google api for user infos
+ if (strpos($this->scope, '/auth/plus.profile.emails.read') !== false) {
+ $verified = $this->api->api("https://www.googleapis.com/plus/v1/people/me");
+
+ if (!isset($verified->id) || isset($verified->error))
+ $verified = new stdClass();
+ } else {
+ $verified = $this->api->api("https://www.googleapis.com/plus/v1/people/me/openIdConnect");
+
+ if (!isset($verified->sub) || isset($verified->error))
+ $verified = new stdClass();
+ }
+
+ $response = $this->api->api("https://www.googleapis.com/plus/v1/people/me");
+ if (!isset($response->id) || isset($response->error)) {
+ throw new Exception("User profile request failed! {$this->providerId} returned an invalid response:" . Hybrid_Logger::dumpData( $response ), 6);
+ }
+
+ $this->user->profile->identifier = (property_exists($verified, 'id')) ? $verified->id : ((property_exists($response, 'id')) ? $response->id : "");
+ $this->user->profile->firstName = (property_exists($response, 'name')) ? $response->name->givenName : "";
+ $this->user->profile->lastName = (property_exists($response, 'name')) ? $response->name->familyName : "";
+ $this->user->profile->displayName = (property_exists($response, 'displayName')) ? $response->displayName : "";
+ $this->user->profile->photoURL = (property_exists($response, 'image')) ? ((property_exists($response->image, 'url')) ? substr($response->image->url, 0, -2) . "200" : '') : '';
+ $this->user->profile->profileURL = (property_exists($response, 'url')) ? $response->url : "";
+ $this->user->profile->description = (property_exists($response, 'aboutMe')) ? $response->aboutMe : "";
+ $this->user->profile->gender = (property_exists($response, 'gender')) ? $response->gender : "";
+ $this->user->profile->language = (property_exists($response, 'locale')) ? $response->locale : ((property_exists($verified, 'locale')) ? $verified->locale : "");
+ $this->user->profile->email = (property_exists($response, 'email')) ? $response->email : ((property_exists($verified, 'email')) ? $verified->email : "");
+ $this->user->profile->emailVerified = (property_exists($verified, 'email')) ? $verified->email : "";
+ if (property_exists($response, 'emails')) {
+ if (count($response->emails) == 1) {
+ $this->user->profile->email = $response->emails[0]->value;
+ } else {
+ foreach ($response->emails as $email) {
+ if ($email->type == 'account') {
+ $this->user->profile->email = $email->value;
+ break;
+ }
+ }
+ }
+ if (property_exists($verified, 'emails')) {
+ if (count($verified->emails) == 1) {
+ $this->user->profile->emailVerified = $verified->emails[0]->value;
+ } else {
+ foreach ($verified->emails as $email) {
+ if ($email->type == 'account') {
+ $this->user->profile->emailVerified = $email->value;
+ break;
+ }
+ }
+ }
+ }
+ }
+ $this->user->profile->phone = (property_exists($response, 'phone')) ? $response->phone : "";
+ $this->user->profile->country = (property_exists($response, 'country')) ? $response->country : "";
+ $this->user->profile->region = (property_exists($response, 'region')) ? $response->region : "";
+ $this->user->profile->zip = (property_exists($response, 'zip')) ? $response->zip : "";
+ if (property_exists($response, 'placesLived')) {
+ $this->user->profile->city = "";
+ $this->user->profile->address = "";
+ foreach ($response->placesLived as $c) {
+ if (property_exists($c, 'primary')) {
+ if ($c->primary == true) {
+ $this->user->profile->address = $c->value;
+ $this->user->profile->city = $c->value;
+ break;
+ }
+ } else {
+ if (property_exists($c, 'value')) {
+ $this->user->profile->address = $c->value;
+ $this->user->profile->city = $c->value;
+ }
+ }
+ }
+ }
+
+ // google API returns multiple urls, but a "website" only if it is verified
+ // see http://support.google.com/plus/answer/1713826?hl=en
+ if (property_exists($response, 'urls')) {
+ foreach ($response->urls as $u) {
+ if (property_exists($u, 'primary') && $u->primary == true)
+ $this->user->profile->webSiteURL = $u->value;
+ }
+ } else {
+ $this->user->profile->webSiteURL = '';
+ }
+ // google API returns age ranges min and/or max as of https://developers.google.com/+/web/api/rest/latest/people#resource
+ if (property_exists($response, 'ageRange')) {
+ if (property_exists($response->ageRange, 'min') && property_exists($response->ageRange, 'max')) {
+ $this->user->profile->age = $response->ageRange->min . ' - ' . $response->ageRange->max;
+ } else {
+ if (property_exists($response->ageRange, 'min')) {
+ $this->user->profile->age = '>= ' . $response->ageRange->min;
+ } else {
+ if (property_exists($response->ageRange, 'max')) {
+ $this->user->profile->age = '<= ' . $response->ageRange->max;
+ } else {
+ $this->user->profile->age = '';
+ }
+ }
+ }
+ } else {
+ $this->user->profile->age = '';
+ }
+ // google API returns birthdays only if a user set 'show in my account'
+ if (property_exists($response, 'birthday')) {
+ list($birthday_year, $birthday_month, $birthday_day) = explode('-', $response->birthday);
+
+ $this->user->profile->birthDay = (int) $birthday_day;
+ $this->user->profile->birthMonth = (int) $birthday_month;
+ $this->user->profile->birthYear = (int) $birthday_year;
+ } else {
+ $this->user->profile->birthDay = 0;
+ $this->user->profile->birthMonth = 0;
+ $this->user->profile->birthYear = 0;
+ }
+
+ $this->user->profile->sid = get_social_convert_id( $this->user->profile->identifier, $this->providerId );
+
+ return $this->user->profile;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function getUserContacts() {
+ // refresh tokens if needed
+ $this->refreshToken();
+
+ $contacts = array();
+ if (!isset($this->config['contacts_param'])) {
+ $this->config['contacts_param'] = array("max-results" => 500);
+ }
+
+ // Google Gmail and Android contacts
+ if (strpos($this->scope, '/m8/feeds/') !== false) {
+
+ $response = $this->api->api("https://www.google.com/m8/feeds/contacts/default/full?"
+ . http_build_query(array_merge(array('alt' => 'json'), $this->config['contacts_param'])));
+
+ if (!$response) {
+ return array();
+ }
+
+ if (isset($response->feed->entry)) {
+ foreach ($response->feed->entry as $idx => $entry) {
+ $uc = new Hybrid_User_Contact();
+ $uc->email = isset($entry->{'gd$email'}[0]->address) ? (string) $entry->{'gd$email'}[0]->address : '';
+ $uc->displayName = isset($entry->title->{'$t'}) ? (string) $entry->title->{'$t'} : '';
+ $uc->identifier = ($uc->email != '') ? $uc->email : '';
+ $uc->description = '';
+ if (property_exists($entry, 'link')) {
+ /**
+ * sign links with access_token
+ */
+ if (is_array($entry->link)) {
+ foreach ($entry->link as $l) {
+ if (property_exists($l, 'gd$etag') && $l->type == "image/*") {
+ $uc->photoURL = $this->addUrlParam($l->href, array('access_token' => $this->api->access_token));
+ } else if ($l->type == "self") {
+ $uc->profileURL = $this->addUrlParam($l->href, array('access_token' => $this->api->access_token));
+ }
+ }
+ }
+ } else {
+ $uc->profileURL = '';
+ }
+ if (property_exists($response, 'website')) {
+ if (is_array($response->website)) {
+ foreach ($response->website as $w) {
+ if ($w->primary == true)
+ $uc->webSiteURL = $w->value;
+ }
+ } else {
+ $uc->webSiteURL = $response->website->value;
+ }
+ } else {
+ $uc->webSiteURL = '';
+ }
+
+ $contacts[] = $uc;
+ }
+ }
+ }
+
+ // Google social contacts
+ if (strpos($this->scope, '/auth/plus.login') !== false) {
+
+ $response = $this->api->api("https://www.googleapis.com/plus/v1/people/me/people/visible?"
+ . http_build_query($this->config['contacts_param']));
+
+ if (!$response) {
+ return array();
+ }
+
+ foreach ($response->items as $idx => $item) {
+ $uc = new Hybrid_User_Contact();
+ $uc->email = (property_exists($item, 'email')) ? $item->email : '';
+ $uc->displayName = (property_exists($item, 'displayName')) ? $item->displayName : '';
+ $uc->identifier = (property_exists($item, 'id')) ? $item->id : '';
+
+ $uc->description = (property_exists($item, 'objectType')) ? $item->objectType : '';
+ $uc->photoURL = (property_exists($item, 'image')) ? ((property_exists($item->image, 'url')) ? $item->image->url : '') : '';
+ $uc->profileURL = (property_exists($item, 'url')) ? $item->url : '';
+ $uc->webSiteURL = '';
+
+ $contacts[] = $uc;
+ }
+ }
+
+ return $contacts;
+ }
+
+ /**
+ * Add query parameters to the $url
+ *
+ * @param string $url URL
+ * @param array $params Parameters to add
+ * @return string
+ */
+ function addUrlParam($url, array $params){
+ $query = parse_url($url, PHP_URL_QUERY);
+
+ // Returns the URL string with new parameters
+ if ($query) {
+ $url .= '&' . http_build_query($params);
+ } else {
+ $url .= '?' . http_build_query($params);
+ }
+ return $url;
+ }
+
+}
+
diff --git a/plugin/social/Hybrid/Providers/Kakao.php b/plugin/social/Hybrid/Providers/Kakao.php
new file mode 100644
index 000000000..3038b8352
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Kakao.php
@@ -0,0 +1,168 @@
+
+ * http://facebook.com/chuckoh
+ *
+ * Date: 11 10, 2014
+ * Time: 01:51 AM
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://www.wtfpl.net/txt/copying/ for more details.
+ *
+ */
+
+//https://github.com/jinseokoh/additional-providers
+class Hybrid_Providers_Kakao extends Hybrid_Provider_Model_OAuth2
+{
+ /**
+ * initialization
+ */
+ function initialize()
+ {
+ parent::initialize();
+
+ // Provider API end-points
+ $this->api->api_base_url = "https://kapi.kakao.com/v1/";
+ $this->api->authorize_url = "https://kauth.kakao.com/oauth/authorize";
+ $this->api->token_url = "https://kauth.kakao.com/oauth/token";
+ }
+
+ /**
+ * finish login step
+ */
+ function loginFinish()
+ {
+ $error = (array_key_exists('error', $_REQUEST)) ? $_REQUEST['error'] : "";
+ // check for errors
+ if ( $error ){
+ throw new Exception( "Authentication failed! {$this->providerId} returned an error: $error", 5 );
+ }
+ // try to authenicate user
+ $code = (array_key_exists('code', $_REQUEST)) ? $_REQUEST['code'] : "";
+ try{
+ $this->authenticate( $code );
+ }
+ catch( Exception $e ){
+ throw new Exception( "User profile request failed! {$this->providerId} returned an error: $e", 6 );
+ }
+ // check if authenticated
+ if ( ! $this->api->access_token ){
+ throw new Exception( "Authentication failed! {$this->providerId} returned an invalid access token.", 5 );
+ }
+ // store tokens
+ $this->token("access_token", $this->api->access_token);
+ $this->token("refresh_token", $this->api->refresh_token);
+ $this->token("expires_in", $this->api->access_token_expires_in);
+ $this->token("expires_at", $this->api->access_token_expires_at);
+ // set user connected locally
+ $this->setUserConnected();
+ }
+
+ /**
+ * load the user profile
+ */
+ function getUserProfile()
+ {
+ $params = array('property_keys'=>'kaccount_email');
+
+ $this->api->decode_json = false;
+ $this->api->curl_header = array( 'Authorization: Bearer ' . $this->api->access_token );
+
+ $data = $this->api->api("user/me", "POST", $params);
+
+ if ( ! isset( $data->id ) ) {
+ throw new Exception("User profile request failed! {$this->providerId} returned an invalid response.", 6);
+ }
+ # store the user profile.
+ $this->user->profile->identifier = @ $data->id;
+ $this->user->profile->displayName = @ $data->properties->nickname;
+ $this->user->profile->photoURL = @ $data->properties->thumbnail_image;
+ $email = @ $data->kaccount_email;
+
+ if( $email ){
+ $this->user->profile->email = $email;
+ }
+
+ $this->user->profile->sid = get_social_convert_id( $this->user->profile->identifier, $this->providerId );
+
+ return $this->user->profile;
+ }
+
+ private function authenticate($code)
+ {
+ $params = array(
+ "grant_type" => "authorization_code",
+ "client_id" => $this->api->client_id,
+ "redirect_uri" => $this->api->redirect_uri,
+ "code" => $code
+ );
+
+ $response = $this->request($this->api->token_url, $params, $this->api->curl_authenticate_method);
+ $response = $this->parseRequestResult($response);
+ if ( ! $response || ! isset($response->access_token) ) {
+ throw new Exception("The Authorization Service has return: " . $response->error);
+ }
+ if ( isset($response->access_token) ) $this->api->access_token = $response->access_token;
+ if ( isset($response->refresh_token) ) $this->api->refresh_token = $response->refresh_token;
+ if ( isset($response->expires_in) ) $this->api->access_token_expires_in = $response->expires_in;
+
+ // calculate when the access token expire
+ if ( isset($response->expires_in) ) {
+ $this->api->access_token_expires_at = time() + $response->expires_in;
+ }
+
+ return $response;
+ }
+
+ private function request($url, $params=false, $type="GET")
+ {
+ if(Class_exists('Hybrid_Logger')){
+ Hybrid_Logger::info("Enter OAuth2Client::request( $url )");
+ Hybrid_Logger::debug("OAuth2Client::request(). dump request params: ", serialize( $params ));
+ }
+ $this->http_info = array();
+ $ch = curl_init();
+
+ curl_setopt($ch, CURLOPT_URL , $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_TIMEOUT , $this->api->curl_time_out);
+ curl_setopt($ch, CURLOPT_USERAGENT , $this->api->curl_useragent);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->api->curl_connect_time_out);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->api->curl_ssl_verifypeer);
+ curl_setopt($ch, CURLOPT_HTTPHEADER , $this->api->curl_header);
+
+ if ( $this->api->curl_proxy ) {
+ curl_setopt( $ch, CURLOPT_PROXY, $this->curl_proxy);
+ }
+ if ( $type == "POST" ) {
+ curl_setopt($ch, CURLOPT_POST, 1);
+ if ($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query($params) );
+ }
+
+ $response = curl_exec($ch);
+ if(Class_exists('Hybrid_Logger')){
+ Hybrid_Logger::debug( "OAuth2Client::request(). dump request info: ", serialize(curl_getinfo($ch)) );
+ Hybrid_Logger::debug( "OAuth2Client::request(). dump request result: ", serialize($response ));
+ }
+ $this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $this->http_info = array_merge($this->http_info, curl_getinfo($ch));
+ curl_close ($ch);
+
+ return $response;
+ }
+
+ private function parseRequestResult($result)
+ {
+ if ( json_decode($result) ) return json_decode($result);
+ parse_str( $result, $ouput );
+ $result = new StdClass();
+ foreach( $ouput as $k => $v )
+ $result->$k = $v;
+
+ return $result;
+ }
+}
\ No newline at end of file
diff --git a/plugin/social/Hybrid/Providers/Naver.php b/plugin/social/Hybrid/Providers/Naver.php
new file mode 100644
index 000000000..bd5713be7
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Naver.php
@@ -0,0 +1,229 @@
+
+ * http://facebook.com/chuckoh
+ *
+ * Date: 11 11, 2014
+ * Time: 11:38 AM
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://www.wtfpl.net/txt/copying/ for more details.
+ *
+ */
+
+//https://github.com/jinseokoh/additional-providers
+class Hybrid_Providers_Naver extends Hybrid_Provider_Model_OAuth2
+{
+ /**
+ * initialization
+ */
+ function initialize()
+ {
+ parent::initialize();
+
+ // Provider API end-points
+ $this->api->api_base_url = "https://apis.naver.com/nidlogin/";
+ $this->api->authorize_url = "https://nid.naver.com/oauth2.0/authorize";
+ $this->api->token_url = "https://nid.naver.com/oauth2.0/token";
+ }
+
+ /**
+ * begin login step
+ */
+ function loginBegin()
+ {
+ $token = $this->generate_state_token();
+ Hybrid_Auth::storage()->set("naver_state_token", $token);
+
+ $parameters = array(
+ "response_type" => "code",
+ "client_id" => $this->api->client_id,
+ "redirect_uri" => $this->api->redirect_uri,
+ "state" => $token,
+ );
+
+ Hybrid_Auth::redirect($this->api->authorizeUrl($parameters));
+ }
+
+ /**
+ * finish login step
+ */
+ function loginFinish()
+ {
+ $error = (array_key_exists('error', $_REQUEST)) ? $_REQUEST['error'] : "";
+ // check for errors
+ if ( $error ){
+ throw new Exception( "Authentication failed! {$this->providerId} returned an error: $error", 5 );
+ }
+ // try to authenicate user
+ $code = (array_key_exists('code', $_REQUEST)) ? $_REQUEST['code'] : "";
+ try{
+ $this->authenticate( $code );
+ }
+ catch( Exception $e ){
+ throw new Exception( "User profile request failed! {$this->providerId} returned an error: $e", 6 );
+ }
+ // check if authenticated
+ if ( ! $this->api->access_token ){
+ throw new Exception( "Authentication failed! {$this->providerId} returned an invalid access token.", 5 );
+ }
+ // store tokens
+ $this->token("access_token", $this->api->access_token);
+ $this->token("refresh_token", $this->api->refresh_token);
+ $this->token("expires_in", $this->api->access_token_expires_in);
+ $this->token("expires_at", $this->api->access_token_expires_at);
+ // set user connected locally
+ $this->setUserConnected();
+ }
+
+ /**
+ * set propper headers
+ */
+ function profile($url) {
+ $this->api->decode_json = false;
+ $this->api->curl_header = array( 'Authorization: Bearer ' . $this->api->access_token );
+ $response = $this->api->get($url, array(), false);
+
+ return $response;
+ }
+
+ /**
+ * load the user profile
+ */
+ //https://developers.naver.com/docs/login/profile/
+ function getUserProfile()
+ {
+ $response = $this->profile("nid/getUserProfile.xml");
+
+ $xml = @ new SimpleXMLElement($response);
+ $data = array();
+ if ( $xml->result[0]->resultcode == '00' ) {
+ foreach ($xml->response->children() as $response => $k) {
+ $data[(string)$response] = (string) $k;
+ }
+ } else {
+ throw new Exception("User profile request failed! {$this->providerId} returned an invalid response.", 6);
+ }
+
+ # store the user profile.
+ //$this->user->profile->identifier = (array_key_exists('enc_id',$data))?$data['enc_id']:"";
+ $this->user->profile->identifier = (array_key_exists('id',$data))?$data['id']:"";
+ $this->user->profile->age = (array_key_exists('age',$data))?$data['age']:"";
+ $this->user->profile->displayName = '';
+ /*
+ if( array_key_exists('email',$data) ){
+ $tmp = explode("@", $data['email']);
+ $this->user->profile->displayName = $tmp[0];
+ }
+ */
+ $this->user->profile->displayName = (array_key_exists('nickname',$data))?$data['nickname']:"";
+ $this->user->profile->birthDay = '';
+ $this->user->profile->birthMonth = '';
+ if( array_key_exists('birthday',$data) ){
+ $tmp = explode("-",$data['birthday']);
+ if( isset($tmp[0]) ){
+ $this->user->profile->birthDay = $tmp[0];
+ }
+ if( isset($tmp[1]) ){
+ $this->user->profile->birthMonth = $tmp[1];
+ }
+ }
+ $this->user->profile->email = (array_key_exists('email',$data))?$data['email']:"";
+ $this->user->profile->emailVerified = (array_key_exists('email',$data))?$data['email']:"";
+ $this->user->profile->gender = (array_key_exists('gender',$data))?(($data['gender'] == "M")?"male":"female"):"";
+ $this->user->profile->photoURL = (array_key_exists('profile_image',$data))?$data['profile_image']:"";
+
+ $this->user->profile->sid = get_social_convert_id( $this->user->profile->identifier, $this->providerId );
+
+ return $this->user->profile;
+ }
+
+ private function authenticate($code)
+ {
+ $token = Hybrid_Auth::storage()->get("naver_state_token");
+ $params = array(
+ "grant_type" => "authorization_code",
+ "client_id" => $this->api->client_id,
+ "client_secret" => $this->api->client_secret,
+ // "redirect_uri" => $this->api->redirect_uri,
+ "code" => $code,
+ "state" => $token
+ );
+ Hybrid_Auth::storage()->set("naver_state_token", null);
+
+ $response = $this->request($this->api->token_url, $params, $this->api->curl_authenticate_method);
+ $response = $this->parseRequestResult($response);
+ if ( ! $response || ! isset($response->access_token) ) {
+ throw new Exception("The Authorization Service has return: " . $response->error);
+ }
+ if ( isset($response->access_token) ) $this->api->access_token = $response->access_token;
+ if ( isset($response->refresh_token) ) $this->api->refresh_token = $response->refresh_token;
+ if ( isset($response->expires_in) ) $this->api->access_token_expires_in = $response->expires_in;
+
+ // calculate when the access token expire
+ if ( isset($response->expires_in) ) {
+ $this->api->access_token_expires_at = time() + $response->expires_in;
+ }
+
+ return $response;
+ }
+
+ private function request($url, $params=false, $type="GET")
+ {
+ if(Class_exists('Hybrid_Logger')){
+ Hybrid_Logger::info("Enter OAuth2Client::request( $url )");
+ Hybrid_Logger::debug("OAuth2Client::request(). dump request params: ", serialize( $params ));
+ }
+ $this->http_info = array();
+ $ch = curl_init();
+
+ curl_setopt($ch, CURLOPT_URL , $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_TIMEOUT , $this->api->curl_time_out);
+ curl_setopt($ch, CURLOPT_USERAGENT , $this->api->curl_useragent);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->api->curl_connect_time_out);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->api->curl_ssl_verifypeer);
+ curl_setopt($ch, CURLOPT_HTTPHEADER , $this->api->curl_header);
+
+ if ( $this->api->curl_proxy ) {
+ curl_setopt( $ch, CURLOPT_PROXY, $this->curl_proxy);
+ }
+ if ( $type == "POST" ) {
+ curl_setopt($ch, CURLOPT_POST, 1);
+ if ($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query($params) );
+ }
+
+ $response = curl_exec($ch);
+ if(Class_exists('Hybrid_Logger')){
+ Hybrid_Logger::debug( "OAuth2Client::request(). dump request info: ", serialize(curl_getinfo($ch)) );
+ Hybrid_Logger::debug( "OAuth2Client::request(). dump request result: ", serialize($response ));
+ }
+ $this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $this->http_info = array_merge($this->http_info, curl_getinfo($ch));
+ curl_close ($ch);
+
+ return $response;
+ }
+
+ private function parseRequestResult($result)
+ {
+ if ( json_decode($result) ) return json_decode($result);
+ parse_str( $result, $ouput );
+ $result = new StdClass();
+ foreach( $ouput as $k => $v )
+ $result->$k = $v;
+
+ return $result;
+ }
+
+ private function generate_state_token() {
+ $mt = microtime();
+ $rand = mt_rand();
+
+ return md5($mt . $rand);
+ }
+}
diff --git a/plugin/social/Hybrid/Providers/Payco.php b/plugin/social/Hybrid/Providers/Payco.php
new file mode 100644
index 000000000..5eb5694f4
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Payco.php
@@ -0,0 +1,382 @@
+api->api_base_url = 'https://id.payco.com/oauth2.0/';
+ $this->api->authorize_url = 'https://id.payco.com/oauth2.0/authorize';
+ $this->api->token_url = 'https://id.payco.com/oauth2.0/token';
+ $this->api->token_info = 'https://apis3.krp.toastoven.net/payco/friends/getIdNoByFriendsToken.json';
+ $this->api->profile_url = 'https://apis3.krp.toastoven.net/payco/friends/getMemberProfileByFriendsToken.json';
+
+ if (!$this->config["keys"]["id"] || !$this->config["keys"]["secret"]) {
+ throw new Exception("Your application id and secret are required in order to connect to {$this->providerId}.", 4);
+ }
+
+ }
+ /**
+ * {@inheritdoc}
+ */
+ function loginBegin() {
+
+ $token = md5(uniqid(mt_rand(), true));
+ Hybrid_Auth::storage()->set('payco_auth_token', $token);
+
+ $parameters = array(
+ "response_type" => "code",
+ "client_id" => $this->api->client_id,
+ "redirect_uri" => $this->api->redirect_uri,
+ "state" => $token,
+ "userLocale" => "ko_KR",
+ "serviceProviderCode" => "FRIENDS",
+ );
+
+ Hybrid_Auth::redirect($this->api->authorizeUrl($parameters));
+
+ exit;
+
+ }
+ /**
+ * {@inheritdoc}
+ */
+ function loginFinish() {
+
+ // in case we get error_reason=user_denied&error=access_denied
+ if (isset($_REQUEST['error']) && $_REQUEST['error'] == "access_denied") {
+ throw new Exception("Authentication failed! The user denied your request.", 5);
+ }
+
+ // try to authenicate user
+ $code = (array_key_exists('code', $_REQUEST)) ? $_REQUEST['code'] : "";
+ try{
+ $response = $this->api->authenticate( $code );
+ }
+ catch( Exception $e ){
+ throw new Exception( "User profile request failed! {$this->providerId} returned an error: $e", 6 );
+ }
+
+ // check if authenticated
+ if ( ! $this->api->authenticated() ){
+ throw new Exception( "Authentication failed! {$this->providerId} returned an invalid access token.", 5 );
+ }
+ // store tokens
+ $this->token("access_token", $this->api->access_token);
+ $this->token("refresh_token", $this->api->refresh_token);
+ $this->token("expires_in", $this->api->access_token_expires_in);
+ $this->token("expires_at", $this->api->access_token_expires_at);
+
+ $this->setUserConnected();
+
+ }
+
+ function check_valid_access_token(){
+
+ $params = array(
+ 'body' => array(
+ 'client_id'=>$this->api->client_id,
+ 'access_token'=>$this->api->access_token,
+ ),
+ );
+
+ $this->api->curl_header = array(
+
+ 'Content-Type:application/json',
+ 'client_id: '.$this->api->client_id,
+ 'access_token: '.$this->api->access_token,
+
+ );
+
+ $response = $this->api->api( $this->api->token_info, 'POST', $params );
+
+ if( is_object($response) && !empty($response->idNo) && $response->header->successful ){
+ $this->idNo = $response->idNo;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function logout() {
+ parent::logout();
+ }
+ /**
+ * {@inheritdoc}
+ */
+
+ /**
+ * set propper headers
+ */
+ function getUserProfile() {
+
+ $data = null;
+
+ // request user profile
+ try {
+
+ if( $this->check_valid_access_token() ){
+ $params = array(
+ 'body' => array(
+ 'client_id'=>$this->api->client_id,
+ 'access_token'=>$this->api->access_token,
+ 'MemberProfile'=>'idNo,id,name',
+ 'idNo'=>$this->idNo,
+ ),
+ );
+
+ $this->api->curl_header = array(
+ 'Content-Type:application/json',
+ 'client_id: '.$this->api->client_id,
+ 'access_token: '.$this->api->access_token,
+ 'Authorization: Bearer ' . $this->api->access_token,
+ );
+
+ $response = $this->api->api( $this->api->profile_url, 'POST', $params );
+ }
+
+ } catch (Exception $e) {
+ throw new Exception("User profile request failed! {$this->providerId} returned an error: {$e->getMessage()}", 6, $e);
+ }
+
+ if( ! is_object($response) || property_exists($response, 'error_code') ){
+ $this->logout();
+
+ throw new Exception( "Authentication failed! {$this->providerId} returned an invalid access token.", 5 );
+ }
+
+ if( is_object($response) ){
+ $result = json_decode(json_encode($response), true);
+ $data = $result['memberProfile'];
+ }
+
+ // if the provider identifier is not received, we assume the auth has failed
+ if (!isset($data["id"])) {
+ $this->logout();
+ throw new Exception("User profile request failed! {$this->providerId} api returned an invalid response: " . Hybrid_Logger::dumpData( $data ), 6);
+ }
+
+ # store the user profile.
+ $this->user->profile->identifier = (array_key_exists('idNo', $data)) ? $data['idNo'] : "";
+ $this->user->profile->username = (array_key_exists('name', $data)) ? $data['name'] : "";
+ $this->user->profile->displayName = (array_key_exists('name', $data)) ? $data['name'] : "";
+ $this->user->profile->age = (array_key_exists('ageGroup', $data)) ? $data['ageGroup'] : "";
+
+ include_once(G5_LIB_PATH.'/register.lib.php');
+
+ $payco_no = substr(base_convert($this->user->profile->identifier, 16, 36), 0, 16);
+ $email = (array_key_exists('id', $data)) ? $data['id'] : "";
+
+ $this->user->profile->gender = (array_key_exists('sexCode', $data)) ? $data['sexCode'] : "";
+
+ $this->user->profile->email = ! valid_mb_email($email) ? $email : "";
+ $this->user->profile->emailVerified = ! valid_mb_email($email) ? $email : "";
+
+
+ if (array_key_exists('birthdayMMdd', $data)) {
+ $this->user->profile->birthMonth = substr($data['birthdayMMdd'], 0, 2);
+ $this->user->profile->birthDay = substr($data['birthdayMMdd'], 2, 4);
+ }
+
+ $this->user->profile->sid = get_social_convert_id( $this->user->profile->identifier, $this->providerId );
+
+ return $this->user->profile;
+ }
+ /**
+ * Attempt to retrieve the url to the cover image given the coverInfoURL
+ *
+ * @param string $coverInfoURL coverInfoURL variable
+ * @return string url to the cover image OR blank string
+ */
+ function getCoverURL($coverInfoURL) {
+ try {
+ $headers = get_headers($coverInfoURL);
+ if (substr($headers[0], 9, 3) != "404") {
+ $coverOBJ = json_decode(file_get_contents($coverInfoURL));
+ if (array_key_exists('cover', $coverOBJ)) {
+ return $coverOBJ->cover->source;
+ }
+ }
+ } catch (Exception $e) {
+ }
+ return "";
+ }
+ /**
+ * {@inheritdoc}
+ */
+ function getUserContacts() {
+ $apiCall = '?fields=link,name';
+ $returnedContacts = array();
+ $pagedList = false;
+ do {
+ try {
+ $response = $this->api->api('/me/friends' . $apiCall);
+ } catch (Exception $e) {
+ throw new Exception("User contacts request failed! {$this->providerId} returned an error {$e->getMessage()}", 0, $e);
+ }
+ // Prepare the next call if paging links have been returned
+ if (array_key_exists('paging', $response) && array_key_exists('next', $response['paging'])) {
+ $pagedList = true;
+ $next_page = explode('friends', $response['paging']['next']);
+ $apiCall = $next_page[1];
+ } else {
+ $pagedList = false;
+ }
+ // Add the new page contacts
+ $returnedContacts = array_merge($returnedContacts, $response['data']);
+ } while ($pagedList == true);
+ $contacts = array();
+ foreach ($returnedContacts as $item) {
+ $uc = new Hybrid_User_Contact();
+ $uc->identifier = (array_key_exists("id", $item)) ? $item["id"] : "";
+ $uc->displayName = (array_key_exists("name", $item)) ? $item["name"] : "";
+ $uc->profileURL = (array_key_exists("link", $item)) ? $item["link"] : "https://www.facebook.com/profile.php?id=" . $uc->identifier;
+ $uc->photoURL = "https://graph.facebook.com/" . $uc->identifier . "/picture?width=150&height=150";
+ $contacts[] = $uc;
+ }
+ return $contacts;
+ }
+ /**
+ * Update user status
+ *
+ * @param mixed $status An array describing the status, or string
+ * @param string $pageid (optional) User page id
+ * @return array
+ * @throw Exception
+ */
+ function setUserStatus($status, $pageid = null) {
+ if (!is_array($status)) {
+ $status = array('message' => $status);
+ }
+ if (is_null($pageid)) {
+ $pageid = 'me';
+ // if post on page, get access_token page
+ } else {
+ $access_token = null;
+ foreach ($this->getUserPages(true) as $p) {
+ if (isset($p['id']) && intval($p['id']) == intval($pageid)) {
+ $access_token = $p['access_token'];
+ break;
+ }
+ }
+ if (is_null($access_token)) {
+ throw new Exception("Update user page failed, page not found or not writable!");
+ }
+ $status['access_token'] = $access_token;
+ }
+ try {
+ $response = $this->api->api('/' . $pageid . '/feed', 'post', $status);
+ } catch (Exception $e) {
+ throw new Exception("Update user status failed! {$this->providerId} returned an error {$e->getMessage()}", 0, $e);
+ }
+ return $response;
+ }
+ /**
+ * {@inheridoc}
+ */
+ function getUserStatus($postid) {
+ try {
+ $postinfo = $this->api->api("/" . $postid);
+ } catch (Exception $e) {
+ throw new Exception("Cannot retrieve user status! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
+ }
+ return $postinfo;
+ }
+ /**
+ * {@inheridoc}
+ */
+ function getUserPages($writableonly = false) {
+ if (( isset($this->config['scope']) && strpos($this->config['scope'], 'manage_pages') === false ) || (!isset($this->config['scope']) && strpos($this->scope, 'manage_pages') === false ))
+ throw new Exception("User status requires manage_page permission!");
+ try {
+ $pages = $this->api->api("/me/accounts", 'get');
+ } catch (Exception $e) {
+ throw new Exception("Cannot retrieve user pages! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
+ }
+ if (!isset($pages['data'])) {
+ return array();
+ }
+ if (!$writableonly) {
+ return $pages['data'];
+ }
+ $wrpages = array();
+ foreach ($pages['data'] as $p) {
+ if (isset($p['perms']) && in_array('CREATE_CONTENT', $p['perms'])) {
+ $wrpages[] = $p;
+ }
+ }
+ return $wrpages;
+ }
+ /**
+ * load the user latest activity
+ * - timeline : all the stream
+ * - me : the user activity only
+ * {@inheritdoc}
+ */
+ function getUserActivity($stream) {
+ try {
+ if ($stream == "me") {
+ $response = $this->api->api('/me/feed');
+ } else {
+ $response = $this->api->api('/me/home');
+ }
+ } catch (Exception $e) {
+ throw new Exception("User activity stream request failed! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
+ }
+ if (!$response || !count($response['data'])) {
+ return array();
+ }
+ $activities = array();
+ foreach ($response['data'] as $item) {
+ if ($stream == "me" && $item["from"]["id"] != $this->api->getUser()) {
+ continue;
+ }
+ $ua = new Hybrid_User_Activity();
+ $ua->id = (array_key_exists("id", $item)) ? $item["id"] : "";
+ $ua->date = (array_key_exists("created_time", $item)) ? strtotime($item["created_time"]) : "";
+ if ($item["type"] == "video") {
+ $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
+ }
+ if ($item["type"] == "link") {
+ $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
+ }
+ if (empty($ua->text) && isset($item["story"])) {
+ $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
+ }
+ if (empty($ua->text) && isset($item["message"])) {
+ $ua->text = (array_key_exists("message", $item)) ? $item["message"] : "";
+ }
+ if (!empty($ua->text)) {
+ $ua->user->identifier = (array_key_exists("id", $item["from"])) ? $item["from"]["id"] : "";
+ $ua->user->displayName = (array_key_exists("name", $item["from"])) ? $item["from"]["name"] : "";
+ $ua->user->profileURL = "https://www.facebook.com/profile.php?id=" . $ua->user->identifier;
+ $ua->user->photoURL = "https://graph.facebook.com/" . $ua->user->identifier . "/picture?type=square";
+ $activities[] = $ua;
+ }
+ }
+ return $activities;
+ }
+}
\ No newline at end of file
diff --git a/plugin/social/Hybrid/Providers/Twitter.php b/plugin/social/Hybrid/Providers/Twitter.php
new file mode 100644
index 000000000..cb1c1706b
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Twitter.php
@@ -0,0 +1,266 @@
+api->api_base_url = "https://api.twitter.com/1.1/";
+ $this->api->authorize_url = "https://api.twitter.com/oauth/authenticate";
+ $this->api->request_token_url = "https://api.twitter.com/oauth/request_token";
+ $this->api->access_token_url = "https://api.twitter.com/oauth/access_token";
+
+ if (isset($this->config['api_version']) && $this->config['api_version']) {
+ $this->api->api_base_url = "https://api.twitter.com/{$this->config['api_version']}/";
+ }
+
+ if (isset($this->config['authorize']) && $this->config['authorize']) {
+ $this->api->authorize_url = "https://api.twitter.com/oauth/authorize";
+ }
+
+ $this->api->curl_auth_header = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginBegin() {
+ // Initiate the Reverse Auth flow; cf. https://dev.twitter.com/docs/ios/using-reverse-auth
+ if (isset($_REQUEST['reverse_auth']) && ($_REQUEST['reverse_auth'] == 'yes')) {
+ $stage1 = $this->api->signedRequest($this->api->request_token_url, 'POST', array('x_auth_mode' => 'reverse_auth'));
+ if ($this->api->http_code != 200) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code), 5);
+ }
+ $responseObj = array('x_reverse_auth_parameters' => $stage1, 'x_reverse_auth_target' => $this->config["keys"]["key"]);
+ $response = json_encode($responseObj);
+ header("Content-Type: application/json", true, 200);
+ echo $response;
+ die();
+ }
+ $tokens = $this->api->requestToken($this->endpoint);
+
+ // request tokens as received from provider
+ $this->request_tokens_raw = $tokens;
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code), 5);
+ }
+
+ if (!isset($tokens["oauth_token"])) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an invalid oauth token.", 5);
+ }
+
+ $this->token("request_token", $tokens["oauth_token"]);
+ $this->token("request_token_secret", $tokens["oauth_token_secret"]);
+
+ // redirect the user to the provider authentication url with force_login
+ if (( isset($this->config['force_login']) && $this->config['force_login'] ) || ( isset($this->config['force']) && $this->config['force'] === true )) {
+ Hybrid_Auth::redirect($this->api->authorizeUrl($tokens, array('force_login' => true)));
+ }
+
+ // else, redirect the user to the provider authentication url
+ Hybrid_Auth::redirect($this->api->authorizeUrl($tokens));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function loginFinish() {
+ // in case we are completing a Reverse Auth flow; cf. https://dev.twitter.com/docs/ios/using-reverse-auth
+ if (isset($_REQUEST['oauth_token_secret'])) {
+ $tokens = $_REQUEST;
+ $this->access_tokens_raw = $tokens;
+
+ // we should have an access_token unless something has gone wrong
+ if (!isset($tokens["oauth_token"])) {
+ throw new Exception("Authentication failed! {$this->providerId} returned an invalid access token.", 5);
+ }
+
+ // Get rid of tokens we don't need
+ $this->deleteToken("request_token");
+ $this->deleteToken("request_token_secret");
+
+ // Store access_token and secret for later use
+ $this->token("access_token", $tokens['oauth_token']);
+ $this->token("access_token_secret", $tokens['oauth_token_secret']);
+
+ // set user as logged in to the current provider
+ $this->setUserConnected();
+ return;
+ }
+ parent::loginFinish();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function getUserProfile() {
+ $includeEmail = isset($this->config['includeEmail']) ? (bool) $this->config['includeEmail'] : false;
+ $response = $this->api->get('account/verify_credentials.json'. ($includeEmail ? '?include_email=true' : ''));
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("User profile request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code), 6);
+ }
+
+ if (!is_object($response) || !isset($response->id)) {
+ throw new Exception("User profile request failed! {$this->providerId} api returned an invalid response: " . Hybrid_Logger::dumpData( $response ), 6);
+ }
+
+ # store the user profile.
+ $this->user->profile->identifier = (property_exists($response, 'id')) ? $response->id : "";
+ $this->user->profile->displayName = (property_exists($response, 'screen_name')) ? $response->screen_name : "";
+ $this->user->profile->description = (property_exists($response, 'description')) ? $response->description : "";
+ $this->user->profile->firstName = (property_exists($response, 'name')) ? $response->name : "";
+ $this->user->profile->photoURL = (property_exists($response, 'profile_image_url')) ? (str_replace('_normal', '', $response->profile_image_url)) : "";
+ $this->user->profile->profileURL = (property_exists($response, 'screen_name')) ? ("http://twitter.com/" . $response->screen_name) : "";
+ $this->user->profile->webSiteURL = (property_exists($response, 'url')) ? $response->url : "";
+ $this->user->profile->region = (property_exists($response, 'location')) ? $response->location : "";
+ if($includeEmail) $this->user->profile->email = (property_exists($response, 'email')) ? $response->email : "";
+ if($includeEmail) $this->user->profile->emailVerified = (property_exists($response, 'email')) ? $response->email : "";
+
+ $this->user->profile->sid = get_social_convert_id( $this->user->profile->identifier, $this->providerId );
+
+ return $this->user->profile;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function getUserContacts() {
+ $parameters = array('cursor' => '-1');
+ $response = $this->api->get('friends/ids.json', $parameters);
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("User contacts request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code));
+ }
+
+ if (!$response || !count($response->ids)) {
+ return array();
+ }
+
+ // 75 id per time should be okey
+ $contactsids = array_chunk($response->ids, 75);
+
+ $contacts = array();
+
+ foreach ($contactsids as $chunk) {
+ $parameters = array('user_id' => implode(",", $chunk));
+ $response = $this->api->get('users/lookup.json', $parameters);
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("User contacts request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code));
+ }
+
+ if ($response && count($response)) {
+ foreach ($response as $item) {
+ $uc = new Hybrid_User_Contact();
+
+ $uc->identifier = (property_exists($item, 'id')) ? $item->id : "";
+ $uc->displayName = (property_exists($item, 'name')) ? $item->name : "";
+ $uc->profileURL = (property_exists($item, 'screen_name')) ? ("http://twitter.com/" . $item->screen_name) : "";
+ $uc->photoURL = (property_exists($item, 'profile_image_url')) ? $item->profile_image_url : "";
+ $uc->description = (property_exists($item, 'description')) ? $item->description : "";
+
+ $contacts[] = $uc;
+ }
+ }
+ }
+
+ return $contacts;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function setUserStatus($status) {
+
+ if (is_array($status) && isset($status['message']) && isset($status['picture'])) {
+ $response = $this->api->post('statuses/update_with_media.json', array('status' => $status['message'], 'media[]' => file_get_contents($status['picture'])), null, null, true);
+ } else {
+ $response = $this->api->post('statuses/update.json', array('status' => $status));
+ }
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("Update user status failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code));
+ }
+
+ return $response;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function getUserStatus($tweetid) {
+ $info = $this->api->get('statuses/show.json?id=' . $tweetid . '&include_entities=true');
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200 || !isset($info->id)) {
+ throw new Exception("Cannot retrieve user status! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code));
+ }
+
+ return $info;
+ }
+
+ /**
+ * load the user latest activity
+ * - timeline : all the stream
+ * - me : the user activity only
+ *
+ * by default return the timeline
+ * {@inheritdoc}
+ */
+ function getUserActivity($stream) {
+ if ($stream == "me") {
+ $response = $this->api->get('statuses/user_timeline.json');
+ } else {
+ $response = $this->api->get('statuses/home_timeline.json');
+ }
+
+ // check the last HTTP status code returned
+ if ($this->api->http_code != 200) {
+ throw new Exception("User activity stream request failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus($this->api->http_code));
+ }
+
+ if (!$response) {
+ return array();
+ }
+
+ $activities = array();
+
+ foreach ($response as $item) {
+ $ua = new Hybrid_User_Activity();
+
+ $ua->id = (property_exists($item, 'id')) ? $item->id : "";
+ $ua->date = (property_exists($item, 'created_at')) ? strtotime($item->created_at) : "";
+ $ua->text = (property_exists($item, 'text')) ? $item->text : "";
+
+ $ua->user->identifier = (property_exists($item->user, 'id')) ? $item->user->id : "";
+ $ua->user->displayName = (property_exists($item->user, 'name')) ? $item->user->name : "";
+ $ua->user->profileURL = (property_exists($item->user, 'screen_name')) ? ("http://twitter.com/" . $item->user->screen_name) : "";
+ $ua->user->photoURL = (property_exists($item->user, 'profile_image_url')) ? $item->user->profile_image_url : "";
+
+ $activities[] = $ua;
+ }
+
+ return $activities;
+ }
+
+}
diff --git a/plugin/social/Hybrid/Storage.php b/plugin/social/Hybrid/Storage.php
new file mode 100644
index 000000000..16a2f6cb2
--- /dev/null
+++ b/plugin/social/Hybrid/Storage.php
@@ -0,0 +1,141 @@
+config("php_session_id", session_id());
+ $this->config("version", Hybrid_Auth::$version);
+ }
+
+ /**
+ * Saves a value in the config storage, or returns config if value is null
+ *
+ * @param string $key Config name
+ * @param string $value Config value
+ * @return array|null
+ */
+ public function config($key, $value = null) {
+ $key = strtolower($key);
+
+ if ($value) {
+ $_SESSION["HA::CONFIG"][$key] = serialize($value);
+ } elseif (isset($_SESSION["HA::CONFIG"][$key])) {
+ return unserialize($_SESSION["HA::CONFIG"][$key]);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns value from session storage
+ *
+ * @param string $key Key
+ * @return string|null
+ */
+ public function get($key) {
+ $key = strtolower($key);
+
+ if (isset($_SESSION["HA::STORE"], $_SESSION["HA::STORE"][$key])) {
+ return unserialize($_SESSION["HA::STORE"][$key]);
+ }
+
+ return null;
+ }
+
+ /**
+ * Saves a key value pair to the session storage
+ *
+ * @param string $key Key
+ * @param string $value Value
+ * @return void
+ */
+ public function set($key, $value) {
+ $key = strtolower($key);
+ $_SESSION["HA::STORE"][$key] = serialize($value);
+ }
+
+ /**
+ * Clear session storage
+ * @return void
+ */
+ function clear() {
+ $_SESSION["HA::STORE"] = array();
+ }
+
+ /**
+ * Delete a specific key from session storage
+ *
+ * @param string $key Key
+ * @return void
+ */
+ function delete($key) {
+ $key = strtolower($key);
+
+ if (isset($_SESSION["HA::STORE"], $_SESSION["HA::STORE"][$key])) {
+ $f = $_SESSION['HA::STORE'];
+ unset($f[$key]);
+ $_SESSION["HA::STORE"] = $f;
+ }
+ }
+
+ /**
+ * Delete all keys recursively from session storage
+ *
+ * @param string $key Key
+ * @retun void
+ */
+ function deleteMatch($key) {
+ $key = strtolower($key);
+
+ if (isset($_SESSION["HA::STORE"]) && count($_SESSION["HA::STORE"])) {
+ $f = $_SESSION['HA::STORE'];
+ foreach ($f as $k => $v) {
+ if (strstr($k, $key)) {
+ unset($f[$k]);
+ }
+ }
+ $_SESSION["HA::STORE"] = $f;
+ }
+ }
+
+ /**
+ * Returns session storage as a serialized string
+ * @return string|null
+ */
+ function getSessionData() {
+ if (isset($_SESSION["HA::STORE"])) {
+ return serialize($_SESSION["HA::STORE"]);
+ }
+ return null;
+ }
+
+ /**
+ * Restores the session from serialized session data
+ *
+ * @param string $sessiondata Serialized session data
+ * @return void
+ */
+ function restoreSessionData($sessiondata = null) {
+ $_SESSION["HA::STORE"] = unserialize($sessiondata);
+ }
+
+}
diff --git a/plugin/social/Hybrid/StorageInterface.php b/plugin/social/Hybrid/StorageInterface.php
new file mode 100644
index 000000000..5b171ec3a
--- /dev/null
+++ b/plugin/social/Hybrid/StorageInterface.php
@@ -0,0 +1,29 @@
+timestamp = time();
+ $this->profile = new Hybrid_User_Profile();
+ }
+
+}
diff --git a/plugin/social/Hybrid/User_Activity.php b/plugin/social/Hybrid/User_Activity.php
new file mode 100644
index 000000000..5d0712ab6
--- /dev/null
+++ b/plugin/social/Hybrid/User_Activity.php
@@ -0,0 +1,55 @@
+user = new stdClass();
+
+ // typically, we should have a few information about the user who created the event from social apis
+ $this->user->identifier = null;
+ $this->user->displayName = null;
+ $this->user->profileURL = null;
+ $this->user->photoURL = null;
+ }
+
+}
diff --git a/plugin/social/Hybrid/User_Contact.php b/plugin/social/Hybrid/User_Contact.php
new file mode 100644
index 000000000..976595835
--- /dev/null
+++ b/plugin/social/Hybrid/User_Contact.php
@@ -0,0 +1,60 @@
+
+
+ Directory access is forbidden.
+ + +