1<?php 2 3/** 4 * URI normalization routines. 5 * 6 * @package OpenID 7 * @author JanRain, Inc. <openid@janrain.com> 8 * @copyright 2005-2008 Janrain, Inc. 9 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache 10 */ 11 12require_once 'Auth/Yadis/Misc.php'; 13 14// from appendix B of rfc 3986 (http://www.ietf.org/rfc/rfc3986.txt) 15function Auth_OpenID_getURIPattern() 16{ 17 return '&^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?&'; 18} 19 20function Auth_OpenID_getAuthorityPattern() 21{ 22 return '/^([^@]*@)?([^:]*)(:.*)?/'; 23} 24 25function Auth_OpenID_getEncodedPattern() 26{ 27 return '/%([0-9A-Fa-f]{2})/'; 28} 29 30# gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" 31# 32# sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 33# / "*" / "+" / "," / ";" / "=" 34# 35# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 36function Auth_OpenID_getURLIllegalCharRE() 37{ 38 return "/([^-A-Za-z0-9:\/\?#\[\]@\!\$&'\(\)\*\+,;=\._~\%])/"; 39} 40 41function Auth_OpenID_getUnreserved() 42{ 43 $_unreserved = []; 44 for ($i = 0; $i < 256; $i++) { 45 $_unreserved[$i] = false; 46 } 47 48 for ($i = ord('A'); $i <= ord('Z'); $i++) { 49 $_unreserved[$i] = true; 50 } 51 52 for ($i = ord('0'); $i <= ord('9'); $i++) { 53 $_unreserved[$i] = true; 54 } 55 56 for ($i = ord('a'); $i <= ord('z'); $i++) { 57 $_unreserved[$i] = true; 58 } 59 60 $_unreserved[ord('-')] = true; 61 $_unreserved[ord('.')] = true; 62 $_unreserved[ord('_')] = true; 63 $_unreserved[ord('~')] = true; 64 65 return $_unreserved; 66} 67 68function Auth_OpenID_getEscapeRE() 69{ 70 $parts = []; 71 foreach (array_merge(Auth_Yadis_getUCSChars(), 72 Auth_Yadis_getIPrivateChars()) as $pair) { 73 list($m, $n) = $pair; 74 $parts[] = sprintf("%s-%s", chr($m), chr($n)); 75 } 76 77 return sprintf('[%s]', implode('', $parts)); 78} 79 80function Auth_OpenID_pct_encoded_replace_unreserved($mo) 81{ 82 $_unreserved = Auth_OpenID_getUnreserved(); 83 84 $i = intval($mo[1], 16); 85 if ($_unreserved[$i]) { 86 return chr($i); 87 } 88 return strtoupper($mo[0]); 89} 90 91function Auth_OpenID_pct_encoded_replace($mo) 92{ 93 $code = intval($mo[1], 16); 94 95 // Prevent request splitting by ignoring newline and space characters 96 if($code === 0xA || $code === 0xD || $code === ord(' ')) 97 { 98 return $mo[0]; 99 } 100 else 101 { 102 return chr($code); 103 } 104} 105 106function Auth_OpenID_remove_dot_segments($path) 107{ 108 $result_segments = []; 109 110 while ($path) { 111 if (Auth_Yadis_startswith($path, '../')) { 112 $path = substr($path, 3); 113 } else if (Auth_Yadis_startswith($path, './')) { 114 $path = substr($path, 2); 115 } else if (Auth_Yadis_startswith($path, '/./')) { 116 $path = substr($path, 2); 117 } else if ($path == '/.') { 118 $path = '/'; 119 } else if (Auth_Yadis_startswith($path, '/../')) { 120 $path = substr($path, 3); 121 if ($result_segments) { 122 array_pop($result_segments); 123 } 124 } else if ($path == '/..') { 125 $path = '/'; 126 if ($result_segments) { 127 array_pop($result_segments); 128 } 129 } else if (($path == '..') || 130 ($path == '.')) { 131 $path = ''; 132 } else { 133 $i = 0; 134 if ($path[0] == '/') { 135 $i = 1; 136 } 137 $i = strpos($path, '/', $i); 138 if ($i === false) { 139 $i = strlen($path); 140 } 141 $result_segments[] = substr($path, 0, $i); 142 $path = substr($path, $i); 143 } 144 } 145 146 return implode('', $result_segments); 147} 148 149function Auth_OpenID_urinorm($uri) 150{ 151 $uri_matches = []; 152 preg_match(Auth_OpenID_getURIPattern(), $uri, $uri_matches); 153 154 if (count($uri_matches) < 9) { 155 for ($i = count($uri_matches); $i <= 9; $i++) { 156 $uri_matches[] = ''; 157 } 158 } 159 160 $illegal_matches = []; 161 preg_match(Auth_OpenID_getURLIllegalCharRE(), 162 $uri, $illegal_matches); 163 if ($illegal_matches) { 164 return null; 165 } 166 167 $scheme = $uri_matches[2]; 168 if ($scheme === '') { 169 // No scheme specified 170 return null; 171 } 172 173 $scheme = strtolower($scheme); 174 if (!in_array($scheme, ['http', 'https'])) { 175 // Not an absolute HTTP or HTTPS URI 176 return null; 177 } 178 179 $authority = $uri_matches[4]; 180 if ($authority === '') { 181 // Not an absolute URI 182 return null; 183 } 184 185 $authority_matches = []; 186 preg_match(Auth_OpenID_getAuthorityPattern(), 187 $authority, $authority_matches); 188 if (count($authority_matches) === 0) { 189 // URI does not have a valid authority 190 return null; 191 } 192 193 if (count($authority_matches) < 4) { 194 for ($i = count($authority_matches); $i <= 4; $i++) { 195 $authority_matches[] = ''; 196 } 197 } 198 199 list(, $userinfo, $host, $port) = $authority_matches; 200 201 if ($userinfo === null) { 202 $userinfo = ''; 203 } 204 205 if (strpos($host, '%') !== false) { 206 $host = strtolower($host); 207 $host = preg_replace_callback( 208 Auth_OpenID_getEncodedPattern(), 209 'Auth_OpenID_pct_encoded_replace', $host); 210 // NO IDNA. 211 // $host = unicode($host, 'utf-8').encode('idna'); 212 } else { 213 $host = strtolower($host); 214 } 215 216 if ($port) { 217 if (($port == ':') || 218 ($scheme == 'http' && $port == ':80') || 219 ($scheme == 'https' && $port == ':443')) { 220 $port = ''; 221 } 222 } else { 223 $port = ''; 224 } 225 226 $authority = $userinfo . $host . $port; 227 228 $path = $uri_matches[5]; 229 $path = preg_replace_callback( 230 Auth_OpenID_getEncodedPattern(), 231 'Auth_OpenID_pct_encoded_replace_unreserved', $path); 232 233 $path = Auth_OpenID_remove_dot_segments($path); 234 if (!$path) { 235 $path = '/'; 236 } 237 238 $query = $uri_matches[6]; 239 if ($query === null) { 240 $query = ''; 241 } 242 243 $fragment = $uri_matches[8]; 244 if ($fragment === null) { 245 $fragment = ''; 246 } 247 248 return $scheme . '://' . $authority . $path . $query . $fragment; 249} 250 251 252