diff --git a/mobile/skin/member/basic/scrap.skin.php b/mobile/skin/member/basic/scrap.skin.php
index ccb43bac5..6ba0a162c 100644
--- a/mobile/skin/member/basic/scrap.skin.php
+++ b/mobile/skin/member/basic/scrap.skin.php
@@ -5,13 +5,13 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
add_stylesheet('
+
-
+
-
-
+
삭제
@@ -22,6 +22,6 @@ add_stylesheet('',
-
+
diff --git a/mobile/skin/member/basic/scrap_popin.skin.php b/mobile/skin/member/basic/scrap_popin.skin.php
index 04dfd5fd3..1b3fc792b 100644
--- a/mobile/skin/member/basic/scrap_popin.skin.php
+++ b/mobile/skin/member/basic/scrap_popin.skin.php
@@ -5,35 +5,34 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
add_stylesheet('
', 0);
?>
-
+
스크랩하기
-
-
-
+
+
+
+
+
+
+
+
+
+ 본문
+
-
-
+ // 파일 출력
+ if($view['img_count']) {
+ echo "\n";
-
+ for ($i=0; $i<$view['img_count']; $i++) {
+ //echo $view['img_file'][$i];
+ echo get_view_thumbnail($view['img_file'][$i], $qaconfig['qa_image_width']);
+ }
-
-
-
\n";
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- 본문
-
- \n";
-
- for ($i=0; $i<$view['img_count']; $i++) {
- //echo $view['img_file'][$i];
- echo get_view_thumbnail($view['img_file'][$i], $qaconfig['qa_image_width']);
- }
-
- echo "
\n";
- }
- ?>
-
-
-
-
-
-
-
-
-
-
-
', 0);
연관질문
-
-
-
-
- | 분류 |
- 제목 |
- 상태 |
- 등록일 |
-
-
-
+
-
+ for($i=0; $i<$view['rel_count']; $i++) {
+ ?>
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
diff --git a/mobile/skin/qa/basic/write.skin.php b/mobile/skin/qa/basic/write.skin.php
index adbcbdfbb..3d19ca931 100644
--- a/mobile/skin/qa/basic/write.skin.php
+++ b/mobile/skin/qa/basic/write.skin.php
@@ -27,92 +27,82 @@ add_stylesheet('
', 0);
echo $option_hidden;
?>
-
-
-
-
목록
+
diff --git a/mobile/skin/search/basic/search.skin.php b/mobile/skin/search/basic/search.skin.php
index c1ae64ec8..d4229ec77 100644
--- a/mobile/skin/search/basic/search.skin.php
+++ b/mobile/skin/search/basic/search.skin.php
@@ -9,7 +9,7 @@ add_stylesheet('
',
-
+
+
-
-
-
회사소개
@@ -25,25 +23,50 @@ if(defined('G5_THEME_PATH')) {
서비스이용약관
Copyright ©
소유하신 도메인. All rights reserved.
-
상단으로
+
+
+
PC 버전으로 보기
+
-
-
PC 버전으로 보기
-
diff --git a/plugin/editor/smarteditor2/css/smart_editor2.css b/plugin/editor/smarteditor2/css/smart_editor2.css
index 6fde2758d..881272d8d 100644
--- a/plugin/editor/smarteditor2/css/smart_editor2.css
+++ b/plugin/editor/smarteditor2/css/smart_editor2.css
@@ -3,6 +3,7 @@
/* COMMON */
body,#smart_editor2,#smart_editor2 p,#smart_editor2 h1,#smart_editor2 h2,#smart_editor2 h3,#smart_editor2 h4,#smart_editor2 h5,#smart_editor2 h6,#smart_editor2 ul,#smart_editor2 ol,#smart_editor2 li,#smart_editor2 dl,#smart_editor2 dt,#smart_editor2 dd,#smart_editor2 table,#smart_editor2 th,#smart_editor2 td,#smart_editor2 form,#smart_editor2 fieldset,#smart_editor2 legend,#smart_editor2 input,#smart_editor2 textarea,#smart_editor2 button,#smart_editor2 select{margin:0;padding:0}
#smart_editor2,#smart_editor2 h1,#smart_editor2 h2,#smart_editor2 h3,#smart_editor2 h4,#smart_editor2 h5,#smart_editor2 h6,#smart_editor2 input,#smart_editor2 textarea,#smart_editor2 select,#smart_editor2 table,#smart_editor2 button{font-family:'돋움',Dotum,Helvetica,sans-serif;font-size:12px;color:#666}
+#smart_editor2{background:#fff}
#smart_editor2 span,#smart_editor2 em{font-size:12px}
#smart_editor2 em,#smart_editor2 address{font-style:normal}
#smart_editor2 img,#smart_editor2 fieldset{border:0}
diff --git a/plugin/kcaptcha/kcaptcha.lib.php b/plugin/kcaptcha/kcaptcha.lib.php
index 7fc5309a7..50a3cef69 100644
--- a/plugin/kcaptcha/kcaptcha.lib.php
+++ b/plugin/kcaptcha/kcaptcha.lib.php
@@ -248,9 +248,9 @@ function captcha_html($class="captcha")
if (is_mobile()) $html .= '
';
//$html .= "\n".'

';
$html .= "\n".'

';
+ $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/recaptcha_inv/_common.php b/plugin/recaptcha_inv/_common.php
new file mode 100644
index 000000000..bad54a5d7
--- /dev/null
+++ b/plugin/recaptcha_inv/_common.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/plugin/recaptcha_inv/captcha.lib.php b/plugin/recaptcha_inv/captcha.lib.php
new file mode 100644
index 000000000..9eec82f03
--- /dev/null
+++ b/plugin/recaptcha_inv/captcha.lib.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/plugin/recaptcha_inv/recaptcha.class.php b/plugin/recaptcha_inv/recaptcha.class.php
new file mode 100644
index 000000000..781c13d3c
--- /dev/null
+++ b/plugin/recaptcha_inv/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_inv/recaptcha.js b/plugin/recaptcha_inv/recaptcha.js
new file mode 100644
index 000000000..8aac8389f
--- /dev/null
+++ b/plugin/recaptcha_inv/recaptcha.js
@@ -0,0 +1,20 @@
+function chk_captcha()
+{
+ if ( ! jQuery('#g-recaptcha-response').val()) {
+ grecaptcha.execute();
+ return false;
+ }
+
+ return true;
+}
+
+function recaptcha_validate(token) {
+ var $form = jQuery("#g-recaptcha-response").closest("form"),
+ form_id = $form.attr("id");
+
+
+ if( $form.length ){
+ $form.submit();
+ }
+
+}
diff --git a/plugin/recaptcha_inv/recaptcha.user.lib.php b/plugin/recaptcha_inv/recaptcha.user.lib.php
new file mode 100644
index 000000000..cd6e6bcc4
--- /dev/null
+++ b/plugin/recaptcha_inv/recaptcha.user.lib.php
@@ -0,0 +1,52 @@
+';
+ $html .= '';
+ $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 '
';
+ echo '';
if ($facebook_user) {
echo '
';
echo '';
echo '';
} else {
$facebook_url = $facebook->getLoginUrl(array("redirect_uri"=>G5_SNS_URL."/facebook/callback.php", "scope"=>"publish_stream,read_stream,offline_access", "display"=>"popup"));
-
- echo '
';
- echo '';
echo '';
+ echo '
';
+ echo '';
echo '';
}
echo '';
@@ -90,15 +89,15 @@ if ($config['cf_twitter_key']) {
}
}
- echo '
';
+ echo '';
diff --git a/plugin/social/Hybrid/Auth.php b/plugin/social/Hybrid/Auth.php
new file mode 100644
index 000000000..5642b5091
--- /dev/null
+++ b/plugin/social/Hybrid/Auth.php
@@ -0,0 +1,414 @@
+getSessionData());
+ Hybrid_Logger::info("Hybrid_Auth initialize: check if any error is stored on the endpoint...");
+
+ if (Hybrid_Error::hasError()) {
+ $m = Hybrid_Error::getErrorMessage();
+ $c = Hybrid_Error::getErrorCode();
+ $p = Hybrid_Error::getErrorPrevious();
+
+ Hybrid_Logger::error("Hybrid_Auth initialize: A stored Error found, Throw an new Exception and delete it from the store: Error#$c, '$m'");
+
+ Hybrid_Error::clearError();
+
+ // try to provide the previous if any
+ // Exception::getPrevious (PHP 5 >= 5.3.0) http://php.net/manual/en/exception.getprevious.php
+ if (version_compare(PHP_VERSION, '5.3.0', '>=') && ($p instanceof Exception)) {
+ throw new Exception($m, $c, $p);
+ } else {
+ throw new Exception($m, $c);
+ }
+ }
+
+ Hybrid_Logger::info("Hybrid_Auth initialize: no error found. initialization succeed.");
+ }
+
+ /**
+ * Hybrid storage system accessor
+ *
+ * Users sessions are stored using HybridAuth storage system ( HybridAuth 2.0 handle PHP Session only) and can be accessed directly by
+ * Hybrid_Auth::storage()->get($key) to retrieves the data for the given key, or calling
+ * Hybrid_Auth::storage()->set($key, $value) to store the key => $value set.
+ *
+ * @return Hybrid_Storage
+ */
+ public static function storage() {
+ return Hybrid_Auth::$store;
+ }
+
+ /**
+ * Get hybridauth session data
+ * @return string|null
+ */
+ function getSessionData() {
+ return Hybrid_Auth::storage()->getSessionData();
+ }
+
+ /**
+ * Restore hybridauth session data
+ *
+ * @param string $sessiondata Serialized session data
+ * @retun void
+ */
+ function restoreSessionData($sessiondata = null) {
+ Hybrid_Auth::storage()->restoreSessionData($sessiondata);
+ }
+
+ /**
+ * Try to authenticate the user with a given provider.
+ *
+ * If the user is already connected we just return and instance of provider adapter,
+ * ELSE, try to authenticate and authorize the user with the provider.
+ *
+ * $params is generally an array with required info in order for this provider and HybridAuth to work,
+ * like :
+ * hauth_return_to: URL to call back after authentication is done
+ * openid_identifier: The OpenID identity provider identifier
+ * google_service: can be "Users" for Google user accounts service or "Apps" for Google hosted Apps
+ *
+ * @param string $providerId ID of the provider
+ * @param array $params Params
+ * @return
+ */
+ public static function authenticate($providerId, $params = null) {
+ Hybrid_Logger::info("Enter Hybrid_Auth::authenticate( $providerId )");
+
+ if (!Hybrid_Auth::storage()->get("hauth_session.$providerId.is_logged_in")) {
+ // if user not connected to $providerId then try setup a new adapter and start the login process for this provider
+ Hybrid_Logger::info("Hybrid_Auth::authenticate( $providerId ), User not connected to the provider. Try to authenticate..");
+ $provider_adapter = Hybrid_Auth::setup($providerId, $params);
+ $provider_adapter->login();
+ } else {
+ // else, then return the adapter instance for the given provider
+ Hybrid_Logger::info("Hybrid_Auth::authenticate( $providerId ), User is already connected to this provider. Return the adapter instance.");
+ return Hybrid_Auth::getAdapter($providerId);
+ }
+ }
+
+ /**
+ * Return the adapter instance for an authenticated provider
+ *
+ * @param string $providerId ID of the provider
+ * @return Hybrid_Provider_Adapter
+ */
+ public static function getAdapter($providerId = null) {
+ Hybrid_Logger::info("Enter Hybrid_Auth::getAdapter( $providerId )");
+ return Hybrid_Auth::setup($providerId);
+ }
+
+ /**
+ * Setup an adapter for a given provider
+ *
+ * @param string $providerId ID of the provider
+ * @param array $params Adapter params
+ * @return Hybrid_Provider_Adapter
+ */
+ public static function setup($providerId, $params = null) {
+ Hybrid_Logger::debug("Enter Hybrid_Auth::setup( $providerId )", $params);
+
+ if (!$params) {
+ $params = Hybrid_Auth::storage()->get("hauth_session.$providerId.id_provider_params");
+
+ Hybrid_Logger::debug("Hybrid_Auth::setup( $providerId ), no params given. Trying to get the stored for this provider.", $params);
+ }
+
+ if (!$params) {
+ $params = array();
+ Hybrid_Logger::info("Hybrid_Auth::setup( $providerId ), no stored params found for this provider. Initialize a new one for new session");
+ }
+
+ if (is_array($params) && !isset($params["hauth_return_to"])) {
+ $params["hauth_return_to"] = Hybrid_Auth::getCurrentUrl();
+ Hybrid_Logger::debug("Hybrid_Auth::setup( $providerId ). HybridAuth Callback URL set to: ", $params["hauth_return_to"]);
+ }
+
+ # instantiate a new IDProvider Adapter
+ $provider = new Hybrid_Provider_Adapter();
+ $provider->factory($providerId, $params);
+ return $provider;
+ }
+
+ /**
+ * Check if the current user is connected to a given provider
+ *
+ * @param string $providerId ID of the provider
+ * @return bool
+ */
+ public static function isConnectedWith($providerId) {
+ return (bool) Hybrid_Auth::storage()->get("hauth_session.{$providerId}.is_logged_in");
+ }
+
+ /**
+ * Return array listing all authenticated providers
+ * @return array
+ */
+ public static function getConnectedProviders() {
+ $idps = array();
+
+ foreach (Hybrid_Auth::$config["providers"] as $idpid => $params) {
+ if (Hybrid_Auth::isConnectedWith($idpid)) {
+ $idps[] = $idpid;
+ }
+ }
+
+ return $idps;
+ }
+
+ /**
+ * Return array listing all enabled providers as well as a flag if you are connected
+ *
+ *
+ * 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..8fd9149a1
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Facebook.php
@@ -0,0 +1,334 @@
+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);
+ }
+
+ // redirect uri mismatches when authenticating with Facebook.
+ if (isset($this->config['redirect_uri']) && !empty($this->config['redirect_uri'])) {
+ $this->api->redirect_uri = $this->config['redirect_uri'];
+ }
+ }
+ /**
+ * {@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..1b1130768
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Kakao.php
@@ -0,0 +1,177 @@
+
+ * 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";
+
+ // redirect uri mismatches when authenticating with Kakao.
+ if (isset($this->config['redirect_uri']) && !empty($this->config['redirect_uri'])) {
+ $this->api->redirect_uri = $this->config['redirect_uri'];
+ }
+ }
+
+ /**
+ * 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
+ );
+
+ if( $this->api->client_secret && ($this->api->client_secret !== $this->api->client_id) ){
+ $params['client_secret'] = $this->api->client_secret;
+ }
+
+ $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..431425b43
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Naver.php
@@ -0,0 +1,234 @@
+
+ * 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";
+
+ // redirect uri mismatches when authenticating with Naver.
+ if (isset($this->config['redirect_uri']) && !empty($this->config['redirect_uri'])) {
+ $this->api->redirect_uri = $this->config['redirect_uri'];
+ }
+ }
+
+ /**
+ * 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..c0bb45480
--- /dev/null
+++ b/plugin/social/Hybrid/Providers/Payco.php
@@ -0,0 +1,212 @@
+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);
+ }
+
+ // redirect uri mismatches when authenticating with Payco.
+ if (isset($this->config['redirect_uri']) && !empty($this->config['redirect_uri'])) {
+ $this->api->redirect_uri = $this->config['redirect_uri'];
+ }
+ }
+ /**
+ * {@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;
+ } //end function getUserProfile
+
+}
\ 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 @@
+
+
+
403 Forbidden
+
+
+
+
Directory access is forbidden.
+
+
+
\ No newline at end of file
diff --git a/plugin/social/Hybrid/resources/index.html b/plugin/social/Hybrid/resources/index.html
new file mode 100644
index 000000000..c942a79ce
--- /dev/null
+++ b/plugin/social/Hybrid/resources/index.html
@@ -0,0 +1,10 @@
+
+
+
403 Forbidden
+
+
+
+
Directory access is forbidden.
+
+
+
\ No newline at end of file
diff --git a/plugin/social/Hybrid/resources/openid_policy.html b/plugin/social/Hybrid/resources/openid_policy.html
new file mode 100644
index 000000000..01462daec
--- /dev/null
+++ b/plugin/social/Hybrid/resources/openid_policy.html
@@ -0,0 +1,10 @@
+
+
+
OpenID Policy
+
+
+
+
+
\ No newline at end of file
diff --git a/plugin/social/Hybrid/resources/openid_realm.html b/plugin/social/Hybrid/resources/openid_realm.html
new file mode 100644
index 000000000..78b1b1d62
--- /dev/null
+++ b/plugin/social/Hybrid/resources/openid_realm.html
@@ -0,0 +1,13 @@
+
+
+
HybridAuth Endpoint
+
+
+
+
+
HybridAuth
+ Open Source Social Sign On PHP Library.
+
+
hybridauth.sourceforge.net/
+
+
diff --git a/plugin/social/Hybrid/resources/openid_xrds.xml b/plugin/social/Hybrid/resources/openid_xrds.xml
new file mode 100644
index 000000000..ab94e5c01
--- /dev/null
+++ b/plugin/social/Hybrid/resources/openid_xrds.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ http://specs.openid.net/auth/2.0/return_to
+ {RETURN_TO_URL}
+
+
+
\ No newline at end of file
diff --git a/plugin/social/Hybrid/thirdparty/OAuth/OAuth.php b/plugin/social/Hybrid/thirdparty/OAuth/OAuth.php
new file mode 100644
index 000000000..876665fae
--- /dev/null
+++ b/plugin/social/Hybrid/thirdparty/OAuth/OAuth.php
@@ -0,0 +1,901 @@
+key = $key;
+ $this->secret = $secret;
+ $this->callback_url = $callback_url;
+ }
+
+ function __toString() {
+ return "OAuthConsumer[key=$this->key,secret=$this->secret]";
+ }
+}
+
+class OAuthToken {
+ // access tokens and request tokens
+ public $key;
+ public $secret;
+
+ /**
+ * key = the token
+ * secret = the token secret
+ */
+ function __construct($key, $secret) {
+ $this->key = $key;
+ $this->secret = $secret;
+ }
+
+ /**
+ * generates the basic string serialization of a token that a server
+ * would respond to request_token and access_token calls with
+ */
+ function to_string() {
+ return "oauth_token=" .
+ OAuthUtil::urlencode_rfc3986($this->key) .
+ "&oauth_token_secret=" .
+ OAuthUtil::urlencode_rfc3986($this->secret);
+ }
+
+ function __toString() {
+ return $this->to_string();
+ }
+}
+
+/**
+ * A class for implementing a Signature Method
+ * See section 9 ("Signing Requests") in the spec
+ */
+abstract class OAuthSignatureMethod {
+ /**
+ * Needs to return the name of the Signature Method (ie HMAC-SHA1)
+ * @return string
+ */
+ abstract public function get_name();
+
+ /**
+ * Build up the signature
+ * NOTE: The output of this function MUST NOT be urlencoded.
+ * the encoding is handled in OAuthRequest when the final
+ * request is serialized
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @return string
+ */
+ abstract public function build_signature($request, $consumer, $token);
+
+ /**
+ * Verifies that a given signature is correct
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @param string $signature
+ * @return bool
+ */
+ public function check_signature($request, $consumer, $token, $signature) {
+ $built = $this->build_signature($request, $consumer, $token);
+
+ // Check for zero length, although unlikely here
+ if (strlen($built) == 0 || strlen($signature) == 0) {
+ return false;
+ }
+
+ if (strlen($built) != strlen($signature)) {
+ return false;
+ }
+
+ // Avoid a timing leak with a (hopefully) time insensitive compare
+ $result = 0;
+ for ($i = 0; $i < strlen($signature); $i++) {
+ $result |= ord($built{$i}) ^ ord($signature{$i});
+ }
+
+ return $result == 0;
+ }
+}
+
+/**
+ * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
+ * where the Signature Base String is the text and the key is the concatenated values (each first
+ * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
+ * character (ASCII code 38) even if empty.
+ * - Chapter 9.2 ("HMAC-SHA1")
+ */
+class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
+ function get_name() {
+ return "HMAC-SHA1";
+ }
+
+ public function build_signature($request, $consumer, $token) {
+ $base_string = $request->get_signature_base_string();
+ $request->base_string = $base_string;
+
+ $key_parts = array(
+ $consumer->secret,
+ ($token) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
+ $key = implode('&', $key_parts);
+
+ return base64_encode(hash_hmac('sha1', $base_string, $key, true));
+ }
+}
+
+/**
+ * The PLAINTEXT method does not provide any security protection and SHOULD only be used
+ * over a secure channel such as HTTPS. It does not use the Signature Base String.
+ * - Chapter 9.4 ("PLAINTEXT")
+ */
+class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
+ public function get_name() {
+ return "PLAINTEXT";
+ }
+
+ /**
+ * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
+ * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
+ * empty. The result MUST be encoded again.
+ * - Chapter 9.4.1 ("Generating Signatures")
+ *
+ * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
+ * OAuthRequest handles this!
+ */
+ public function build_signature($request, $consumer, $token) {
+ $key_parts = array(
+ $consumer->secret,
+ ($token) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
+ $key = implode('&', $key_parts);
+ $request->base_string = $key;
+
+ return $key;
+ }
+}
+
+/**
+ * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
+ * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
+ * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
+ * verified way to the Service Provider, in a manner which is beyond the scope of this
+ * specification.
+ * - Chapter 9.3 ("RSA-SHA1")
+ */
+abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
+ public function get_name() {
+ return "RSA-SHA1";
+ }
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
+ // (2) fetch via http using a url provided by the requester
+ // (3) some sort of specific discovery code based on request
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_public_cert(&$request);
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_private_cert(&$request);
+
+ public function build_signature($request, $consumer, $token) {
+ $base_string = $request->get_signature_base_string();
+ $request->base_string = $base_string;
+
+ // Fetch the private key cert based on the request
+ $cert = $this->fetch_private_cert($request);
+
+ // Pull the private key ID from the certificate
+ $privatekeyid = openssl_get_privatekey($cert);
+
+ // Sign using the key
+ $ok = openssl_sign($base_string, $signature, $privatekeyid);
+
+ // Release the key resource
+ openssl_free_key($privatekeyid);
+
+ return base64_encode($signature);
+ }
+
+ public function check_signature($request, $consumer, $token, $signature) {
+ $decoded_sig = base64_decode($signature);
+
+ $base_string = $request->get_signature_base_string();
+
+ // Fetch the public key cert based on the request
+ $cert = $this->fetch_public_cert($request);
+
+ // Pull the public key ID from the certificate
+ $publickeyid = openssl_get_publickey($cert);
+
+ // Check the computed signature against the one passed in the query
+ $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
+
+ // Release the key resource
+ openssl_free_key($publickeyid);
+
+ return $ok == 1;
+ }
+}
+
+class OAuthRequest {
+ protected $parameters;
+ protected $http_method;
+ protected $http_url;
+ // for debug purposes
+ public $base_string;
+ public static $version = '1.0';
+ public static $POST_INPUT = 'php://input';
+
+ function __construct($http_method, $http_url, $parameters=null) {
+ $parameters = ($parameters) ? $parameters : array();
+ $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
+ $this->parameters = $parameters;
+ $this->http_method = $http_method;
+ $this->http_url = $http_url;
+ }
+
+
+ /**
+ * attempt to build up a request from what was passed to the server
+ */
+ public static function from_request($http_method=null, $http_url=null, $parameters=null) {
+ $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
+ ? 'http'
+ : 'https';
+ if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+ $scheme = $_SERVER['HTTP_X_FORWARDED_PROTO'];
+ }
+ $http_url = ($http_url) ? $http_url : $scheme .
+ '://' . $_SERVER['SERVER_NAME'] .
+ ':' .
+ $_SERVER['SERVER_PORT'] .
+ $_SERVER['REQUEST_URI'];
+ $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
+
+ // We weren't handed any parameters, so let's find the ones relevant to
+ // this request.
+ // If you run XML-RPC or similar you should use this to provide your own
+ // parsed parameter-list
+ if (!$parameters) {
+ // Find request headers
+ $request_headers = OAuthUtil::get_headers();
+
+ // Parse the query-string to find GET parameters
+ $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
+
+ // It's a POST request of the proper content-type, so parse POST
+ // parameters and add those overriding any duplicates from GET
+ if ($http_method == "POST"
+ && isset($request_headers['Content-Type'])
+ && strstr($request_headers['Content-Type'],
+ 'application/x-www-form-urlencoded')
+ ) {
+ $post_data = OAuthUtil::parse_parameters(
+ file_get_contents(self::$POST_INPUT)
+ );
+ $parameters = array_merge($parameters, $post_data);
+ }
+
+ // We have a Authorization-header with OAuth data. Parse the header
+ // and add those overriding any duplicates from GET or POST
+ if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
+ $header_parameters = OAuthUtil::split_header(
+ $request_headers['Authorization']
+ );
+ $parameters = array_merge($parameters, $header_parameters);
+ }
+
+ }
+
+ return new OAuthRequest($http_method, $http_url, $parameters);
+ }
+
+ /**
+ * pretty much a helper function to set up the request
+ */
+ public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=null) {
+ $parameters = ($parameters) ? $parameters : array();
+ $defaults = array("oauth_version" => OAuthRequest::$version,
+ "oauth_nonce" => OAuthRequest::generate_nonce(),
+ "oauth_timestamp" => OAuthRequest::generate_timestamp(),
+ "oauth_consumer_key" => $consumer->key);
+ if ($token)
+ $defaults['oauth_token'] = $token->key;
+
+ $parameters = array_merge($defaults, $parameters);
+
+ return new OAuthRequest($http_method, $http_url, $parameters);
+ }
+
+ public function set_parameter($name, $value, $allow_duplicates = true) {
+ if ($allow_duplicates && isset($this->parameters[$name])) {
+ // We have already added parameter(s) with this name, so add to the list
+ if (is_scalar($this->parameters[$name])) {
+ // This is the first duplicate, so transform scalar (string)
+ // into an array so we can add the duplicates
+ $this->parameters[$name] = array($this->parameters[$name]);
+ }
+
+ $this->parameters[$name][] = $value;
+ } else {
+ $this->parameters[$name] = $value;
+ }
+ }
+
+ public function get_parameter($name) {
+ return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
+ }
+
+ public function get_parameters() {
+ return $this->parameters;
+ }
+
+ public function unset_parameter($name) {
+ unset($this->parameters[$name]);
+ }
+
+ /**
+ * The request parameters, sorted and concatenated into a normalized string.
+ * @return string
+ */
+ public function get_signable_parameters() {
+ // Grab all parameters
+ $params = $this->parameters;
+
+ // Remove oauth_signature if present
+ // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
+ if (isset($params['oauth_signature'])) {
+ unset($params['oauth_signature']);
+ }
+
+ return OAuthUtil::build_http_query($params);
+ }
+
+ /**
+ * Returns the base string of this request
+ *
+ * The base string defined as the method, the url
+ * and the parameters (normalized), each urlencoded
+ * and the concated with &.
+ */
+ public function get_signature_base_string() {
+ $parts = array(
+ $this->get_normalized_http_method(),
+ $this->get_normalized_http_url(),
+ $this->get_signable_parameters()
+ );
+
+ $parts = OAuthUtil::urlencode_rfc3986($parts);
+
+ return implode('&', $parts);
+ }
+
+ /**
+ * just uppercases the http method
+ */
+ public function get_normalized_http_method() {
+ return strtoupper($this->http_method);
+ }
+
+ /**
+ * parses the url and rebuilds it to be
+ * scheme://host/path
+ */
+ public function get_normalized_http_url() {
+ $parts = parse_url($this->http_url);
+
+ $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
+ $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
+ $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
+ $path = (isset($parts['path'])) ? $parts['path'] : '';
+
+ if (($scheme == 'https' && $port != '443')
+ || ($scheme == 'http' && $port != '80')) {
+ $host = "$host:$port";
+ }
+ return "$scheme://$host$path";
+ }
+
+ /**
+ * builds a url usable for a GET request
+ */
+ public function to_url() {
+ $post_data = $this->to_postdata();
+ $out = $this->get_normalized_http_url();
+ if ($post_data) {
+ $out .= '?'.$post_data;
+ }
+ return $out;
+ }
+
+ /**
+ * builds the data one would send in a POST request
+ */
+ public function to_postdata() {
+ return OAuthUtil::build_http_query($this->parameters);
+ }
+
+ /**
+ * builds the Authorization: header
+ */
+ public function to_header($realm=null) {
+ $first = true;
+ if($realm) {
+ $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
+ $first = false;
+ } else
+ $out = 'Authorization: OAuth';
+
+ $total = array();
+ foreach ($this->parameters as $k => $v) {
+ if (substr($k, 0, 5) != "oauth") continue;
+ if (is_array($v)) {
+ throw new OAuthException('arrays not supported in headers');
+ }
+ $out .= ($first) ? ' ' : ',';
+ $out .= OAuthUtil::urlencode_rfc3986($k) .
+ '="' .
+ OAuthUtil::urlencode_rfc3986($v) .
+ '"';
+ $first = false;
+ }
+ return $out;
+ }
+
+ public function __toString() {
+ return $this->to_url();
+ }
+
+
+ public function sign_request($signature_method, $consumer, $token) {
+ $this->set_parameter(
+ "oauth_signature_method",
+ $signature_method->get_name(),
+ false
+ );
+ $signature = $this->build_signature($signature_method, $consumer, $token);
+ $this->set_parameter("oauth_signature", $signature, false);
+ }
+
+ public function build_signature($signature_method, $consumer, $token) {
+ $signature = $signature_method->build_signature($this, $consumer, $token);
+ return $signature;
+ }
+
+ /**
+ * util function: current timestamp
+ */
+ private static function generate_timestamp() {
+ return time();
+ }
+
+ /**
+ * util function: current nonce
+ */
+ private static function generate_nonce() {
+ $mt = microtime();
+ $rand = mt_rand();
+
+ return md5($mt . $rand); // md5s look nicer than numbers
+ }
+}
+
+class OAuthServer {
+ protected $timestamp_threshold = 300; // in seconds, five minutes
+ protected $version = '1.0'; // hi blaine
+ protected $signature_methods = array();
+
+ protected $data_store;
+
+ function __construct($data_store) {
+ $this->data_store = $data_store;
+ }
+
+ public function add_signature_method($signature_method) {
+ $this->signature_methods[$signature_method->get_name()] =
+ $signature_method;
+ }
+
+ // high level functions
+
+ /**
+ * process a request_token request
+ * returns the request token on success
+ */
+ public function fetch_request_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // no token required for the initial token request
+ $token = null;
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $callback = $request->get_parameter('oauth_callback');
+ $new_token = $this->data_store->new_request_token($consumer, $callback);
+
+ return $new_token;
+ }
+
+ /**
+ * process an access_token request
+ * returns the access token on success
+ */
+ public function fetch_access_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // requires authorized request token
+ $token = $this->get_token($request, $consumer, "request");
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $verifier = $request->get_parameter('oauth_verifier');
+ $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
+
+ return $new_token;
+ }
+
+ /**
+ * verify an api call, checks all the parameters
+ */
+ public function verify_request(&$request) {
+ $this->get_version($request);
+ $consumer = $this->get_consumer($request);
+ $token = $this->get_token($request, $consumer, "access");
+ $this->check_signature($request, $consumer, $token);
+ return array($consumer, $token);
+ }
+
+ // Internals from here
+ /**
+ * version 1
+ */
+ private function get_version(&$request) {
+ $version = $request->get_parameter("oauth_version");
+ if (!$version) {
+ // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
+ // Chapter 7.0 ("Accessing Protected Ressources")
+ $version = '1.0';
+ }
+ if ($version !== $this->version) {
+ throw new OAuthException("OAuth version '$version' not supported");
+ }
+ return $version;
+ }
+
+ /**
+ * figure out the signature with some defaults
+ */
+ private function get_signature_method($request) {
+ $signature_method = $request instanceof OAuthRequest
+ ? $request->get_parameter("oauth_signature_method")
+ : null;
+
+ if (!$signature_method) {
+ // According to chapter 7 ("Accessing Protected Ressources") the signature-method
+ // parameter is required, and we can't just fallback to PLAINTEXT
+ throw new OAuthException('No signature method parameter. This parameter is required');
+ }
+
+ if (!in_array($signature_method,
+ array_keys($this->signature_methods))) {
+ throw new OAuthException(
+ "Signature method '$signature_method' not supported " .
+ "try one of the following: " .
+ implode(", ", array_keys($this->signature_methods))
+ );
+ }
+ return $this->signature_methods[$signature_method];
+ }
+
+ /**
+ * try to find the consumer for the provided request's consumer key
+ */
+ private function get_consumer($request) {
+ $consumer_key = $request instanceof OAuthRequest
+ ? $request->get_parameter("oauth_consumer_key")
+ : null;
+
+ if (!$consumer_key) {
+ throw new OAuthException("Invalid consumer key");
+ }
+
+ $consumer = $this->data_store->lookup_consumer($consumer_key);
+ if (!$consumer) {
+ throw new OAuthException("Invalid consumer");
+ }
+
+ return $consumer;
+ }
+
+ /**
+ * try to find the token for the provided request's token key
+ */
+ private function get_token($request, $consumer, $token_type="access") {
+ $token_field = $request instanceof OAuthRequest
+ ? $request->get_parameter('oauth_token')
+ : null;
+
+ $token = $this->data_store->lookup_token(
+ $consumer, $token_type, $token_field
+ );
+ if (!$token) {
+ throw new OAuthException("Invalid $token_type token: $token_field");
+ }
+ return $token;
+ }
+
+ /**
+ * all-in-one function to check the signature on a request
+ * should guess the signature method appropriately
+ */
+ private function check_signature($request, $consumer, $token) {
+ // this should probably be in a different method
+ $timestamp = $request instanceof OAuthRequest
+ ? $request->get_parameter('oauth_timestamp')
+ : null;
+ $nonce = $request instanceof OAuthRequest
+ ? $request->get_parameter('oauth_nonce')
+ : null;
+
+ $this->check_timestamp($timestamp);
+ $this->check_nonce($consumer, $token, $nonce, $timestamp);
+
+ $signature_method = $this->get_signature_method($request);
+
+ $signature = $request->get_parameter('oauth_signature');
+ $valid_sig = $signature_method->check_signature(
+ $request,
+ $consumer,
+ $token,
+ $signature
+ );
+
+ if (!$valid_sig) {
+ throw new OAuthException("Invalid signature");
+ }
+ }
+
+ /**
+ * check that the timestamp is new enough
+ */
+ private function check_timestamp($timestamp) {
+ if( ! $timestamp )
+ throw new OAuthException(
+ 'Missing timestamp parameter. The parameter is required'
+ );
+
+ // verify that timestamp is recentish
+ $now = time();
+ if (abs($now - $timestamp) > $this->timestamp_threshold) {
+ throw new OAuthException(
+ "Expired timestamp, yours $timestamp, ours $now"
+ );
+ }
+ }
+
+ /**
+ * check that the nonce is not repeated
+ */
+ private function check_nonce($consumer, $token, $nonce, $timestamp) {
+ if( ! $nonce )
+ throw new OAuthException(
+ 'Missing nonce parameter. The parameter is required'
+ );
+
+ // verify that the nonce is uniqueish
+ $found = $this->data_store->lookup_nonce(
+ $consumer,
+ $token,
+ $nonce,
+ $timestamp
+ );
+ if ($found) {
+ throw new OAuthException("Nonce already used: $nonce");
+ }
+ }
+
+}
+
+class OAuthDataStore {
+ function lookup_consumer($consumer_key) {
+ // implement me
+ }
+
+ function lookup_token($consumer, $token_type, $token) {
+ // implement me
+ }
+
+ function lookup_nonce($consumer, $token, $nonce, $timestamp) {
+ // implement me
+ }
+
+ function new_request_token($consumer, $callback = null) {
+ // return a new token attached to this consumer
+ }
+
+ function new_access_token($token, $consumer, $verifier = null) {
+ // return a new access token attached to this consumer
+ // for the user associated with this token if the request token
+ // is authorized
+ // should also invalidate the request token
+ }
+
+}
+
+class OAuthUtil {
+ public static function urlencode_rfc3986($input) {
+ if (is_array($input)) {
+ return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
+ } else if (is_scalar($input)) {
+ return str_replace(
+ '+',
+ ' ',
+ str_replace('%7E', '~', rawurlencode($input))
+ );
+ } else {
+ return '';
+ }
+}
+
+
+ // This decode function isn't taking into consideration the above
+ // modifications to the encoding process. However, this method doesn't
+ // seem to be used anywhere so leaving it as is.
+ public static function urldecode_rfc3986($string) {
+ return urldecode($string);
+ }
+
+ // Utility function for turning the Authorization: header into
+ // parameters, has to do some unescaping
+ // Can filter out any non-oauth parameters if needed (default behaviour)
+ // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
+ // see http://code.google.com/p/oauth/issues/detail?id=163
+ public static function split_header($header, $only_allow_oauth_parameters = true) {
+ $params = array();
+ if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) {
+ foreach ($matches[1] as $i => $h) {
+ $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
+ }
+ if (isset($params['realm'])) {
+ unset($params['realm']);
+ }
+ }
+ return $params;
+ }
+
+ // helper to try to sort out headers for people who aren't running apache
+ public static function get_headers() {
+ if (function_exists('apache_request_headers')) {
+ // we need this to get the actual Authorization: header
+ // because apache tends to tell us it doesn't exist
+ $headers = apache_request_headers();
+
+ // sanitize the output of apache_request_headers because
+ // we always want the keys to be Cased-Like-This and arh()
+ // returns the headers in the same case as they are in the
+ // request
+ $out = array();
+ foreach ($headers AS $key => $value) {
+ $key = str_replace(
+ " ",
+ "-",
+ ucwords(strtolower(str_replace("-", " ", $key)))
+ );
+ $out[$key] = $value;
+ }
+ } else {
+ // otherwise we don't have apache and are just going to have to hope
+ // that $_SERVER actually contains what we need
+ $out = array();
+ if( isset($_SERVER['CONTENT_TYPE']) )
+ $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
+ if( isset($_ENV['CONTENT_TYPE']) )
+ $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
+
+ foreach ($_SERVER as $key => $value) {
+ if (substr($key, 0, 5) == "HTTP_") {
+ // this is chaos, basically it is just there to capitalize the first
+ // letter of every word that is not an initial HTTP and strip HTTP
+ // code from przemek
+ $key = str_replace(
+ " ",
+ "-",
+ ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
+ );
+ $out[$key] = $value;
+ }
+ }
+ }
+ return $out;
+ }
+
+ // This function takes a input like a=b&a=c&d=e and returns the parsed
+ // parameters like this
+ // array('a' => array('b','c'), 'd' => 'e')
+ public static function parse_parameters( $input ) {
+ if (!isset($input) || !$input) return array();
+
+ $pairs = explode('&', $input);
+
+ $parsed_parameters = array();
+ foreach ($pairs as $pair) {
+ $split = explode('=', $pair, 2);
+ $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
+ $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
+
+ if (isset($parsed_parameters[$parameter])) {
+ // We have already recieved parameter(s) with this name, so add to the list
+ // of parameters with this name
+
+ if (is_scalar($parsed_parameters[$parameter])) {
+ // This is the first duplicate, so transform scalar (string) into an array
+ // so we can add the duplicates
+ $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
+ }
+
+ $parsed_parameters[$parameter][] = $value;
+ } else {
+ $parsed_parameters[$parameter] = $value;
+ }
+ }
+ return $parsed_parameters;
+ }
+
+ public static function build_http_query($params) {
+ if (!$params) return '';
+
+ // Urlencode both keys and values
+ $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
+ $values = OAuthUtil::urlencode_rfc3986(array_values($params));
+ $params = array_combine($keys, $values);
+
+ // Parameters are sorted by name, using lexicographical byte value ordering.
+ // Ref: Spec: 9.1.1 (1)
+ uksort($params, 'strcmp');
+
+ $pairs = array();
+ foreach ($params as $parameter => $value) {
+ if (is_array($value)) {
+ // If two or more parameters share the same name, they are sorted by their value
+ // Ref: Spec: 9.1.1 (1)
+ // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
+ sort($value, SORT_STRING);
+ foreach ($value as $duplicate_value) {
+ $pairs[] = $parameter . '=' . $duplicate_value;
+ }
+ } else {
+ $pairs[] = $parameter . '=' . $value;
+ }
+ }
+ // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
+ // Each name-value pair is separated by an '&' character (ASCII code 38)
+ return implode('&', $pairs);
+ }
+}
diff --git a/plugin/social/Hybrid/thirdparty/OAuth/OAuth1Client.php b/plugin/social/Hybrid/thirdparty/OAuth/OAuth1Client.php
new file mode 100644
index 000000000..64c03c87d
--- /dev/null
+++ b/plugin/social/Hybrid/thirdparty/OAuth/OAuth1Client.php
@@ -0,0 +1,264 @@
+sha1_method = new OAuthSignatureMethod_HMAC_SHA1();
+ $this->consumer = new OAuthConsumer( $consumer_key, $consumer_secret );
+ $this->token = null;
+
+ if ( $oauth_token && $oauth_token_secret ){
+ $this->token = new OAuthConsumer( $oauth_token, $oauth_token_secret );
+ }
+ }
+
+ /**
+ * Build authorize url
+ *
+ * @return string
+ */
+ function authorizeUrl( $token, $extras =array() )
+ {
+ if ( is_array( $token ) ){
+ $token = $token['oauth_token'];
+ }
+
+ $parameters = array( "oauth_token" => $token );
+
+ if( count($extras) )
+ foreach( $extras as $k=>$v )
+ $parameters[$k] = $v;
+
+ return $this->authorize_url . "?" . http_build_query( $parameters );
+ }
+
+ /**
+ * Get a request_token from provider
+ *
+ * @return array a key/value array containing oauth_token and oauth_token_secret
+ */
+ function requestToken( $callback = null )
+ {
+ $parameters = array();
+
+ if ( $callback ) {
+ $this->redirect_uri = $parameters['oauth_callback'] = $callback;
+ }
+
+ $request = $this->signedRequest( $this->request_token_url, $this->request_token_method, $parameters );
+ $token = OAuthUtil::parse_parameters( $request );
+ $this->token = new OAuthConsumer( $token['oauth_token'], $token['oauth_token_secret'] );
+
+ return $token;
+ }
+
+ /**
+ * Exchange the request token and secret for an access token and secret, to sign API calls.
+ *
+ * @return array array('oauth_token' => the access token, 'oauth_token_secret' => the access secret)
+ */
+ function accessToken( $oauth_verifier = false, $oauth_token = false )
+ {
+ $parameters = array();
+
+ // 1.0a
+ if ( $oauth_verifier ) {
+ $parameters['oauth_verifier'] = $oauth_verifier;
+ }
+
+ $request = $this->signedRequest( $this->access_token_url, $this->access_token_method, $parameters );
+ $token = OAuthUtil::parse_parameters( $request );
+ $this->token = new OAuthConsumer( $token['oauth_token'], $token['oauth_token_secret'] );
+
+ return $token;
+ }
+
+ /**
+ * GET wrapper for provider apis request
+ */
+ function get($url, $parameters = array(), $content_type = null)
+ {
+ return $this->api($url, 'GET', $parameters, null, $content_type);
+ }
+
+ /**
+ * POST wrapper for provider apis request
+ */
+ function post($url, $parameters = array(), $body = null, $content_type = null, $multipart = false)
+ {
+ return $this->api($url, 'POST', $parameters, $body, $content_type, $multipart );
+ }
+
+ /**
+ * Format and sign an oauth for provider api
+ */
+ function api( $url, $method = 'GET', $parameters = array(), $body = null, $content_type = null, $multipart = false )
+ {
+ if ( strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0 ) {
+ $url = $this->api_base_url . $url;
+ }
+
+ $response = $this->signedRequest( $url, $method, $parameters, $body, $content_type, $multipart );
+
+ if( $this->decode_json ){
+ $response = json_decode( $response );
+ }
+
+ return $this->response = $response;
+ }
+
+ /**
+ * Return the response object afer the fact
+ *
+ * @return mixed
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Make signed request
+ */
+ function signedRequest( $url, $method, $parameters, $body = null, $content_type = null, $multipart = false )
+ {
+
+ $signature_parameters = array();
+
+ // when making a multipart request, use only oauth_* keys for signature
+ foreach( $parameters AS $key => $value ){
+ if( !$multipart || strpos( $key, 'oauth_' ) === 0 ){
+ $signature_parameters[$key] = $value;
+ }
+ }
+
+ $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $signature_parameters);
+ $request->sign_request($this->sha1_method, $this->consumer, $this->token);
+ switch ($method) {
+ case 'GET': return $this->request( $request->to_url(), 'GET', null, null, $content_type );
+ default :
+ if ($body)
+ return $this->request( $request->to_url(), $method, $body, $request->to_header(), $content_type );
+ else
+ return $this->request( $request->get_normalized_http_url(), $method, ($multipart ? $parameters : $request->to_postdata()), $request->to_header(), $content_type, $multipart ) ;
+ }
+ }
+
+ /**
+ * Make http request
+ */
+ function request( $url, $method, $postfields = null, $auth_header = null, $content_type = null, $multipart = false )
+ {
+ Hybrid_Logger::info( "Enter OAuth1Client::request( $method, $url )" );
+ Hybrid_Logger::debug( "OAuth1Client::request(). dump post fields: ", serialize( $postfields ) );
+
+ $this->http_info = array();
+ $ci = curl_init();
+
+ /* Curl settings */
+ curl_setopt( $ci, CURLOPT_USERAGENT , $this->curl_useragent );
+ curl_setopt( $ci, CURLOPT_CONNECTTIMEOUT, $this->curl_connect_time_out );
+ curl_setopt( $ci, CURLOPT_TIMEOUT , $this->curl_time_out );
+ curl_setopt( $ci, CURLOPT_RETURNTRANSFER, true );
+ curl_setopt( $ci, CURLOPT_HTTPHEADER , array('Expect:') );
+ curl_setopt( $ci, CURLOPT_SSL_VERIFYPEER, $this->curl_ssl_verifypeer );
+ curl_setopt( $ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader') );
+ curl_setopt( $ci, CURLOPT_HEADER , false );
+
+ if( $multipart ){
+ curl_setopt( $ci, CURLOPT_HTTPHEADER, array( 'Expect:', $auth_header ) );
+
+ }elseif ($content_type)
+ curl_setopt( $ci, CURLOPT_HTTPHEADER, array('Expect:', "Content-Type: $content_type") );
+
+ if($this->curl_proxy){
+ curl_setopt( $ci, CURLOPT_PROXY , $this->curl_proxy);
+ }
+
+ switch ($method){
+ case 'POST':
+ curl_setopt( $ci, CURLOPT_POST, true );
+
+ if ( !empty($postfields) ){
+ curl_setopt( $ci, CURLOPT_POSTFIELDS, $postfields );
+ }
+
+ if ( !empty($auth_header) && $this->curl_auth_header && !$multipart ){
+ curl_setopt( $ci, CURLOPT_HTTPHEADER, array( 'Content-Type: application/atom+xml', $auth_header ) );
+ }
+ break;
+ case 'DELETE':
+ curl_setopt( $ci, CURLOPT_CUSTOMREQUEST, 'DELETE' );
+ if ( !empty($postfields) ){
+ $url = "{$url}?{$postfields}";
+ }
+ }
+
+ curl_setopt($ci, CURLOPT_URL, $url);
+ $response = curl_exec($ci);
+ if( $response === false ) {
+ Hybrid_Logger::error( "OAuth1Client::request(). curl_exec error: ", curl_error($ci) );
+ }
+
+
+ Hybrid_Logger::debug( "OAuth1Client::request(). dump request info: ", serialize( curl_getinfo($ci) ) );
+ Hybrid_Logger::debug( "OAuth1Client::request(). dump request result: ", serialize( $response ) );
+
+ $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
+ $this->http_info = array_merge($this->http_info, curl_getinfo($ci));
+
+ curl_close ($ci);
+
+ return $response;
+ }
+
+ /**
+ * Get the header info to store.
+ */
+ function getHeader($ch, $header) {
+ $i = strpos($header, ':');
+
+ if ( !empty($i) ){
+ $key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
+ $value = trim(substr($header, $i + 2));
+ $this->http_header[$key] = $value;
+ }
+
+ return strlen($header);
+ }
+}
diff --git a/plugin/social/Hybrid/thirdparty/OAuth/OAuth2Client.php b/plugin/social/Hybrid/thirdparty/OAuth/OAuth2Client.php
new file mode 100644
index 000000000..0046d2c58
--- /dev/null
+++ b/plugin/social/Hybrid/thirdparty/OAuth/OAuth2Client.php
@@ -0,0 +1,302 @@
+client_id = $client_id;
+ $this->client_secret = $client_secret;
+ $this->redirect_uri = $redirect_uri;
+ $this->curl_compressed = $compressed;
+ }
+
+ public function authorizeUrl( $extras = array() )
+ {
+ $params = array(
+ "client_id" => $this->client_id,
+ "redirect_uri" => $this->redirect_uri,
+ "response_type" => "code"
+ );
+
+ if( count($extras) )
+ foreach( $extras as $k=>$v )
+ $params[$k] = $v;
+
+ return $this->authorize_url . "?" . http_build_query($params, '', '&');
+ }
+
+ public function authenticate( $code )
+ {
+ $params = array(
+ "client_id" => $this->client_id,
+ "client_secret" => $this->client_secret,
+ "grant_type" => "authorization_code",
+ "redirect_uri" => $this->redirect_uri,
+ "code" => $code
+ );
+
+ $response = $this->request( $this->token_url, $params, $this->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->access_token = $response->access_token;
+ if( isset( $response->refresh_token ) ) $this->refresh_token = $response->refresh_token;
+ if( isset( $response->expires_in ) ) $this->access_token_expires_in = $response->expires_in;
+
+ // calculate when the access token expire
+ if( isset($response->expires_in)) {
+ $this->access_token_expires_at = time() + $response->expires_in;
+ }
+
+ return $response;
+ }
+
+ public function authenticated()
+ {
+ if ( $this->access_token ){
+ if ( $this->token_info_url && $this->refresh_token ){
+ // check if this access token has expired,
+ $tokeninfo = $this->tokenInfo( $this->access_token );
+
+ // if yes, access_token has expired, then ask for a new one
+ if( $tokeninfo && isset( $tokeninfo->error ) ){
+ $response = $this->refreshToken( $this->refresh_token );
+
+ // if wrong response
+ if( ! isset( $response->access_token ) || ! $response->access_token ){
+ throw new Exception( "The Authorization Service has return an invalid response while requesting a new access token. given up!" );
+ }
+
+ // set new access_token
+ $this->access_token = $response->access_token;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Format and sign an oauth for provider api
+ */
+ public function api( $url, $method = "GET", $parameters = array(), $decode_json = true )
+ {
+ if ( strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0 ) {
+ $url = $this->api_base_url . $url;
+ }
+
+ $parameters[$this->sign_token_name] = $this->access_token;
+ $response = null;
+
+ switch( $method ){
+ case 'GET' : $response = $this->request( $url, $parameters, "GET" ); break;
+ case 'POST' : $response = $this->request( $url, $parameters, "POST" ); break;
+ case 'DELETE' : $response = $this->request( $url, $parameters, "DELETE" ); break;
+ case 'PATCH' : $response = $this->request( $url, $parameters, "PATCH" ); break;
+ }
+
+ if( $response && $decode_json ){
+ return $this->response = json_decode( $response );
+ }
+
+ return $this->response = $response;
+ }
+
+ /**
+ * Return the response object afer the fact
+ *
+ * @return mixed
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * GET wrapper for provider apis request
+ */
+ function get( $url, $parameters = array(), $decode_json = true )
+ {
+ return $this->api( $url, 'GET', $parameters, $decode_json );
+ }
+
+ /**
+ * POST wrapper for provider apis request
+ */
+ function post( $url, $parameters = array(), $decode_json = true )
+ {
+ return $this->api( $url, 'POST', $parameters, $decode_json );
+ }
+
+ // -- tokens
+
+ public function tokenInfo($accesstoken)
+ {
+ $params['access_token'] = $this->access_token;
+ $response = $this->request( $this->token_info_url, $params );
+ return $this->parseRequestResult( $response );
+ }
+
+ public function refreshToken( $parameters = array() )
+ {
+ $params = array(
+ "client_id" => $this->client_id,
+ "client_secret" => $this->client_secret,
+ "grant_type" => "refresh_token"
+ );
+
+ foreach($parameters as $k=>$v ){
+ $params[$k] = $v;
+ }
+
+ $response = $this->request( $this->token_url, $params, "POST" );
+ return $this->parseRequestResult( $response );
+ }
+
+ // -- utilities
+
+ private function request( $url, $params=false, $type="GET" )
+ {
+ Hybrid_Logger::info( "Enter OAuth2Client::request( $url )" );
+ Hybrid_Logger::debug( "OAuth2Client::request(). dump request params: ", serialize( $params ) );
+
+ $urlEncodedParams = http_build_query($params, '', '&');
+
+ if( $type == "GET" ){
+ $url = $url . ( strpos( $url, '?' ) ? '&' : '?' ) . $urlEncodedParams;
+ }
+
+ $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->curl_time_out );
+ curl_setopt($ch, CURLOPT_USERAGENT , $this->curl_useragent );
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out );
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , $this->curl_ssl_verifypeer );
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST , $this->curl_ssl_verifyhost );
+ curl_setopt($ch, CURLOPT_HTTPHEADER , $this->curl_header );
+
+ if ($this->curl_compressed){
+ curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate");
+ }
+
+ if($this->curl_proxy){
+ curl_setopt( $ch, CURLOPT_PROXY , $this->curl_proxy);
+ }
+
+ if ($type == "POST") {
+ curl_setopt($ch, CURLOPT_POST, 1);
+
+ // If request body exists then encode it for "application/json".
+ if (isset($params['body'])) {
+ $urlEncodedParams = json_encode($params['body']);
+ }
+
+ // Using URL encoded params here instead of a more convenient array
+ // cURL will set a wrong HTTP Content-Type header if using an array (cf. http://www.php.net/manual/en/function.curl-setopt.php, Notes section for "CURLOPT_POSTFIELDS")
+ // OAuth requires application/x-www-form-urlencoded Content-Type (cf. https://tools.ietf.org/html/rfc6749#section-2.3.1)
+ if ($params) {
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $urlEncodedParams);
+ }
+ }
+
+ if( $type == "DELETE" ){
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
+ }
+ if( $type == "PATCH" ){
+ curl_setopt($ch, CURLOPT_POST, 1);
+ if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
+ }
+ $response = curl_exec($ch);
+ if( $response === false ) {
+ Hybrid_Logger::error( "OAuth2Client::request(). curl_exec error: ", curl_error($ch) );
+ }
+ 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, $output );
+
+ $result = new StdClass();
+
+ foreach( $output as $k => $v )
+ $result->$k = $v;
+
+ return $result;
+ }
+ /**
+ * DELETE wrapper for provider apis request
+ */
+ function delete( $url, $parameters = array() )
+ {
+ return $this->api( $url, 'DELETE', $parameters );
+ }
+ /**
+ * PATCH wrapper for provider apis request
+ */
+ function patch( $url, $parameters = array() )
+ {
+ return $this->api( $url, 'PATCH', $parameters );
+ }
+}
diff --git a/plugin/social/Hybrid/thirdparty/OpenID/LightOpenID.php b/plugin/social/Hybrid/thirdparty/OpenID/LightOpenID.php
new file mode 100644
index 000000000..b1cb41bb3
--- /dev/null
+++ b/plugin/social/Hybrid/thirdparty/OpenID/LightOpenID.php
@@ -0,0 +1,1051 @@
+= 5.1.2 with cURL or HTTP/HTTPS stream wrappers enabled.
+ *
+ * @version v1.2.0 (2014-01-14)
+ * @link https://code.google.com/p/lightopenid/ Project URL
+ * @link https://github.com/iignatov/LightOpenID GitHub Repo
+ * @author Mewp
+ * @copyright Copyright (c) 2013 Mewp
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ */
+class LightOpenID
+{
+ public $returnUrl
+ , $required = array()
+ , $optional = array()
+ , $verify_peer = null
+ , $capath = null
+ , $cainfo = null
+ , $cnmatch = null
+ , $data
+ , $oauth = array()
+ , $curl_time_out = 30
+ , $curl_connect_time_out = 30;
+ private $identity, $claimed_id;
+ protected $server, $version, $trustRoot, $aliases, $identifier_select = false
+ , $ax = false, $sreg = false, $setup_url = null, $headers = array()
+ , $proxy = null, $user_agent = 'LightOpenID'
+ , $xrds_override_pattern = null, $xrds_override_replacement = null;
+ static protected $ax_to_sreg = array(
+ 'namePerson/friendly' => 'nickname',
+ 'contact/email' => 'email',
+ 'namePerson' => 'fullname',
+ 'birthDate' => 'dob',
+ 'person/gender' => 'gender',
+ 'contact/postalCode/home' => 'postcode',
+ 'contact/country/home' => 'country',
+ 'pref/language' => 'language',
+ 'pref/timezone' => 'timezone',
+ );
+
+ function __construct($host, $proxy = null)
+ {
+ $this->set_realm($host);
+ $this->set_proxy($proxy);
+
+ $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?');
+ $this->returnUrl = $this->trustRoot . $uri;
+
+ $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
+
+ if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) {
+ throw new ErrorException('You must have either https wrappers or curl enabled.');
+ }
+ }
+
+ function __isset($name)
+ {
+ return in_array($name, array('identity', 'trustRoot', 'realm', 'xrdsOverride', 'mode'));
+ }
+
+ function __set($name, $value)
+ {
+ switch ($name) {
+ case 'identity':
+ if (strlen($value = trim((String) $value))) {
+ if (preg_match('#^xri:/*#i', $value, $m)) {
+ $value = substr($value, strlen($m[0]));
+ } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
+ $value = "http://$value";
+ }
+ if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
+ $value .= '/';
+ }
+ }
+ $this->$name = $this->claimed_id = $value;
+ break;
+ case 'trustRoot':
+ case 'realm':
+ $this->trustRoot = trim($value);
+ break;
+ case 'xrdsOverride':
+ if (is_array($value)) {
+ list($pattern, $replacement) = $value;
+ $this->xrds_override_pattern = $pattern;
+ $this->xrds_override_replacement = $replacement;
+ } else {
+ trigger_error('Invalid value specified for "xrdsOverride".', E_USER_ERROR);
+ }
+ break;
+ }
+ }
+
+ function __get($name)
+ {
+ switch ($name) {
+ case 'identity':
+ # We return claimed_id instead of identity,
+ # because the developer should see the claimed identifier,
+ # i.e. what he set as identity, not the op-local identifier (which is what we verify)
+ return $this->claimed_id;
+ case 'trustRoot':
+ case 'realm':
+ return $this->trustRoot;
+ case 'mode':
+ return empty($this->data['openid_mode']) ? null : $this->data['openid_mode'];
+ }
+ }
+
+ function set_proxy($proxy)
+ {
+ if (!empty($proxy)) {
+ // When the proxy is a string - try to parse it.
+ if (!is_array($proxy)) {
+ $proxy = parse_url($proxy);
+ }
+
+ // Check if $proxy is valid after the parsing.
+ if ($proxy && !empty($proxy['host'])) {
+ // Make sure that a valid port number is specified.
+ if (array_key_exists('port', $proxy)) {
+ if (!is_int($proxy['port'])) {
+ $proxy['port'] = is_numeric($proxy['port']) ? intval($proxy['port']) : 0;
+ }
+
+ if ($proxy['port'] <= 0) {
+ throw new ErrorException('The specified proxy port number is invalid.');
+ }
+ }
+
+ $this->proxy = $proxy;
+ }
+ }
+ }
+
+ /**
+ * Checks if the server specified in the url exists.
+ *
+ * @param $url url to check
+ * @return true, if the server exists; false otherwise
+ */
+ function hostExists($url)
+ {
+ if (strpos($url, '/') === false) {
+ $server = $url;
+ } else {
+ $server = @parse_url($url, PHP_URL_HOST);
+ }
+
+ if (!$server) {
+ return false;
+ }
+
+ return !!gethostbynamel($server);
+ }
+
+ protected function set_realm($uri)
+ {
+ $realm = '';
+
+ # Set a protocol, if not specified.
+ $realm .= (($offset = strpos($uri, '://')) === false) ? $this->get_realm_protocol() : '';
+
+ # Set the offset properly.
+ $offset = (($offset !== false) ? $offset + 3 : 0);
+
+ # Get only the root, without the path.
+ $realm .= (($end = strpos($uri, '/', $offset)) === false) ? $uri : substr($uri, 0, $end);
+
+ $this->trustRoot = $realm;
+ }
+
+ protected function get_realm_protocol()
+ {
+ if (!empty($_SERVER['HTTPS'])) {
+ $use_secure_protocol = ($_SERVER['HTTPS'] != 'off');
+ } else if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+ $use_secure_protocol = ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https');
+ } else {
+ $use_secure_protocol = false;
+ }
+
+ return $use_secure_protocol ? 'https://' : 'http://';
+ }
+
+ protected function request_curl($url, $method='GET', $params=array(), $update_claimed_id)
+ {
+ $params = http_build_query($params, '', '&');
+ $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_TIMEOUT, $this->curl_time_out);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out);
+
+
+ if ($method == 'POST') {
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
+ } else {
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*'));
+ }
+
+ if (!empty($this->proxy)) {
+ curl_setopt($curl, CURLOPT_PROXY, $this->proxy['host']);
+
+ if (!empty($this->proxy['port'])) {
+ curl_setopt($curl, CURLOPT_PROXYPORT, $this->proxy['port']);
+ }
+
+ if (!empty($this->proxy['user'])) {
+ curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
+ }
+ }
+
+ if($this->verify_peer !== null) {
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
+ if($this->capath) {
+ curl_setopt($curl, CURLOPT_CAPATH, $this->capath);
+ }
+
+ if($this->cainfo) {
+ curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
+ }
+ }
+
+ if ($method == 'POST') {
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
+ } elseif ($method == 'HEAD') {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_NOBODY, true);
+ } else {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ }
+ $response = curl_exec($curl);
+
+ if($method == 'HEAD' && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 405) {
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ $response = curl_exec($curl);
+ $response = substr($response, 0, strpos($response, "\r\n\r\n"));
+ }
+
+ if($method == 'HEAD' || $method == 'GET') {
+ $header_response = $response;
+
+ # If it's a GET request, we want to only parse the header part.
+ if($method == 'GET') {
+ $header_response = substr($response, 0, strpos($response, "\r\n\r\n"));
+ }
+
+ $headers = array();
+ foreach(explode("\n", $header_response) as $header) {
+ $pos = strpos($header,':');
+ if ($pos !== false) {
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+ }
+ }
+
+ if($update_claimed_id) {
+ # Update the claimed_id value in case of redirections.
+ $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
+ # Ignore the fragment (some cURL versions don't handle it well).
+ if (strtok($effective_url, '#') != strtok($url, '#')) {
+ $this->identity = $this->claimed_id = $effective_url;
+ }
+ }
+
+ if($method == 'HEAD') {
+ return $headers;
+ } else {
+ $this->headers = $headers;
+ }
+ }
+
+ if (curl_errno($curl)) {
+ throw new ErrorException(curl_error($curl), curl_errno($curl));
+ }
+
+ return $response;
+ }
+
+ protected function parse_header_array($array, $update_claimed_id)
+ {
+ $headers = array();
+ foreach($array as $header) {
+ $pos = strpos($header,':');
+ if ($pos !== false) {
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+
+ # Following possible redirections. The point is just to have
+ # claimed_id change with them, because the redirections
+ # are followed automatically.
+ # We ignore redirections with relative paths.
+ # If any known provider uses them, file a bug report.
+ if($name == 'location' && $update_claimed_id) {
+ if(strpos($headers[$name], 'http') === 0) {
+ $this->identity = $this->claimed_id = $headers[$name];
+ } elseif($headers[$name][0] == '/') {
+ $parsed_url = parse_url($this->claimed_id);
+ $this->identity =
+ $this->claimed_id = $parsed_url['scheme'] . '://'
+ . $parsed_url['host']
+ . $headers[$name];
+ }
+ }
+ }
+ }
+ return $headers;
+ }
+
+ protected function request_streams($url, $method='GET', $params=array(), $update_claimed_id)
+ {
+ if(!$this->hostExists($url)) {
+ throw new ErrorException("Could not connect to $url.", 404);
+ }
+
+ if (empty($this->cnmatch)) {
+ $this->cnmatch = parse_url($url, PHP_URL_HOST);
+ }
+
+ $params = http_build_query($params, '', '&');
+ switch($method) {
+ case 'GET':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'user_agent' => $this->user_agent,
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'CN_match' => $this->cnmatch
+ )
+ );
+ $url = $url . ($params ? '?' . $params : '');
+ if (!empty($this->proxy)) {
+ $opts['http']['proxy'] = $this->proxy_url();
+ }
+ break;
+ case 'POST':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'header' => 'Content-type: application/x-www-form-urlencoded',
+ 'user_agent' => $this->user_agent,
+ 'content' => $params,
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'CN_match' => $this->cnmatch
+ )
+ );
+ if (!empty($this->proxy)) {
+ $opts['http']['proxy'] = $this->proxy_url();
+ }
+ break;
+ case 'HEAD':
+ // We want to send a HEAD request, but since get_headers() doesn't
+ // accept $context parameter, we have to change the defaults.
+ $default = stream_context_get_options(stream_context_get_default());
+
+ // PHP does not reset all options. Instead, it just sets the options
+ // available in the passed array, therefore set the defaults manually.
+ $default += array(
+ 'http' => array(),
+ 'ssl' => array()
+ );
+ $default['http'] += array(
+ 'method' => 'GET',
+ 'header' => '',
+ 'user_agent' => '',
+ 'ignore_errors' => false
+ );
+ $default['ssl'] += array(
+ 'CN_match' => ''
+ );
+
+ $opts = array(
+ 'http' => array(
+ 'method' => 'HEAD',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'user_agent' => $this->user_agent,
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'CN_match' => $this->cnmatch
+ )
+ );
+
+ // Enable validation of the SSL certificates.
+ if ($this->verify_peer) {
+ $default['ssl'] += array(
+ 'verify_peer' => false,
+ 'capath' => '',
+ 'cafile' => ''
+ );
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo
+ );
+ }
+
+ // Change the stream context options.
+ stream_context_get_default($opts);
+
+ $headers = get_headers($url . ($params ? '?' . $params : ''));
+
+ // Restore the stream context options.
+ stream_context_get_default($default);
+
+ if (!empty($headers)) {
+ if (intval(substr($headers[0], strlen('HTTP/1.1 '))) == 405) {
+ // The server doesn't support HEAD - emulate it with a GET.
+ $args = func_get_args();
+ $args[1] = 'GET';
+ call_user_func_array(array($this, 'request_streams'), $args);
+ $headers = $this->headers;
+ } else {
+ $headers = $this->parse_header_array($headers, $update_claimed_id);
+ }
+ } else {
+ $headers = array();
+ }
+
+ return $headers;
+ }
+
+ if ($this->verify_peer) {
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo
+ );
+ }
+
+ $context = stream_context_create ($opts);
+ $data = file_get_contents($url, false, $context);
+ # This is a hack for providers who don't support HEAD requests.
+ # It just creates the headers array for the last request in $this->headers.
+ if(isset($http_response_header)) {
+ $this->headers = $this->parse_header_array($http_response_header, $update_claimed_id);
+ }
+
+ return $data;
+ }
+
+ protected function request($url, $method='GET', $params=array(), $update_claimed_id=false)
+ {
+ $use_curl = false;
+
+ if (function_exists('curl_init')) {
+ if (!$use_curl) {
+ # When allow_url_fopen is disabled, PHP streams will not work.
+ $use_curl = !ini_get('allow_url_fopen');
+ }
+
+ if (!$use_curl) {
+ # When there is no HTTPS wrapper, PHP streams cannott be used.
+ $use_curl = !in_array('https', stream_get_wrappers());
+ }
+
+ if (!$use_curl) {
+ # With open_basedir or safe_mode set, cURL can't follow redirects.
+ $use_curl = !(ini_get('safe_mode') || ini_get('open_basedir'));
+ }
+ }
+
+ return
+ $use_curl
+ ? $this->request_curl($url, $method, $params, $update_claimed_id)
+ : $this->request_streams($url, $method, $params, $update_claimed_id);
+ }
+
+ protected function proxy_url()
+ {
+ $result = '';
+
+ if (!empty($this->proxy)) {
+ $result = $this->proxy['host'];
+
+ if (!empty($this->proxy['port'])) {
+ $result = $result . ':' . $this->proxy['port'];
+ }
+
+ if (!empty($this->proxy['user'])) {
+ $result = $this->proxy['user'] . ':' . $this->proxy['pass'] . '@' . $result;
+ }
+
+ $result = 'http://' . $result;
+ }
+
+ return $result;
+ }
+
+ protected function build_url($url, $parts)
+ {
+ if (isset($url['query'], $parts['query'])) {
+ $parts['query'] = $url['query'] . '&' . $parts['query'];
+ }
+
+ $url = $parts + $url;
+ $url = $url['scheme'] . '://'
+ . (empty($url['username'])?''
+ :(empty($url['password'])? "{$url['username']}@"
+ :"{$url['username']}:{$url['password']}@"))
+ . $url['host']
+ . (empty($url['port'])?'':":{$url['port']}")
+ . (empty($url['path'])?'':$url['path'])
+ . (empty($url['query'])?'':"?{$url['query']}")
+ . (empty($url['fragment'])?'':"#{$url['fragment']}");
+ return $url;
+ }
+
+ /**
+ * Helper function used to scan for / tags and extract information
+ * from them
+ */
+ protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName)
+ {
+ preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1);
+ preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2);
+
+ $result = array_merge($matches1[1], $matches2[1]);
+ return empty($result)?false:$result[0];
+ }
+
+ /**
+ * Performs Yadis and HTML discovery. Normally not used.
+ * @param $url Identity URL.
+ * @return String OP Endpoint (i.e. OpenID provider address).
+ * @throws ErrorException
+ */
+ function discover($url)
+ {
+ if (!$url) throw new ErrorException('No identity supplied.');
+ # Use xri.net proxy to resolve i-name identities
+ if (!preg_match('#^https?:#', $url)) {
+ $url = "https://xri.net/$url";
+ }
+
+ # We save the original url in case of Yadis discovery failure.
+ # It can happen when we'll be lead to an XRDS document
+ # which does not have any OpenID2 services.
+ $originalUrl = $url;
+
+ # A flag to disable yadis discovery in case of failure in headers.
+ $yadis = true;
+
+ # Allows optional regex replacement of the URL, e.g. to use Google Apps
+ # as an OpenID provider without setting up XRDS on the domain hosting.
+ if (!is_null($this->xrds_override_pattern) && !is_null($this->xrds_override_replacement)) {
+ $url = preg_replace($this->xrds_override_pattern, $this->xrds_override_replacement, $url);
+ }
+
+ # We'll jump a maximum of 5 times, to avoid endless redirections.
+ for ($i = 0; $i < 5; $i ++) {
+ if ($yadis) {
+ $headers = $this->request($url, 'HEAD', array(), true);
+
+ $next = false;
+ if (isset($headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location'])));
+ $next = true;
+ }
+
+ if (isset($headers['content-type']) && $this->is_allowed_type($headers['content-type'])) {
+ # Found an XRDS document, now let's find the server, and optionally delegate.
+ $content = $this->request($url, 'GET');
+
+ preg_match_all('#(.*?)#s', $content, $m);
+ foreach($m[1] as $content) {
+ $content = ' ' . $content; # The space is added, so that strpos doesn't return 0.
+
+ # OpenID 2
+ $ns = preg_quote('http://specs.openid.net/auth/2.0/', '#');
+ if(preg_match('#\s*'.$ns.'(server|signon)\s*#s', $content, $type)) {
+ if ($type[1] == 'server') $this->identifier_select = true;
+
+ preg_match('#(.*)#', $content, $server);
+ preg_match('#<(Local|Canonical)ID>(.*)\1ID>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # Does the server advertise support for either AX or SREG?
+ $this->ax = (bool) strpos($content, 'http://openid.net/srv/ax/1.0');
+ $this->sreg = strpos($content, 'http://openid.net/sreg/1.0')
+ || strpos($content, 'http://openid.net/extensions/sreg/1.1');
+
+ $server = $server[1];
+ if (isset($delegate[2])) $this->identity = trim($delegate[2]);
+ $this->version = 2;
+
+ $this->server = $server;
+ return $server;
+ }
+
+ # OpenID 1.1
+ $ns = preg_quote('http://openid.net/signon/1.1', '#');
+ if (preg_match('#\s*'.$ns.'\s*#s', $content)) {
+
+ preg_match('#(.*)#', $content, $server);
+ preg_match('#<.*?Delegate>(.*)#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # AX can be used only with OpenID 2.0, so checking only SREG
+ $this->sreg = strpos($content, 'http://openid.net/sreg/1.0')
+ || strpos($content, 'http://openid.net/extensions/sreg/1.1');
+
+ $server = $server[1];
+ if (isset($delegate[1])) $this->identity = $delegate[1];
+ $this->version = 1;
+
+ $this->server = $server;
+ return $server;
+ }
+ }
+
+ $next = true;
+ $yadis = false;
+ $url = $originalUrl;
+ $content = null;
+ break;
+ }
+ if ($next) continue;
+
+ # There are no relevant information in headers, so we search the body.
+ $content = $this->request($url, 'GET', array(), true);
+
+ if (isset($this->headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($this->headers['x-xrds-location'])));
+ continue;
+ }
+
+ $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
+ if ($location) {
+ $url = $this->build_url(parse_url($url), parse_url($location));
+ continue;
+ }
+ }
+
+ if (!$content) $content = $this->request($url, 'GET');
+
+ # At this point, the YADIS Discovery has failed, so we'll switch
+ # to openid2 HTML discovery, then fallback to openid 1.1 discovery.
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href');
+ $this->version = 2;
+
+ if (!$server) {
+ # The same with openid 1.1
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href');
+ $this->version = 1;
+ }
+
+ if ($server) {
+ # We found an OpenID2 OP Endpoint
+ if ($delegate) {
+ # We have also found an OP-Local ID.
+ $this->identity = $delegate;
+ }
+ $this->server = $server;
+ return $server;
+ }
+
+ throw new ErrorException("No OpenID Server found at $url", 404);
+ }
+ throw new ErrorException('Endless redirection!', 500);
+ }
+
+ protected function is_allowed_type($content_type) {
+ # Apparently, some providers return XRDS documents as text/html.
+ # While it is against the spec, allowing this here shouldn't break
+ # compatibility with anything.
+ $allowed_types = array('application/xrds+xml', 'text/html', 'text/xml');
+
+ foreach ($allowed_types as $type) {
+ if (strpos($content_type, $type) !== false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function sregParams()
+ {
+ $params = array();
+ # We always use SREG 1.1, even if the server is advertising only support for 1.0.
+ # That's because it's fully backwards compatibile with 1.0, and some providers
+ # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
+ $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
+ if ($this->required) {
+ $params['openid.sreg.required'] = array();
+ foreach ($this->required as $required) {
+ if (!isset(self::$ax_to_sreg[$required])) continue;
+ $params['openid.sreg.required'][] = self::$ax_to_sreg[$required];
+ }
+ $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']);
+ }
+
+ if ($this->optional) {
+ $params['openid.sreg.optional'] = array();
+ foreach ($this->optional as $optional) {
+ if (!isset(self::$ax_to_sreg[$optional])) continue;
+ $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional];
+ }
+ $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']);
+ }
+ return $params;
+ }
+
+ protected function axParams()
+ {
+ $params = array();
+ if ($this->required || $this->optional) {
+ $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
+ $params['openid.ax.mode'] = 'fetch_request';
+ $this->aliases = array();
+ $counts = array();
+ $required = array();
+ $optional = array();
+ foreach (array('required','optional') as $type) {
+ foreach ($this->$type as $alias => $field) {
+ if (is_int($alias)) $alias = strtr($field, '/', '_');
+ $this->aliases[$alias] = 'http://axschema.org/' . $field;
+ if (empty($counts[$alias])) $counts[$alias] = 0;
+ $counts[$alias] += 1;
+ ${$type}[] = $alias;
+ }
+ }
+ foreach ($this->aliases as $alias => $ns) {
+ $params['openid.ax.type.' . $alias] = $ns;
+ }
+ foreach ($counts as $alias => $count) {
+ if ($count == 1) continue;
+ $params['openid.ax.count.' . $alias] = $count;
+ }
+
+ # Don't send empty ax.requied and ax.if_available.
+ # Google and possibly other providers refuse to support ax when one of these is empty.
+ if($required) {
+ $params['openid.ax.required'] = implode(',', $required);
+ }
+ if($optional) {
+ $params['openid.ax.if_available'] = implode(',', $optional);
+ }
+ }
+ return $params;
+ }
+
+ protected function authUrl_v1($immediate)
+ {
+ $returnUrl = $this->returnUrl;
+ # If we have an openid.delegate that is different from our claimed id,
+ # we need to somehow preserve the claimed id between requests.
+ # The simplest way is to just send it along with the return_to url.
+ if($this->identity != $this->claimed_id) {
+ $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ $params = array(
+ 'openid.return_to' => $returnUrl,
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.identity' => $this->identity,
+ 'openid.trust_root' => $this->trustRoot,
+ ) + $this->sregParams();
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ protected function authUrl_v2($immediate)
+ {
+ $params = array(
+ 'openid.ns' => 'http://specs.openid.net/auth/2.0',
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.return_to' => $this->returnUrl,
+ 'openid.realm' => $this->trustRoot,
+ );
+
+ if ($this->ax) {
+ $params += $this->axParams();
+ }
+
+ if ($this->sreg) {
+ $params += $this->sregParams();
+ }
+
+ if (!$this->ax && !$this->sreg) {
+ # If OP doesn't advertise either SREG, nor AX, let's send them both
+ # in worst case we don't get anything in return.
+ $params += $this->axParams() + $this->sregParams();
+ }
+
+ if (!empty($this->oauth) && is_array($this->oauth)) {
+ $params['openid.ns.oauth'] = 'http://specs.openid.net/extensions/oauth/1.0';
+ $params['openid.oauth.consumer'] = str_replace(array('http://', 'https://'), '', $this->trustRoot);
+ $params['openid.oauth.scope'] = implode(' ', $this->oauth);
+ }
+
+ if ($this->identifier_select) {
+ $params['openid.identity'] = $params['openid.claimed_id']
+ = 'http://specs.openid.net/auth/2.0/identifier_select';
+ } else {
+ $params['openid.identity'] = $this->identity;
+ $params['openid.claimed_id'] = $this->claimed_id;
+ }
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ /**
+ * Returns authentication url. Usually, you want to redirect your user to it.
+ * @return String The authentication url.
+ * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1.
+ * @throws ErrorException
+ */
+ function authUrl($immediate = false)
+ {
+ if ($this->setup_url && !$immediate) return $this->setup_url;
+ if (!$this->server) $this->discover($this->identity);
+
+ if ($this->version == 2) {
+ return $this->authUrl_v2($immediate);
+ }
+ return $this->authUrl_v1($immediate);
+ }
+
+ /**
+ * Performs OpenID verification with the OP.
+ * @return Bool Whether the verification was successful.
+ * @throws ErrorException
+ */
+ function validate()
+ {
+ # If the request was using immediate mode, a failure may be reported
+ # by presenting user_setup_url (for 1.1) or reporting
+ # mode 'setup_needed' (for 2.0). Also catching all modes other than
+ # id_res, in order to avoid throwing errors.
+ if(isset($this->data['openid_user_setup_url'])) {
+ $this->setup_url = $this->data['openid_user_setup_url'];
+ return false;
+ }
+ if($this->mode != 'id_res') {
+ return false;
+ }
+
+ $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity'];
+ $params = array(
+ 'openid.assoc_handle' => $this->data['openid_assoc_handle'],
+ 'openid.signed' => $this->data['openid_signed'],
+ 'openid.sig' => $this->data['openid_sig'],
+ );
+
+ if (isset($this->data['openid_ns'])) {
+ # We're dealing with an OpenID 2.0 server, so let's set an ns
+ # Even though we should know location of the endpoint,
+ # we still need to verify it by discovery, so $server is not set here
+ $params['openid.ns'] = 'http://specs.openid.net/auth/2.0';
+ } elseif (isset($this->data['openid_claimed_id'])
+ && $this->data['openid_claimed_id'] != $this->data['openid_identity']
+ ) {
+ # If it's an OpenID 1 provider, and we've got claimed_id,
+ # we have to append it to the returnUrl, like authUrl_v1 does.
+ $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?')
+ . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ if ($this->data['openid_return_to'] != $this->returnUrl) {
+ # The return_to url must match the url of current request.
+ # I'm assuing that noone will set the returnUrl to something that doesn't make sense.
+ return false;
+ }
+
+ $server = $this->discover($this->claimed_id);
+
+ foreach (explode(',', $this->data['openid_signed']) as $item) {
+ # Checking whether magic_quotes_gpc is turned on, because
+ # the function may fail if it is. For example, when fetching
+ # AX namePerson, it might containg an apostrophe, which will be escaped.
+ # In such case, validation would fail, since we'd send different data than OP
+ # wants to verify. stripslashes() should solve that problem, but we can't
+ # use it when magic_quotes is off.
+ $value = $this->data['openid_' . str_replace('.','_',$item)];
+ $params['openid.' . $item] = function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() ? stripslashes($value) : $value;
+
+ }
+
+ $params['openid.mode'] = 'check_authentication';
+
+ $response = $this->request($server, 'POST', $params);
+
+ return preg_match('/is_valid\s*:\s*true/i', $response);
+ }
+
+ protected function getAxAttributes()
+ {
+ $result = array();
+
+ if ($alias = $this->getNamespaceAlias('http://openid.net/srv/ax/1.0', 'ax')) {
+ $prefix = 'openid_' . $alias;
+ $length = strlen('http://axschema.org/');
+
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = $alias . '.type.';
+
+ if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) {
+ continue;
+ }
+
+ $key = substr($key, strlen($keyMatch));
+ $idv = $prefix . '_value_' . $key;
+ $idc = $prefix . '_count_' . $key;
+ $key = substr($this->getItem($prefix . '_type_' . $key), $length);
+
+ if (!empty($key)) {
+ if (($count = intval($this->getItem($idc))) > 0) {
+ $value = array();
+
+ for ($i = 1; $i <= $count; $i++) {
+ $value[] = $this->getItem($idv . '_' . $i);
+ }
+
+ $value = ($count == 1) ? reset($value) : $value;
+ } else {
+ $value = $this->getItem($idv);
+ }
+
+ if (!is_null($value)) {
+ $result[$key] = $value;
+ }
+ }
+ }
+ } else {
+ // No alias for the AX schema has been found,
+ // so there is no AX data in the OP's response.
+ }
+
+ return $result;
+ }
+
+ protected function getSregAttributes()
+ {
+ $attributes = array();
+ $sreg_to_ax = array_flip(self::$ax_to_sreg);
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = 'sreg.';
+ if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) {
+ continue;
+ }
+ $key = substr($key, strlen($keyMatch));
+ if (!isset($sreg_to_ax[$key])) {
+ # The field name isn't part of the SREG spec, so we ignore it.
+ continue;
+ }
+ $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key];
+ }
+ return $attributes;
+ }
+
+ /**
+ * Gets AX/SREG attributes provided by OP. should be used only after successful validaton.
+ * Note that it does not guarantee that any of the required/optional parameters will be present,
+ * or that there will be no other attributes besides those specified.
+ * In other words. OP may provide whatever information it wants to.
+ * * SREG names will be mapped to AX names.
+ * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email'
+ * @see http://www.axschema.org/types/
+ */
+ function getAttributes()
+ {
+ if (isset($this->data['openid_ns'])
+ && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0'
+ ) { # OpenID 2.0
+ # We search for both AX and SREG attributes, with AX taking precedence.
+ return $this->getAxAttributes() + $this->getSregAttributes();
+ }
+ return $this->getSregAttributes();
+ }
+
+ /**
+ * Gets an OAuth request token if the OpenID+OAuth hybrid protocol has been used.
+ *
+ * In order to use the OpenID+OAuth hybrid protocol, you need to add at least one
+ * scope to the $openid->oauth array before you get the call to getAuthUrl(), e.g.:
+ * $openid->oauth[] = 'https://www.googleapis.com/auth/plus.me';
+ *
+ * Furthermore the registered consumer name must fit the OpenID realm.
+ * To register an OpenID consumer at Google use: https://www.google.com/accounts/ManageDomains
+ *
+ * @return string|bool OAuth request token on success, FALSE if no token was provided.
+ */
+ function getOAuthRequestToken()
+ {
+ $alias = $this->getNamespaceAlias('http://specs.openid.net/extensions/oauth/1.0');
+
+ return !empty($alias) ? $this->data['openid_' . $alias . '_request_token'] : false;
+ }
+
+ /**
+ * Gets the alias for the specified namespace, if it's present.
+ *
+ * @param string $namespace The namespace for which an alias is needed.
+ * @param string $hint Common alias of this namespace, used for optimization.
+ * @return string|null The namespace alias if found, otherwise - NULL.
+ */
+ private function getNamespaceAlias($namespace, $hint = null)
+ {
+ $result = null;
+
+ if (empty($hint) || $this->getItem('openid_ns_' . $hint) != $namespace) {
+ // The common alias is either undefined or points to
+ // some other extension - search for another alias..
+ $prefix = 'openid_ns_';
+ $length = strlen($prefix);
+
+ foreach ($this->data as $key => $val) {
+ if (strncmp($key, $prefix, $length) === 0 && $val === $namespace) {
+ $result = trim(substr($key, $length));
+ break;
+ }
+ }
+ } else {
+ $result = $hint;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Gets an item from the $data array by the specified id.
+ *
+ * @param string $id The id of the desired item.
+ * @return string|null The item if found, otherwise - NULL.
+ */
+ private function getItem($id)
+ {
+ return isset($this->data[$id]) ? $this->data[$id] : null;
+ }
+}
diff --git a/plugin/social/Hybrid/thirdparty/index.html b/plugin/social/Hybrid/thirdparty/index.html
new file mode 100644
index 000000000..c942a79ce
--- /dev/null
+++ b/plugin/social/Hybrid/thirdparty/index.html
@@ -0,0 +1,10 @@
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
\ No newline at end of file
diff --git a/plugin/social/_common.php b/plugin/social/_common.php
new file mode 100644
index 000000000..bad54a5d7
--- /dev/null
+++ b/plugin/social/_common.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/plugin/social/config.php b/plugin/social/config.php
new file mode 100644
index 000000000..c6816b840
--- /dev/null
+++ b/plugin/social/config.php
@@ -0,0 +1,63 @@
+ "http://localhost/hybridauth-git/hybridauth/",
+ "providers" => array(
+ // openid providers
+ "OpenID" => array(
+ "enabled" => true,
+ ),
+ "Yahoo" => array(
+ "enabled" => true,
+ "keys" => array("id" => "", "secret" => ""),
+ ),
+ "AOL" => array(
+ "enabled" => true,
+ ),
+ "Google" => array(
+ "enabled" => true,
+ "keys" => array("id" => "", "secret" => ""),
+ ),
+ "Facebook" => array(
+ "enabled" => true,
+ "keys" => array("id" => "", "secret" => ""),
+ "trustForwarded" => false,
+ ),
+ "Twitter" => array(
+ "enabled" => true,
+ "keys" => array("key" => "", "secret" => ""),
+ "includeEmail" => false,
+ ),
+ // windows live
+ "Live" => array(
+ "enabled" => true,
+ "keys" => array("id" => "", "secret" => ""),
+ ),
+ "LinkedIn" => array(
+ "enabled" => true,
+ "keys" => array("id" => "", "secret" => ""),
+ "fields" => array(),
+ ),
+ "Foursquare" => array(
+ "enabled" => true,
+ "keys" => array("id" => "", "secret" => ""),
+ ),
+ ),
+ // If you want to enable logging, set 'debug_mode' to true.
+ // You can also set it to
+ // - "error" To log only error messages. Useful in production
+ // - "info" To log info and error messages (ignore debug messages)
+ "debug_mode" => false,
+ // Path to file writable by the web server. Required if 'debug_mode' is not false
+ "debug_file" => "",
+);
diff --git a/plugin/social/error.php b/plugin/social/error.php
new file mode 100644
index 000000000..13aa20acf
--- /dev/null
+++ b/plugin/social/error.php
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+ 소셜 로그인 -
+
+
+
+
+
+
+
Error :
+
+
+ Error:
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugin/social/img/loading_icon.gif b/plugin/social/img/loading_icon.gif
new file mode 100644
index 000000000..3fcf2bd78
Binary files /dev/null and b/plugin/social/img/loading_icon.gif differ
diff --git a/plugin/social/includes/functions.php b/plugin/social/includes/functions.php
new file mode 100644
index 000000000..a44dbd2d5
--- /dev/null
+++ b/plugin/social/includes/functions.php
@@ -0,0 +1,1031 @@
+getSessionData();
+
+ if( defined('G5_SOCIAL_LOGIN_START_PARAM') && G5_SOCIAL_LOGIN_START_PARAM === 'hauth.start' && G5_SOCIAL_LOGIN_DONE_PARAM === 'hauth.done' ){
+ return $g5['hybrid_auth']->authenticate($provider);
+ }
+
+ $base_url = G5_SOCIAL_LOGIN_BASE_URL;
+ $hauth_time = time();
+
+ $connect_data = array(
+ 'login_start' => $base_url . ( strpos($base_url, '?') ? '&' : '?' ) . G5_SOCIAL_LOGIN_START_PARAM.'='.$provider.'&hauth.time='.$hauth_time,
+ 'login_done' => $base_url . ( strpos($base_url, '?') ? '&' : '?' ) . G5_SOCIAL_LOGIN_DONE_PARAM.'='.$provider,
+ );
+
+ return $g5['hybrid_auth']->authenticate($provider, $connect_data);
+}
+
+function social_before_join_check($url=''){
+ global $g5, $config;
+
+ if( $provider_name = social_get_request_provider() ){
+ //재가입 방지
+ if( $user_profile = social_session_exists_check() ){
+
+ $sql = sprintf("select * from {$g5['social_profile_table']} where provider = '%s' and identifier = '%s' ", $provider_name, $user_profile->identifier);
+
+ $is_exist = false;
+
+ $row = sql_fetch($sql);
+
+ if( $row['provider'] ){
+ $is_exist = true;
+
+ $time = time() - (86400 * (int) G5_SOCIAL_DELETE_DAY);
+
+ if( empty($row['mb_id']) && ( 0 == G5_SOCIAL_DELETE_DAY || strtotime($row['mp_latest_day']) < $time) ){
+
+ $sql = "delete from {$g5['social_profile_table']} where mp_no =".$row['mp_no'];
+
+ sql_query($sql);
+
+ $is_exist = false;
+ }
+ }
+
+ if( $is_exist ){
+ $msg = sprintf("해당 %s ID 로 연결 또는 가입된 내역이 있기 때문에 다시 가입할수 없습니다. 회원이시면 로그인 후 정보 수정에서 계정 연결을 해 주세요.", social_get_provider_service_name($provider_name) );
+
+ $url = $url ? $url : G5_URL;
+ alert($msg, $url);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+function social_get_data($by='provider', $provider, $user_profile){
+ global $g5;
+
+ // 소셜 가입이 되어 있는지 체크
+ if( $by == 'provider' ){
+
+ $sql = sprintf("select * from {$g5['social_profile_table']} where provider = '%s' and identifier = '%s' order by mb_id desc ", $provider, $user_profile->identifier);
+
+ $row = sql_fetch($sql);
+
+ if( !empty($row['mb_id']) ){
+ return $row; //mb_id 가 있는 경우에만 데이터를 리턴합니다.
+ }
+
+ return false;
+
+ } else if ( $by == 'member' ){ // 아이디 또는 이메일이나 별명으로 이미 가입되어 있는지 체크
+
+ $email = ($user_profile->emailVerified) ? $user_profile->emailVerified : $user_profile->email;
+ $sid = preg_match("/[^0-9a-z_]+/i", "", $user_profile->sid);
+ $nick = social_relace_nick($user_profile->displayName);
+ if( !$nick ){
+ $tmp = explode("@", $email);
+ $nick = $tmp[0];
+ }
+
+ $sql = "select mb_nick, mb_email from {$g5['member_table']} where mb_nick = '".$nick."' ";
+
+ if( !empty($email) ){
+ $sql .= sprintf(" or mb_email = '%s' ", $email);
+ }
+
+ $result = sql_query($sql);
+
+ $exists = array();
+
+ while($row=sql_fetch_array($result)){
+ if($row['mb_nick'] && $row['mb_nick'] == $nick){
+ $exists['mb_nick'] = $nick;
+ }
+ if($row['mb_email'] && $row['mb_email'] == $email){
+ $exists['mb_email'] = $email;
+ }
+ }
+
+ return $exists;
+
+ }
+
+ return null;
+}
+
+function social_user_profile_replace( $mb_id, $provider, $profile ){
+ global $g5;
+
+ if( !$mb_id )
+ return;
+
+ // $profile 에 성별, 나이, 생일 등의 정보가 포함되어 있습니다.
+
+ //받아온 정보를 암호화 하여
+ $object_sha = sha1( serialize( $profile ) );
+
+ $provider = strtolower($provider);
+
+ $sql = sprintf("SELECT mp_no, mb_id from {$g5['social_profile_table']} where provider= '%s' and identifier = '%s' ", $provider, $profile->identifier);
+ $result = sql_query($sql);
+ for($i=0;$row=sql_fetch_array($result);$i++){ //혹시 맞지 않는 데이터가 있으면 삭제합니다.
+ if( $row['mb_id'] != $mb_id ){
+ sql_query(sprintf("DELETE FROM {$g5['social_profile_table']} where mp_no=%d", $row['mp_no']));
+ }
+ }
+
+ $sql = sprintf("SELECT mp_no, object_sha, mp_register_day from {$g5['social_profile_table']} where mb_id= '%s' and provider= '%s' and identifier = '%s' ", $mb_id, $provider, $profile->identifier);
+
+ $row = sql_fetch($sql);
+
+ $table_data = array(
+ "mp_no" => ! empty($row) ? $row['mp_no'] : 'NULL',
+ 'mb_id' => "'". $mb_id. "'",
+ 'provider' => "'". $provider . "'",
+ 'object_sha' => "'". $object_sha . "'",
+ 'mp_register_day' => ! empty($row) ? "'".$row['mp_register_day']."'" : "'". G5_TIME_YMDHIS . "'",
+ 'mp_latest_day' => "'". G5_TIME_YMDHIS . "'",
+ );
+
+ $fields = array(
+ 'identifier',
+ 'profileurl',
+ 'photourl',
+ 'displayname',
+ 'description',
+ );
+
+ foreach( (array) $profile as $key => $value ){
+ $key = strtolower($key);
+
+ if( in_array( $key, $fields ) )
+ {
+ $value = (string) $value;
+ $table_data[ $key ] = "'". sql_real_escape_string($value). "'";
+ }
+ }
+
+ $fields = '`' . implode( '`, `', array_keys( $table_data ) ) . '`';
+ $values = implode( ", ", array_values( $table_data ) );
+
+ $sql = "REPLACE INTO {$g5['social_profile_table']} ($fields) VALUES ($values) ";
+
+ sql_query($sql);
+
+ return sql_insert_id();
+
+}
+
+function social_build_provider_config($provider){
+ $setting = array(
+ 'base_url' => https_url(G5_PLUGIN_DIR.'/'.G5_SOCIAL_LOGIN_DIR).'/',
+ 'providers' => array(
+ $provider => array(
+ 'enabled' => true,
+ 'keys' => array( 'id' => null, 'key' => null, 'secret' => null )
+ )
+ ),
+ );
+
+ if( function_exists('social_extends_get_keys') ){
+ $setting["providers"][$provider] = social_extends_get_keys($provider);
+ }
+
+ return $setting;
+}
+
+function social_extends_get_keys($provider){
+
+ global $config;
+
+ static $r = array();
+
+ if ( empty($r) ) {
+
+ // Naver
+ $r['Naver'] = array(
+ "enabled" => option_array_checked('naver', $config['cf_social_servicelist']) ? true : false,
+ "redirect_uri" => get_social_callbackurl('naver'),
+ "keys" => array(
+ "id" => $config['cf_naver_clientid'],
+ "secret" => $config['cf_naver_secret'],
+ ),
+ );
+
+ // Kakao
+ $r['Kakao'] = array(
+ "enabled" => option_array_checked('kakao', $config['cf_social_servicelist']) ? true : false,
+ "keys" => array("id" => $config['cf_kakao_rest_key'],
+ "secret" => $config['cf_kakao_client_secret'] ? $config['cf_kakao_client_secret'] : $config['cf_kakao_rest_key']
+ ),
+ "redirect_uri" => get_social_callbackurl('kakao')
+ );
+
+ // Facebook
+ $r['Facebook'] = array(
+ "enabled" => option_array_checked('facebook', $config['cf_social_servicelist']) ? true : false,
+ "keys" => array("id" => $config['cf_facebook_appid'], "secret" => $config['cf_facebook_secret']),
+ "display" => "popup",
+ "redirect_uri" => get_social_callbackurl('facebook'),
+ "trustForwarded" => false
+ );
+
+ // Google
+ $r['Google'] = array(
+ "enabled" => option_array_checked('google', $config['cf_social_servicelist']) ? true : false,
+ "keys" => array("id" => $config['cf_google_clientid'],
+ "secret" => $config['cf_google_secret']),
+ "redirect_uri" => get_social_callbackurl('google'),
+ //"access_type" => "offline", // optional
+ //"approval_prompt" => "force", // optional
+ );
+
+ // Twitter
+ $r['Twitter'] = array(
+ "enabled" => option_array_checked('twitter', $config['cf_social_servicelist']) ? true : false,
+ "keys" => array("key" => $config['cf_twitter_key'], "secret" => $config['cf_twitter_secret']),
+ "redirect_uri" => get_social_callbackurl('twitter'),
+ "trustForwarded" => false
+ );
+
+ // Payco
+ $r['Payco'] = array(
+ "enabled" => option_array_checked('payco', $config['cf_social_servicelist']) ? true : false,
+ "keys" => array("id" => $config['cf_payco_clientid'], "secret" => $config['cf_payco_secret']),
+ "redirect_uri" => get_social_callbackurl('payco'),
+ "trustForwarded" => false
+ );
+ }
+
+ return $r[$provider];
+}
+
+function social_escape_request($request){
+ return clean_xss_tags( strip_tags($request) );
+}
+
+function social_get_request_provider(){
+ $provider_name = isset($_REQUEST['provider']) ? ucfirst(social_escape_request($_REQUEST['provider'])) : '';
+
+ return $provider_name;
+}
+
+function social_login_session_clear($mycf=0){
+ $_SESSION["HA::STORE"] = array(); // used by hybridauth library. to clear as soon as the auth process ends.
+ $_SESSION["HA::CONFIG"] = array(); // used by hybridauth library. to clear as soon as the auth process ends.
+ set_session('sl_userprofile', '');
+ set_session('social_login_redirect', '');
+ if(!$mycf){
+ set_session('ss_social_provider', '');
+ }
+}
+
+function social_session_exists_check(){
+
+ $provider_name = social_get_request_provider();
+
+ if(!$provider_name){
+ return false;
+ }
+
+ if( $provider_name && isset($_SESSION['HA::STORE']['hauth_session.'.strtolower($provider_name).'.is_logged_in']) && !empty($_SESSION['sl_userprofile'][$provider_name]) ){
+ return json_decode($_SESSION['sl_userprofile'][$provider_name]);
+ }
+
+ return false;
+}
+
+function social_relace_nick($nick=''){
+
+ if( empty($nick) ) return '';
+
+ return preg_replace("/[ #\&\+\-%@=\/\\\:;,\.'\"\^`~\_|\!\?\*$#<>()\[\]\{\}]/i", "", $nick);
+}
+
+function social_get_error_msg($type){
+ ob_start();
+
+ switch( $type ){
+ case 0 : echo "지정되지 않은 오류입니다."; break;
+ case 1 : echo "설정 오류입니다."; break;
+ case 2 : echo "해당 provider 설정 오류입니다."; break;
+ case 3 : echo "알수 없거나 비활성화 된 provider 입니다."; break;
+ case 4 : echo "해당 서비스에 접근할수 있는 권한이 없습니다."; break;
+ case 5 : echo "인증이 실패되었습니다.. "
+ . "사용자가 인증을 취소했거나, 공급자가 연결을 거부했습니다.";
+ break;
+ case 6 : echo "사용자 프로필 요청이 실패했습니다.사용자가 해당 서비스에 연결되어 있지 않을 경우도 있습니다. "
+ . "이 경우 다시 인증 요청을 해야 합니다.";
+ break;
+ case 7 : echo "사용자가 해당 서비스에 연결되어 있지 않습니다.";
+ break;
+ case 8 : echo "해당 서비스가 기능을 지원하지 않습니다."; break;
+ }
+
+ $get_error = ob_get_clean();
+
+ return $get_error;
+}
+
+if( !function_exists('replaceQueryParams') ){
+ function replaceQueryParams($url, $params)
+ {
+ $query = parse_url($url, PHP_URL_QUERY);
+ parse_str($query, $oldParams);
+
+ if (empty($oldParams)) {
+ return rtrim($url, '?') . '?' . http_build_query($params);
+ }
+
+ $params = array_merge($oldParams, $params);
+
+ return preg_replace('#\?.*#', '?' . http_build_query($params), $url);
+ }
+}
+
+function social_loading_provider_page( $provider ){
+
+ social_login_session_clear(1);
+
+ define('G5_SOCIAL_IS_LOADING', TRUE );
+
+ $login_action_url = G5_URL;
+
+ $img_url = G5_SOCIAL_LOGIN_URL.'/img/';
+ include_once(G5_SOCIAL_LOGIN_PATH.'/includes/loading.php');
+}
+
+function social_check_login_before($p_service=''){
+ global $is_member, $member;
+
+ $action = isset( $_REQUEST['action'] ) ? social_escape_request($_REQUEST['action']) : '';
+ $provider_name = $p_service ? $p_service : social_get_request_provider();
+ $url = isset($_REQUEST['url']) ? $_REQUEST['url'] : G5_URL;
+ $mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : 'login';
+ $use_popup = G5_SOCIAL_USE_POPUP ? 1 : 2;
+ $ss_social_provider = get_session('ss_social_provider');
+
+ if( $provider_name ){
+
+ if( ! isset( $_REQUEST["redirect_to_idp"] ) )
+ {
+ return social_loading_provider_page( $provider_name );
+ }
+
+ try
+ {
+ $adapter = social_login_get_provider_adapter( $provider_name );
+
+ // then grab the user profile
+ $user_profile = $adapter->getUserProfile();
+
+ if( ! $is_member ){
+ $_SESSION['sl_userprofile'][$provider_name] = json_encode( $user_profile );
+ }
+ }
+
+ catch( Exception $e )
+ {
+ $get_error = social_get_error_msg( $e->getCode() );
+
+ if( is_object( $adapter ) ){
+ $adapter->logout();
+ }
+
+ include_once(G5_SOCIAL_LOGIN_PATH.'/error.php');
+ exit;
+ }
+
+ $register_url = G5_BBS_URL.'/register_form.php?provider='.$provider_name;
+ $register_action_url = G5_BBS_URL.'/register_form_update.php';
+
+ $login_action_url = G5_HTTPS_BBS_URL."/login_check.php";
+ $mylink = (isset($_REQUEST['mylink']) && !empty($_REQUEST['mylink'])) ? 1 : 0;
+
+ //소셜로 이미 가입 했다면 로그인 처리 합니다.
+ if( $user_provider = social_get_data('provider', $provider_name, $user_profile) ){
+
+ if( $is_member ){
+
+ $msg = "이미 로그인 하셨거나 잘못된 요청입니다.";
+
+ if( $mylink ){
+ $msg = "이미 연결된 아이디가 있거나, 잘못된 요청입니다.";
+ }
+
+ if( $use_popup == 1 || ! $use_popup ){ //팝업이면
+ alert_close( $msg );
+ } else {
+ alert( $msg );
+ }
+
+ if( is_object( $adapter ) ){ //연결한것은 인증 받은 즉시 로그아웃한다.
+ social_logout_with_adapter($adapter);
+ }
+ exit;
+ }
+
+ //데이터가 틀리면 데이터를 갱신 후 로그인 처리 합니다.
+
+ $mb_id = $user_provider['mb_id'];
+ //이미 소셜로 가입된 데이터가 있다면 password를 필요하지 않으니, 패스워드를 무작위 생성하여 넘깁니다.
+ $mb_password = sha1( str_shuffle( "0123456789abcdefghijklmnoABCDEFGHIJ" ) );
+
+ echo social_return_from_provider_page( $provider_name, $login_action_url, $mb_id, $mb_password, $url, $use_popup );
+ exit;
+
+ //소셜 데이터와 회원데이터가 일치 하는 경우 계정와 연결할지, 새로 계정을 만들지 선택합니다.
+ } else {
+
+ if( $is_member && !empty($user_profile) ){ //회원이면
+
+ if( $mylink ){
+
+ social_user_profile_replace($member['mb_id'], $provider_name, $user_profile);
+
+ if( is_object( $adapter ) ){ //연결한것은 인증 받은 즉시 로그아웃한다.
+ social_logout_with_adapter($adapter);
+ }
+
+ // 세션에 소셜정보가 없으면 연결된 소셜서비스를 저장합니다.
+ if( ! get_session('ss_social_provider') ){
+ set_session('ss_social_provider', $provider_name);
+ }
+
+ if( $use_popup == 1 || ! $use_popup ){ //팝업이면
+ ?>
+
+ $provider_name);
+
+ $url = replaceQueryParams($url, $params);
+ goto_url($url);
+ } else {
+ goto_url(G5_URL);
+ }
+ }
+ exit;
+ }
+
+ goto_url(G5_URL);
+ }
+
+ if( !( property_exists($user_profile, 'sid') && !empty($user_profile->sid) ) ){
+ $msg = '소셜 데이터 오류';
+ if( $use_popup == 1 || ! $use_popup ){ //팝업이면
+ alert_close($msg);
+ } else {
+ alert($msg);
+ }
+ }
+
+ /*
+ * 회원이 아닌 경우에만 아래 실행
+ */
+ $register_url = G5_SOCIAL_LOGIN_URL.'/register_member.php?provider='.$provider_name;
+
+ if( $url ){
+ $register_url .= '&url='.urlencode($url);
+ }
+
+ if( $use_popup == 1 || ! $use_popup ){ //팝업이면
+ ?>
+
+ displayName);
+ $member['mb_sex'] = $user_profile->gender;
+ $member['mb_email'] = ($user_profile->emailVerified) ? $user_profile->emailVerified : $user_profile->email;
+
+ }
+
+ return $member;
+}
+
+function social_profile_img_resize($path, $file_url, $width, $height){
+
+ // getimagesize 경우 php.ini 에서 allow_url_fopen 이 활성화 되어 있어야 원격이미지를 읽어올수 있습니다.
+ list($w, $h, $ext) = @getimagesize($file_url);
+ if( $w && $h && $ext ){
+ $ratio = max($width/$w, $height/$h);
+ $h = ceil($height / $ratio);
+ $x = ($w - $width / $ratio) / 2;
+ $w = ceil($width / $ratio);
+
+ $tmp = imagecreatetruecolor($width, $height);
+
+ if($ext == 1){
+ $image = imagecreatefromgif($file_url);
+ } else if($ext == 3) {
+ $image = imagecreatefrompng($file_url);
+ } else {
+ $image = imagecreatefromjpeg($file_url);
+ }
+ imagecopyresampled($tmp, $image,
+ 0, 0,
+ $x, 0,
+ $width, $height,
+ $w, $h);
+
+ switch ($ext) {
+ case '2':
+ imagejpeg($tmp, $path, 100);
+ break;
+ case '3':
+ imagepng($tmp, $path, 0);
+ break;
+ case '1':
+ imagegif($tmp, $path);
+ break;
+ }
+
+ chmod($path, G5_FILE_PERMISSION);
+
+ /* cleanup memory */
+ imagedestroy($image);
+ imagedestroy($tmp);
+ }
+}
+
+function social_is_login_check(){
+
+ //소셜 로그인이 맞는지 체크합니다.
+ if( social_session_exists_check() ){
+ return true;
+ }
+
+ return false;
+}
+
+function social_logout_with_adapter($adapter=null){
+ if( is_object( $adapter ) ){
+ $adapter->logout();
+ }
+ social_login_session_clear(1);
+}
+
+function social_member_provider_manage(){
+ global $member;
+
+ return social_login_link_account($member['mb_id'], false, 'mb_form');
+}
+
+function social_member_comfirm_redirect(){
+ global $is_member;
+
+ if( !$is_member ){
+ return;
+ }
+
+ $provider_name = get_session('ss_social_provider');
+
+ if( social_get_provider_service_name($provider_name) ){
+
+ try
+ {
+ $adapter = social_login_get_provider_adapter( $provider_name );
+
+ // then grab the user profile
+ $user_profile = $adapter->getUserProfile();
+ }
+
+ catch( Exception $e )
+ {
+ $get_error = social_get_error_msg( $e->getCode() );
+
+ if( is_object( $adapter ) ){
+ social_logout_with_adapter($adapter);
+ }
+
+ alert('SNS 사용자 인증에 실패하였습니다.', G5_URL);
+ }
+
+ if( $user_provider = social_get_data('provider', $provider_name, $user_profile) ){
+
+ social_login_session_clear(1);
+
+ $url = G5_BBS_URL.'/register_form.php';
+
+ $social_token = social_nonce_create($provider_name);
+ set_session('social_link_token', $social_token);
+
+ $params = array('provider'=>$provider_name);
+
+ $url = replaceQueryParams($url, $params);
+ goto_url($url);
+
+ }
+
+ set_session('ss_social_provider', '');
+ alert('잘못된 요청입니다.', G5_URL);
+ }
+}
+
+function social_is_login_password_check($mb_id){
+ global $g5;
+
+ $action = isset($_POST['action']) ? $_POST['action'] : '';
+ $provider_name = social_get_request_provider();
+
+ if(!$mb_id || $action === 'link'){ //아이디가 없거나, 계정 연결이면
+ if($action === 'link'){ //계정연결이면 같은 서비스명이 있는 경우
+
+ $sql = sprintf("select count(*) as num from {$g5['social_profile_table']} where provider = '%s' and mb_id = '%s' ", $provider_name, $mb_id);
+
+ $row = sql_fetch($sql);
+ if( $row['num'] ){
+ alert("해당 계정에 이미 $provider_name ID 가 연결되어 있습니다. 연결을 해제 후 다시 시도해 주세요.");
+ }
+ }
+ return false;
+ }
+
+ //소셜 로그인이 맞는지 체크합니다.
+ if( $user_profile = social_session_exists_check() ){
+
+ // db에 이미 쇼셜 계정이 존재하는 경우에는
+ if( $user_provider = social_get_data('provider', $provider_name, $user_profile) ){
+
+ if($user_provider['mb_id'] == $mb_id)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//소셜 로그인 후 계정 업데이트
+function social_login_success_after($mb, $link='', $mode='', $tmp_create_info=array()){
+ global $g5, $config;
+
+ $provider = social_get_request_provider();
+
+ if( isset($mb['mb_id']) && !empty($mb['mb_id']) && $provider && $user_profile = social_session_exists_check() ){
+
+ $mb_id = $mb['mb_id'];
+ //로그인에 성공 했으면 기존 데이터와 비교하여 틀린 값이 없으면 업데이트 합니다.
+ social_user_profile_replace($mb_id, $provider, $user_profile);
+
+ //소셜로그인의 provider 이름( naver, kakao, facebook 기타 등등 ) 서비스 이름을 세션에 입력합니다.
+ set_session('ss_social_provider', $provider);
+
+ //소셜로그인 최초 받아온 세션에 저장된 값을 삭제합니다.
+ if( isset($_SESSION['sl_userprofile']) && isset($_SESSION['sl_userprofile'][$provider]) ){
+ unset($_SESSION['sl_userprofile'][$provider]);
+ }
+
+ if($mode=='register'){ //회원가입 했다면
+ return;
+ }
+
+ }
+
+ return $link;
+}
+
+function social_login_link_account($mb_id, $is_buffer=false, $is_type=''){
+ global $g5, $is_admin, $is_guest, $member, $config;
+
+ if( !$mb_id )
+ return;
+
+ $sql = "select * from {$g5['social_profile_table']} where mb_id = '".$mb_id."' ";
+
+ $result = sql_query($sql);
+
+ $my_social_accounts = array();
+
+ for($i=0;$row=sql_fetch_array($result);$i++){
+ $my_social_accounts[] = $row;
+ }
+
+ if( $is_type === 'get_data' ){
+ return $my_social_accounts;
+ }
+
+ ob_start();
+
+ if( $is_type === 'mb_form' ) {
+
+ global $urlencode;
+
+ static $social_pop_once;
+
+ $my_provides = array();
+
+ foreach( $my_social_accounts as $account ){
+ $my_provides[] = strtolower($account['provider']);
+ }
+
+ $self_url = G5_BBS_URL."/login.php";
+
+ //새창을 사용한다면
+ if( G5_SOCIAL_USE_POPUP )
+ $self_url = G5_SOCIAL_LOGIN_URL.'/popup.php';
+
+ include(get_social_skin_path().'/social_u_register_form.skin.php');
+ }
+
+ $html = ob_get_clean();
+
+ if($is_buffer){
+ return $html;
+ } else {
+ echo $html;
+ }
+}
+
+function social_get_provider_service_name($provider='', $all=''){
+
+ $services = array(
+ 'naver' => '네이버',
+ 'kakao' => '카카오',
+ 'daum' => '다음',
+ 'facebook' => '페이스북',
+ 'google' => '구글',
+ 'twitter' => '트위터',
+ 'payco' => '페이코',
+ );
+
+ if( $all ){
+ return $services;
+ }
+
+ $provider = $provider ? strtolower($provider) : '';
+
+ return ($provider && isset($services[$provider])) ? $services[$provider] : '';
+}
+
+function social_provider_logout($provider='', $session_delete=1){
+
+ $provider = $provider ? $provider : get_session('ss_social_provider');
+
+ if( $provider ){
+
+ try
+ {
+ if( ! class_exists( 'Hybrid_Auth', false ) )
+ {
+ include_once G5_SOCIAL_LOGIN_PATH . "/Hybrid/Auth.php";
+ }
+
+ Hybrid_Auth::logoutAllProviders();
+
+ /*
+ if( $adapter = social_login_get_provider_adapter( $provider ) ){
+ $adapter->logout();
+ }
+ */
+ if( $session_delete )
+ set_session('ss_social_provider', '');
+ }
+
+ catch( Exception $e ){
+ if( is_object( $adapter ) ){
+ social_logout_with_adapter($adapter);
+ }
+ }
+ }
+}
+
+//회원 연결을 해제하거나 회원 탈퇴시
+function social_member_link_delete($mb_id, $mp_no=''){
+
+ global $g5;
+
+ if(!$mb_id)
+ return;
+
+ $mp_no = (int) $mp_no;
+
+ if( G5_SOCIAL_DELETE_DAY > 0 ){
+
+ //mb_id가 없는 소셜 데이터 중에 해당 기간이 넘어간 db 데이터를 삭제합니다.
+ $time = date("Y-m-d H:i:s", time() - (86400 * (int) G5_SOCIAL_DELETE_DAY));
+
+ $sql = "delete from {$g5['social_profile_table']} where mb_id = '' and mp_latest_day < '$time' ";
+ sql_query($sql);
+
+ $sql = "update {$g5['social_profile_table']} set mb_id='', object_sha='', profileurl='', photourl='', displayname='', mp_latest_day = '".G5_TIME_YMDHIS."' where mb_id= '".$mb_id."'";
+ } else {
+ $sql = "delete from {$g5['social_profile_table']} where mb_id= '".$mb_id."'"; //바로 삭제합니다.
+ }
+
+ if($mp_no){
+ $sql .= " and mp_no=$mp_no";
+ }
+
+ sql_query($sql, false);
+}
+
+function social_service_check($provider){
+ global $config;
+
+ if( $config['cf_social_servicelist'] && option_array_checked($provider, $config['cf_social_servicelist']) ) {
+ return true;
+ }
+
+ return false;
+}
+
+function exist_mb_id_recursive($mb_id){
+ static $count = 0;
+
+ $mb_id_add = ($count > 0) ? $mb_id.(string)$count : $mb_id;
+
+ if( ! exist_mb_id($mb_id_add) ){
+ return $mb_id_add;
+ }
+
+ $count++;
+ return exist_mb_id_recursive($mb_id);
+}
+
+function exist_mb_nick_recursive($mb_nick){
+ static $count = 0;
+
+ $mb_nick_add = ($count > 0) ? $mb_nick.(string)$count : $mb_nick;
+
+ if( ! exist_mb_nick($mb_nick_add, '') ){
+ return $mb_nick_add;
+ }
+
+ $count++;
+ return exist_mb_nick_recursive($mb_nick);
+}
+
+function social_get_nonce($key=''){
+ if( $key == 'd' ){ //nonce_duration
+ return 7200;
+ } else if ($key == 'n' ){ //nonce_name
+ return '_nonce';
+ } else {
+
+ if( empty($key) )
+ $key = social_get_request_provider();
+
+ $setting = social_build_provider_config($key);
+ try{
+ return sha1($setting['providers'][$key]['secret']);
+ } catch(Exception $e) {
+ return '';
+ }
+ }
+
+ return '';
+}
+
+function social_nonce_create_query_string( $action = '' , $user = '', $provider = '' ){
+ if($nonce_key=social_get_nonce('n')){
+ return $nonce_key."=".social_nonce_create( $action , $user, $provider );
+ }
+ return '';
+}
+
+function social_nonce_create( $action = '' , $user='' , $provider = '' ){
+ return substr( social_nonce_generate_hash( $action , $user, $provider ), -12, 10);
+}
+
+function social_nonce_is_valid( $nonce , $action = '' , $user='' , $provider = '' ){
+ // Nonce generated 0-12 hours ago
+ if ( substr(social_nonce_generate_hash( $action , $user, $provider ), -12, 10) == $nonce ){
+ return true;
+ }
+ return false;
+}
+
+function social_nonce_generate_hash( $action='' , $user='', $provider = '' ){
+ $i = ceil( time() / ( social_get_nonce('d') / 2 ) );
+ return md5( $i . $action . $user . social_get_nonce($provider) );
+}
+?>
\ No newline at end of file
diff --git a/plugin/social/includes/g5_endpoint.php b/plugin/social/includes/g5_endpoint.php
new file mode 100644
index 000000000..0eda8063a
--- /dev/null
+++ b/plugin/social/includes/g5_endpoint.php
@@ -0,0 +1,32 @@
+
\ No newline at end of file
diff --git a/plugin/social/includes/g5_endpoint_class.php b/plugin/social/includes/g5_endpoint_class.php
new file mode 100644
index 000000000..29c8f4304
--- /dev/null
+++ b/plugin/social/includes/g5_endpoint_class.php
@@ -0,0 +1,32 @@
+dieError( "412 Precondition Failed", $e->getMessage(), $e );
+ }
+ }
+
+ protected function processAuthDone()
+ {
+ try {
+ parent::processAuthDone();
+ }
+ catch( Exception $e ){
+ $this->dieError( "410 Gone", $e->getMessage(), $e );
+ }
+ }
+
+ public function dieError( $code, $message, $e )
+ {
+ $get_error = $message;
+ include_once(G5_SOCIAL_LOGIN_PATH.'/error.php');
+ die();
+ }
+}
+?>
\ No newline at end of file
diff --git a/plugin/social/includes/index.php b/plugin/social/includes/index.php
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugin/social/includes/loading.php b/plugin/social/includes/loading.php
new file mode 100644
index 000000000..61a428427
--- /dev/null
+++ b/plugin/social/includes/loading.php
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+ 소셜 로그인 -
+
+
+
+
+  |
+
+
+
Loading...
|
+
+
+ | 에 연결중입니다. 잠시만 기다려주세요. |
+
+
+
+
+
+
+
+
+
+
+