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