@@ -120,7 +120,12 @@ abstract class BaseFacebook
120120 /**
121121 * Version.
122122 */
123- const VERSION = '3.1.1 ' ;
123+ const VERSION = '3.2.0 ' ;
124+
125+ /**
126+ * Signed Request Algorithm.
127+ */
128+ const SIGNED_REQUEST_ALGORITHM = 'HMAC-SHA256 ' ;
124129
125130 /**
126131 * Default options for curl.
@@ -129,7 +134,7 @@ abstract class BaseFacebook
129134 CURLOPT_CONNECTTIMEOUT => 10 ,
130135 CURLOPT_RETURNTRANSFER => true ,
131136 CURLOPT_TIMEOUT => 60 ,
132- CURLOPT_USERAGENT => 'facebook-php-3.1 ' ,
137+ CURLOPT_USERAGENT => 'facebook-php-3.2 ' ,
133138 );
134139
135140 /**
@@ -200,6 +205,13 @@ abstract class BaseFacebook
200205 */
201206 protected $ fileUploadSupport = false ;
202207
208+ /**
209+ * Indicates if we trust HTTP_X_FORWARDED_* headers.
210+ *
211+ * @var boolean
212+ */
213+ protected $ trustForwarded = false ;
214+
203215 /**
204216 * Initialize a Facebook Application.
205217 *
@@ -216,7 +228,9 @@ public function __construct($config) {
216228 if (isset ($ config ['fileUpload ' ])) {
217229 $ this ->setFileUploadSupport ($ config ['fileUpload ' ]);
218230 }
219-
231+ if (isset ($ config ['trustForwarded ' ]) && $ config ['trustForwarded ' ]) {
232+ $ this ->trustForwarded = true ;
233+ }
220234 $ state = $ this ->getPersistentData ('state ' );
221235 if (!empty ($ state )) {
222236 $ this ->state = $ state ;
@@ -934,8 +948,9 @@ protected function parseSignedRequest($signed_request) {
934948 $ sig = self ::base64UrlDecode ($ encoded_sig );
935949 $ data = json_decode (self ::base64UrlDecode ($ payload ), true );
936950
937- if (strtoupper ($ data ['algorithm ' ]) !== 'HMAC-SHA256 ' ) {
938- self ::errorLog ('Unknown algorithm. Expected HMAC-SHA256 ' );
951+ if (strtoupper ($ data ['algorithm ' ]) !== self ::SIGNED_REQUEST_ALGORITHM ) {
952+ self ::errorLog (
953+ 'Unknown algorithm. Expected ' . self ::SIGNED_REQUEST_ALGORITHM );
939954 return null ;
940955 }
941956
@@ -950,6 +965,26 @@ protected function parseSignedRequest($signed_request) {
950965 return $ data ;
951966 }
952967
968+ /**
969+ * Makes a signed_request blob using the given data.
970+ *
971+ * @param array The data array.
972+ * @return string The signed request.
973+ */
974+ protected function makeSignedRequest ($ data ) {
975+ if (!is_array ($ data )) {
976+ throw new InvalidArgumentException (
977+ 'makeSignedRequest expects an array. Got: ' . print_r ($ data , true ));
978+ }
979+ $ data ['algorithm ' ] = self ::SIGNED_REQUEST_ALGORITHM ;
980+ $ data ['issued_at ' ] = time ();
981+ $ json = json_encode ($ data );
982+ $ b64 = self ::base64UrlEncode ($ json );
983+ $ raw_sig = hash_hmac ('sha256 ' , $ b64 , $ this ->getAppSecret (), $ raw = true );
984+ $ sig = self ::base64UrlEncode ($ raw_sig );
985+ return $ sig .'. ' .$ b64 ;
986+ }
987+
953988 /**
954989 * Build the URL for api given parameters.
955990 *
@@ -1051,25 +1086,52 @@ protected function getUrl($name, $path='', $params=array()) {
10511086 return $ url ;
10521087 }
10531088
1089+ protected function getHttpHost () {
1090+ if ($ this ->trustForwarded && isset ($ _SERVER ['HTTP_X_FORWARDED_HOST ' ])) {
1091+ return $ _SERVER ['HTTP_X_FORWARDED_HOST ' ];
1092+ }
1093+ return $ _SERVER ['HTTP_HOST ' ];
1094+ }
1095+
1096+ protected function getHttpProtocol () {
1097+ if ($ this ->trustForwarded && isset ($ _SERVER ['HTTP_X_FORWARDED_PROTO ' ])) {
1098+ if ($ _SERVER ['HTTP_X_FORWARDED_PROTO ' ] === 'https ' ) {
1099+ return 'https ' ;
1100+ }
1101+ return 'http ' ;
1102+ }
1103+ if (isset ($ _SERVER ['HTTPS ' ]) &&
1104+ ($ _SERVER ['HTTPS ' ] === 'on ' || $ _SERVER ['HTTPS ' ] == 1 )) {
1105+ return 'https ' ;
1106+ }
1107+ return 'http ' ;
1108+ }
1109+
1110+ /**
1111+ * Get the base domain used for the cookie.
1112+ */
1113+ protected function getBaseDomain () {
1114+ // The base domain is stored in the metadata cookie if not we fallback
1115+ // to the current hostname
1116+ $ metadata = $ this ->getMetadataCookie ();
1117+ if (array_key_exists ('base_domain ' , $ metadata ) &&
1118+ !empty ($ metadata ['base_domain ' ])) {
1119+ return trim ($ metadata ['base_domain ' ], '. ' );
1120+ }
1121+ return $ this ->getHttpHost ();
1122+ }
1123+
1124+ /**
1125+
10541126 /**
10551127 * Returns the Current URL, stripping it of known FB parameters that should
10561128 * not persist.
10571129 *
10581130 * @return string The current URL
10591131 */
10601132 protected function getCurrentUrl () {
1061- if (isset ($ _SERVER ['HTTPS ' ]) &&
1062- ($ _SERVER ['HTTPS ' ] == 'on ' || $ _SERVER ['HTTPS ' ] == 1 ) ||
1063- isset ($ _SERVER ['HTTP_X_FORWARDED_PROTO ' ]) &&
1064- $ _SERVER ['HTTP_X_FORWARDED_PROTO ' ] == 'https ' ) {
1065- $ protocol = 'https:// ' ;
1066- }
1067- else {
1068- $ protocol = 'http:// ' ;
1069- }
1070- $ host = isset ($ _SERVER ['HTTP_X_FORWARDED_HOST ' ])
1071- ? $ _SERVER ['HTTP_X_FORWARDED_HOST ' ]
1072- : $ _SERVER ['HTTP_HOST ' ];
1133+ $ protocol = $ this ->getHttpProtocol () . ':// ' ;
1134+ $ host = $ this ->getHttpHost ();
10731135 $ currentUrl = $ protocol .$ host .$ _SERVER ['REQUEST_URI ' ];
10741136 $ parts = parse_url ($ currentUrl );
10751137
@@ -1173,6 +1235,7 @@ protected static function errorLog($msg) {
11731235 * Exactly the same as base64_encode except it uses
11741236 * - instead of +
11751237 * _ instead of /
1238+ * No padded =
11761239 *
11771240 * @param string $input base64UrlEncoded string
11781241 * @return string
@@ -1181,6 +1244,21 @@ protected static function base64UrlDecode($input) {
11811244 return base64_decode (strtr ($ input , '-_ ' , '+/ ' ));
11821245 }
11831246
1247+ /**
1248+ * Base64 encoding that doesn't need to be urlencode()ed.
1249+ * Exactly the same as base64_encode except it uses
1250+ * - instead of +
1251+ * _ instead of /
1252+ *
1253+ * @param string $input string
1254+ * @return string base64Url encoded string
1255+ */
1256+ protected static function base64UrlEncode ($ input ) {
1257+ $ str = strtr (base64_encode ($ input ), '+/ ' , '-_ ' );
1258+ $ str = str_replace ('= ' , '' , $ str );
1259+ return $ str ;
1260+ }
1261+
11841262 /**
11851263 * Destroy the current session
11861264 */
@@ -1196,23 +1274,14 @@ public function destroySession() {
11961274 if (array_key_exists ($ cookie_name , $ _COOKIE )) {
11971275 unset($ _COOKIE [$ cookie_name ]);
11981276 if (!headers_sent ()) {
1199- // The base domain is stored in the metadata cookie if not we fallback
1200- // to the current hostname
1201- $ base_domain = '. ' . $ _SERVER ['HTTP_HOST ' ];
1202-
1203- $ metadata = $ this ->getMetadataCookie ();
1204- if (array_key_exists ('base_domain ' , $ metadata ) &&
1205- !empty ($ metadata ['base_domain ' ])) {
1206- $ base_domain = $ metadata ['base_domain ' ];
1207- }
1208-
1209- setcookie ($ cookie_name , '' , 0 , '/ ' , $ base_domain );
1277+ $ base_domain = $ this ->getBaseDomain ();
1278+ setcookie ($ cookie_name , '' , 1 , '/ ' , '. ' .$ base_domain );
12101279 } else {
12111280 // @codeCoverageIgnoreStart
12121281 self ::errorLog (
12131282 'There exists a cookie that we wanted to clear that we couldn \'t ' .
12141283 'clear because headers was already sent. Make sure to do the first ' .
1215- 'API call before outputing anything '
1284+ 'API call before outputing anything. '
12161285 );
12171286 // @codeCoverageIgnoreEnd
12181287 }
@@ -1250,6 +1319,21 @@ protected function getMetadataCookie() {
12501319 return $ metadata ;
12511320 }
12521321
1322+ protected static function isAllowedDomain ($ big , $ small ) {
1323+ if ($ big === $ small ) {
1324+ return true ;
1325+ }
1326+ return self ::endsWith ($ big , '. ' .$ small );
1327+ }
1328+
1329+ protected static function endsWith ($ big , $ small ) {
1330+ $ len = strlen ($ small );
1331+ if ($ len === 0 ) {
1332+ return true ;
1333+ }
1334+ return substr ($ big , -$ len ) === $ small ;
1335+ }
1336+
12531337 /**
12541338 * Each of the following four methods should be overridden in
12551339 * a concrete subclass, as they are in the provided Facebook class.
0 commit comments