Move library\OAuth1.php to class structure Friendica\Security\OAuth1
[friendica.git/.git] / src / Security / OAuth1 / OAuthUtil.php
1 <?php
2
3 namespace Friendica\Security\OAuth1;
4
5 class OAuthUtil
6 {
7         public static function urlencode_rfc3986($input)
8         {
9                 if (is_array($input)) {
10                         return array_map(['Friendica\Security\OAuth1\OAuthUtil', 'urlencode_rfc3986'], $input);
11                 } else if (is_scalar($input)) {
12                         return str_replace(
13                                 '+',
14                                 ' ',
15                                 str_replace('%7E', '~', rawurlencode($input))
16                         );
17                 } else {
18                         return '';
19                 }
20         }
21
22
23         // This decode function isn't taking into consideration the above
24         // modifications to the encoding process. However, this method doesn't
25         // seem to be used anywhere so leaving it as is.
26         public static function urldecode_rfc3986($string)
27         {
28                 return urldecode($string);
29         }
30
31         // Utility function for turning the Authorization: header into
32         // parameters, has to do some unescaping
33         // Can filter out any non-oauth parameters if needed (default behaviour)
34         public static function split_header($header, $only_allow_oauth_parameters = true)
35         {
36                 $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
37                 $offset  = 0;
38                 $params  = [];
39                 while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
40                         $match          = $matches[0];
41                         $header_name    = $matches[2][0];
42                         $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
43                         if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
44                                 $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
45                         }
46                         $offset = $match[1] + strlen($match[0]);
47                 }
48
49                 if (isset($params['realm'])) {
50                         unset($params['realm']);
51                 }
52
53                 return $params;
54         }
55
56         // helper to try to sort out headers for people who aren't running apache
57         public static function get_headers()
58         {
59                 if (function_exists('apache_request_headers')) {
60                         // we need this to get the actual Authorization: header
61                         // because apache tends to tell us it doesn't exist
62                         $headers = apache_request_headers();
63
64                         // sanitize the output of apache_request_headers because
65                         // we always want the keys to be Cased-Like-This and arh()
66                         // returns the headers in the same case as they are in the
67                         // request
68                         $out = [];
69                         foreach ($headers as $key => $value) {
70                                 $key       = str_replace(
71                                         " ",
72                                         "-",
73                                         ucwords(strtolower(str_replace("-", " ", $key)))
74                                 );
75                                 $out[$key] = $value;
76                         }
77                 } else {
78                         // otherwise we don't have apache and are just going to have to hope
79                         // that $_SERVER actually contains what we need
80                         $out = [];
81                         if (isset($_SERVER['CONTENT_TYPE']))
82                                 $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
83                         if (isset($_ENV['CONTENT_TYPE']))
84                                 $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
85
86                         foreach ($_SERVER as $key => $value) {
87                                 if (substr($key, 0, 5) == "HTTP_") {
88                                         // this is chaos, basically it is just there to capitalize the first
89                                         // letter of every word that is not an initial HTTP and strip HTTP
90                                         // code from przemek
91                                         $key       = str_replace(
92                                                 " ",
93                                                 "-",
94                                                 ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
95                                         );
96                                         $out[$key] = $value;
97                                 }
98                         }
99                 }
100                 return $out;
101         }
102
103         // This function takes a input like a=b&a=c&d=e and returns the parsed
104         // parameters like this
105         // array('a' => array('b','c'), 'd' => 'e')
106         public static function parse_parameters($input)
107         {
108                 if (!isset($input) || !$input) return [];
109
110                 $pairs = explode('&', $input);
111
112                 $parsed_parameters = [];
113                 foreach ($pairs as $pair) {
114                         $split     = explode('=', $pair, 2);
115                         $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
116                         $value     = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
117
118                         if (isset($parsed_parameters[$parameter])) {
119                                 // We have already recieved parameter(s) with this name, so add to the list
120                                 // of parameters with this name
121
122                                 if (is_scalar($parsed_parameters[$parameter])) {
123                                         // This is the first duplicate, so transform scalar (string) into an array
124                                         // so we can add the duplicates
125                                         $parsed_parameters[$parameter] = [$parsed_parameters[$parameter]];
126                                 }
127
128                                 $parsed_parameters[$parameter][] = $value;
129                         } else {
130                                 $parsed_parameters[$parameter] = $value;
131                         }
132                 }
133                 return $parsed_parameters;
134         }
135
136         public static function build_http_query($params)
137         {
138                 if (!$params) return '';
139
140                 // Urlencode both keys and values
141                 $keys   = OAuthUtil::urlencode_rfc3986(array_keys($params));
142                 $values = OAuthUtil::urlencode_rfc3986(array_values($params));
143                 $params = array_combine($keys, $values);
144
145                 // Parameters are sorted by name, using lexicographical byte value ordering.
146                 // Ref: Spec: 9.1.1 (1)
147                 uksort($params, 'strcmp');
148
149                 $pairs = [];
150                 foreach ($params as $parameter => $value) {
151                         if (is_array($value)) {
152                                 // If two or more parameters share the same name, they are sorted by their value
153                                 // Ref: Spec: 9.1.1 (1)
154                                 natsort($value);
155                                 foreach ($value as $duplicate_value) {
156                                         $pairs[] = $parameter . '=' . $duplicate_value;
157                                 }
158                         } else {
159                                 $pairs[] = $parameter . '=' . $value;
160                         }
161                 }
162                 // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
163                 // Each name-value pair is separated by an '&' character (ASCII code 38)
164                 return implode('&', $pairs);
165         }
166 }