Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.21% covered (warning)
87.21%
75 / 86
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
OAuthUtil
87.21% covered (warning)
87.21%
75 / 86
50.00% covered (danger)
50.00%
3 / 6
30.76
0.00% covered (danger)
0.00%
0 / 1
 urlencode_rfc3986
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
3
 urldecode_rfc3986
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 split_header
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 get_headers
62.50% covered (warning)
62.50%
15 / 24
0.00% covered (danger)
0.00%
0 / 1
9.58
 parse_parameters
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
7.02
 build_http_query
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
5.01
1<?php
2// vim: foldmethod=marker
3/**
4 * The MIT License
5 *
6 * Copyright (c) 2007 Andy Smith
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files ( the "Software" ), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27namespace MediaWiki\Extension\OAuth\Lib;
28
29use MediaWiki\Logger\LoggerFactory;
30
31class OAuthUtil {
32    public static function urlencode_rfc3986( $input ) {
33        if ( is_array( $input ) ) {
34            return array_map(
35                array(
36                    self::class,
37                    'urlencode_rfc3986'
38                ),
39                $input
40            );
41        } else {
42            if ( is_scalar( $input ) ) {
43                return str_replace(
44                    '+',
45                    ' ',
46                    str_replace( '%7E', '~', rawurlencode( $input ) )
47                );
48            } else {
49                return '';
50            }
51        }
52    }
53
54
55    // This decode function isn't taking into consideration the above
56    // modifications to the encoding process. However, this method doesn't
57    // seem to be used anywhere so leaving it as is.
58    public static function urldecode_rfc3986( $string ) {
59        return urldecode( $string );
60    }
61
62    // Utility function for turning the Authorization: header into
63    // parameters, has to do some unescaping
64    // Can filter out any non-oauth parameters if needed ( default behaviour )
65    // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
66    // see http://code.google.com/p/oauth/issues/detail?id = 163
67    public static function split_header( $header, $only_allow_oauth_parameters = true ) {
68        $logger = LoggerFactory::getInstance( 'OAuth' );
69        $logger->debug( __METHOD__ . ": pulling headers from '$header'" );
70        $params = array();
71        if ( preg_match_all(
72            '/(' . ( $only_allow_oauth_parameters ? 'oauth_' : '' ) . '[a-z_-]*)=(:?"([^"]*)"|([^,]*))/',
73            $header,
74            $matches
75        )
76        ) {
77            foreach ( $matches[1] as $i => $h ) {
78                $params[$h] = OAuthUtil::urldecode_rfc3986(
79                    empty( $matches[3][$i] ) ? $matches[4][$i] : $matches[3][$i]
80                );
81            }
82            if ( isset( $params['realm'] ) ) {
83                unset( $params['realm'] );
84            }
85        }
86
87        return $params;
88    }
89
90    // helper to try to sort out headers for people who aren't running apache
91    public static function get_headers() {
92        if ( function_exists( 'apache_request_headers' ) ) {
93            // we need this to get the actual Authorization: header
94            // because apache tends to tell us it doesn't exist
95            $headers = apache_request_headers();
96
97            // sanitize the output of apache_request_headers because
98            // we always want the keys to be Cased-Like-This and arh()
99            // returns the headers in the same case as they are in the
100            // request
101            $out = array();
102            foreach ( $headers as $key => $value ) {
103                $key = str_replace(
104                    " ",
105                    "-",
106                    ucwords( strtolower( str_replace( "-", " ", $key ) ) )
107                );
108                $out[$key] = $value;
109            }
110        } else {
111            // otherwise we don't have apache and are just going to have to hope
112            // that $_SERVER actually contains what we need
113            $out = array();
114            if ( isset( $_SERVER['CONTENT_TYPE'] ) ) {
115                $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
116            }
117            if ( isset( $_ENV['CONTENT_TYPE'] ) ) {
118                $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
119            }
120
121            foreach ( $_SERVER as $key => $value ) {
122                if ( substr( $key, 0, 5 ) == "HTTP_" ) {
123                    // this is chaos, basically it is just there to capitalize the first
124                    // letter of every word that is not an initial HTTP and strip HTTP
125                    // code from przemek
126                    $key = str_replace(
127                        " ",
128                        "-",
129                        ucwords( strtolower( str_replace( "_", " ", substr( $key, 5 ) ) ) )
130                    );
131                    $out[$key] = $value;
132                }
133            }
134        }
135
136        return $out;
137    }
138
139    // This function takes a input like a=b&a=c&d=e and returns the parsed
140    // parameters like this
141    // array( 'a' => array( 'b','c' ), 'd' => 'e' )
142    public static function parse_parameters( $input ) {
143        if ( !isset( $input ) || !$input ) {
144            return array();
145        }
146
147        $pairs = explode( '&', $input );
148
149        $parsed_parameters = array();
150        foreach ( $pairs as $pair ) {
151            $split = explode( '=', $pair, 2 );
152            $parameter = OAuthUtil::urldecode_rfc3986( $split[0] );
153            $value = isset( $split[1] ) ? OAuthUtil::urldecode_rfc3986( $split[1] ) : '';
154
155            if ( isset( $parsed_parameters[$parameter] ) ) {
156                // We have already recieved parameter( s ) with this name, so add to the list
157                // of parameters with this name
158
159                if ( is_scalar( $parsed_parameters[$parameter] ) ) {
160                    // This is the first duplicate, so transform scalar ( string ) into an array
161                    // so we can add the duplicates
162                    $parsed_parameters[$parameter] = array( $parsed_parameters[$parameter] );
163                }
164
165                $parsed_parameters[$parameter][] = $value;
166            } else {
167                $parsed_parameters[$parameter] = $value;
168            }
169        }
170
171        return $parsed_parameters;
172    }
173
174    public static function build_http_query( $params ) {
175        LoggerFactory::getInstance( 'OAuth' )->debug(
176            __METHOD__ . " called with params:\n" . print_r( $params, true )
177        );
178        if ( !$params ) {
179            return '';
180        }
181
182        // Urlencode both keys and values
183        $keys = OAuthUtil::urlencode_rfc3986( array_keys( $params ) );
184        $values = OAuthUtil::urlencode_rfc3986( array_values( $params ) );
185        $params = array_combine( $keys, $values );
186
187        // Parameters are sorted by name, using lexicographical byte value ordering.
188        // Ref: Spec: 9.1.1 ( 1 )
189        uksort( $params, 'strcmp' );
190
191        $pairs = array();
192        foreach ( $params as $parameter => $value ) {
193            if ( is_array( $value ) ) {
194                // If two or more parameters share the same name, they are sorted by their value
195                // Ref: Spec: 9.1.1 ( 1 )
196                // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
197                sort( $value, SORT_STRING );
198                foreach ( $value as $duplicate_value ) {
199                    $pairs[] = $parameter . '=' . $duplicate_value;
200                }
201            } else {
202                $pairs[] = $parameter . '=' . $value;
203            }
204        }
205        // For each parameter, the name is separated from the corresponding value by an ' = ' character ( ASCII code 61 )
206        // Each name-value pair is separated by an '&' character ( ASCII code 38 )
207        return implode( '&', $pairs );
208    }
209}