GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

ARC2_Reader::setMessageBody()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/**
3
 * ARC2 Web Client
4
 *
5
 * @author Benjamin Nowack
6
 * @license <http://arc.semsol.org/license>
7
 * @homepage <http://arc.semsol.org/>
8
 * @package ARC2
9
 * @version 2010-07-06
10
*/
11
12
ARC2::inc('Class');
13
14
class ARC2_Reader extends ARC2_Class {
15
16
  function __construct($a = '', &$caller) {
17
    parent::__construct($a, $caller);
18
  }
19
  
20
  function ARC2_Reader($a = '', &$caller) {
21
    $this->__construct($a, $caller);
22
  }
23
24
  function __init() {/* inc_path, proxy_host, proxy_port, proxy_skip, http_accept_header, http_user_agent_header, max_redirects */
25
    parent::__init();
26
    $this->http_method = $this->v('http_method', 'GET', $this->a);
27
    $this->message_body = $this->v('message_body', '', $this->a);;
28
    $this->http_accept_header = $this->v('http_accept_header', 'Accept: application/rdf+xml; q=0.9, */*; q=0.1', $this->a);
29
    $this->http_user_agent_header = $this->v('http_user_agent_header', 'User-Agent: ARC Reader (http://arc.semsol.org/)', $this->a);
30
    $this->http_custom_headers = $this->v('http_custom_headers', '', $this->a);
31
    $this->max_redirects = $this->v('max_redirects', 3, $this->a);
32
    $this->format = $this->v('format', false, $this->a);
33
    $this->redirects = array();
34
    $this->stream_id = '';
35
    $this->timeout = $this->v('reader_timeout', 30, $this->a);
36
    $this->response_headers = array();
37
    $this->digest_auth = 0;
38
    $this->auth_infos = $this->v('reader_auth_infos', array(), $this->a);
39
  }
40
41
  /*  */
42
  
43
  function setHTTPMethod($v) {
44
    $this->http_method = $v;
45
  }
46
47
  function setMessageBody($v) {
48
    $this->message_body = $v;
49
  }
50
51
  function setAcceptHeader($v) {
52
    $this->http_accept_header = $v;
53
  }
54
55
  function setCustomHeaders($v) {
56
    $this->http_custom_headers = $v;
57
  }
58
59
  function addCustomHeaders($v) {
60
    if ($this->http_custom_headers) $this->http_custom_headers .= "\r\n";
61
    $this->http_custom_headers .= $v;
62
  }
63
64
  /*  */
65
66
  function activate($path, $data = '', $ping_only = 0, $timeout = 0) {
67
    $this->setCredentials($path);
68
    $this->ping_only = $ping_only;
69
    if ($timeout) $this->timeout = $timeout;
70
    $id = md5($path . ' ' . $data);
71
    if ($this->stream_id != $id) {
72
      $this->stream_id = $id;
73
      /* data uri? */
74
      if (!$data && preg_match('/^data\:([^\,]+)\,(.*)$/', $path, $m)) {
75
        $path = '';
76
        $data = preg_match('/base64/', $m[1]) ? base64_decode($m[2]) : rawurldecode($m[2]);
77
      }
78
      $this->base = $this->calcBase($path);
79
      $this->uri = $this->calcURI($path, $this->base);
80
      $this->stream = ($data) ? $this->getDataStream($data) : $this->getSocketStream($this->base, $ping_only);
81
      if ($this->stream && !$this->ping_only) {
82
        $this->getFormat();
83
      }
84
    }
85
  }
86
87
  /*
88
   * HTTP Basic/Digest + Proxy authorization can be defined in the
89
   * arc_reader_credentials config setting:
90
91
        'arc_reader_credentials' => array(
92
          'http://basic.example.com/' => 'user:pass', // shortcut for type=basic
93
          'http://digest.example.com/' => 'user::pass', // shortcut for type=digest
94
          'http://proxy.example.com/' => array('type' => 'basic', 'proxy', 'user' => 'user', 'pass' => 'pass'),
95
        ),
96
97
   */
98
99
  function setCredentials($url) {
100
    if (!$creds = $this->v('arc_reader_credentials', array(), $this->a))  return 0;
101
    foreach ($creds as $pattern => $creds) {
102
      /* digest shortcut (user::pass) */
103
      if (!is_array($creds) && preg_match('/^(.+)\:\:(.+)$/', $creds, $m)) {
104
        $creds = array('type' => 'digest', 'user' => $m[1], 'pass' => $m[2]);
105
      }
106
      /* basic shortcut (user:pass) */
107
      if (!is_array($creds) && preg_match('/^(.+)\:(.+)$/', $creds, $m)) {
108
        $creds = array('type' => 'basic', 'user' => $m[1], 'pass' => $m[2]);
109
      }
110
      if (!is_array($creds)) return 0;
111
      $regex = '/' . preg_replace('/([\:\/\.\?])/', '\\\\\1', $pattern) . '/';
112
      if (!preg_match($regex, $url)) continue;
113
      $mthd = 'set' . $this->camelCase($creds['type']) . 'AuthCredentials';
114
      if (method_exists($this, $mthd)) $this->$mthd($creds, $url);
115
    }
116
  }
117
118
  function setBasicAuthCredentials($creds) {
119
    $auth = 'Basic ' . base64_encode($creds['user'] . ':' . $creds['pass']);
120
    $h = in_array('proxy', $creds) ? 'Proxy-Authorization' : 'Authorization';
121
    $this->addCustomHeaders($h . ': ' . $auth);
122
    //echo $h . ': ' . $auth . print_r($creds, 1);
123
  }
124
125
  function setDigestAuthCredentials($creds, $url) {
126
    $path = $this->v1('path', '/', parse_url($url));
0 ignored issues
show
Bug introduced by
It seems like parse_url($url) targeting parse_url() can also be of type array<string,string>; however, ARC2_Class::v1() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
127
    $auth = '';
128
    $hs = $this->getResponseHeaders();
129
    /* initial 401 */
130
    $h = $this->v('www-authenticate', '', $hs);
131
    if ($h && preg_match('/Digest/i', $h)) {
132
      $auth = 'Digest ';
133
      /* Digest realm="$realm", nonce="$nonce", qop="auth", opaque="$opaque" */
134
      $ks = array('realm', 'nonce', 'opaque');/* skipping qop, assuming "auth" */
135
      foreach ($ks as $i => $k) {
136
        $$k = preg_match('/' . $k . '=\"?([^\"]+)\"?/i', $h, $m) ? $m[1] : '';
137
        $auth .= ($i ? ', ' : '') . $k . '="' . $$k . '"';
138
        $this->auth_infos[$k] = $$k;
139
      }
140
      $this->auth_infos['auth'] = $auth;
141
      $this->auth_infos['request_count'] = 1;
142
    }
143
    /* initial 401 or repeated request */
144
    if ($this->v('auth', 0, $this->auth_infos)) {
145
      $qop = 'auth';
146
      $auth = $this->auth_infos['auth'];
147
      $rc = $this->auth_infos['request_count'];
148
      $realm = $this->auth_infos['realm'];
149
      $nonce = $this->auth_infos['nonce'];
150
      $ha1 = md5($creds['user'] . ':' . $realm . ':' . $creds['pass']);
151
      $ha2 = md5($this->http_method . ':' . $path);
152
      $nc = dechex($rc);
153
      $cnonce = dechex($rc * 2);
154
      $resp = md5($ha1 . ':' . $nonce . ':' . $nc . ':' . $cnonce . ':' . $qop . ':' . $ha2);
155
      $auth .= ', username="' . $creds['user'] . '"' .
156
        ', uri="' . $path . '"' .
157
        ', qop=' . $qop . '' .
158
        ', nc=' . $nc .
159
        ', cnonce="' . $cnonce . '"' .
160
        ', uri="' . $path . '"' .
161
        ', response="' . $resp . '"' .
162
      '';
163
      $this->auth_infos['request_count'] = $rc + 1;
164
    }
165
    if (!$auth) return 0;
166
    $h = in_array('proxy', $creds) ? 'Proxy-Authorization' : 'Authorization';
167
    $this->addCustomHeaders($h . ': ' . $auth);
168
  }
169
170
  /*  */
171
172
  function useProxy($url) {
173
    if (!$this->v1('proxy_host', 0, $this->a)) {
174
      return false;
175
    }
176
    $skips = $this->v1('proxy_skip', array(), $this->a);
177
    foreach ($skips as $skip) {
178
      if (strpos($url, $skip) !== false) {
179
        return false;
180
      }
181
    }
182
    return true;
183
  }
184
185
  /*  */
186
  
187
  function createStream($path, $data = '') {
188
    $this->base = $this->calcBase($path);
189
    $this->stream = ($data) ? $this->getDataStream($data) : $this->getSocketStream($this->base);
190
  }
191
192
  function getDataStream($data) {
193
    return array('type' => 'data', 'pos' => 0, 'headers' => array(), 'size' => strlen($data), 'data' => $data, 'buffer' => '');
194
  }
195
  
196
  function getSocketStream($url) {
197
    if ($url == 'file://') {
198
      return $this->addError('Error: file does not exists or is not accessible');
199
    }
200
    $parts = parse_url($url);
201
    $mappings = array('file' => 'File', 'http' => 'HTTP', 'https' => 'HTTP');
202
    if ($scheme = $this->v(strtolower($parts['scheme']), '', $mappings)) {
203
      return $this->m('get' . $scheme . 'Socket', $url, $this->getDataStream(''));
204
    }
205
  }
206
  
207
  function getFileSocket($url) {
208
    $parts = parse_url($url);
209
    $s = file_exists($parts['path']) ? @fopen($parts['path'], 'rb') : false;
210
    if (!$s) {
211
      return $this->addError('Socket error: Could not open "' . $parts['path'] . '"');
212
    }
213
    return array('type' => 'socket', 'socket' =>& $s, 'headers' => array(), 'pos' => 0, 'size' => filesize($parts['path']), 'buffer' => '');
214
  }
215
  
216
  function getHTTPSocket($url, $redirs = 0, $prev_parts = '') {
217
    $parts = parse_url($url);
218
    /* relative redirect */
219
    if (!isset($parts['scheme']) && $prev_parts) $parts['scheme'] = $prev_parts['scheme'];
220
    if (!isset($parts['host']) && $prev_parts) $parts['host'] = $prev_parts['host'];
221
    /* no scheme */
222
    if (!$this->v('scheme', '', $parts)) return $this->addError('Socket error: Missing URI scheme.');
0 ignored issues
show
Bug introduced by
It seems like $parts defined by parse_url($url) on line 217 can also be of type array<string,string>; however, ARC2_Class::v() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
223
    /* port tweaks */
224
    $parts['port'] = ($parts['scheme'] == 'https') ? $this->v1('port', 443, $parts) : $this->v1('port', 80, $parts);
0 ignored issues
show
Bug introduced by
It seems like $parts defined by parse_url($url) on line 217 can also be of type array<string,string>; however, ARC2_Class::v1() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
225
    $nl = "\r\n";
226
    $http_mthd = strtoupper($this->http_method);
227
    if ($this->v1('user', 0, $parts) || $this->useProxy($url)) {
0 ignored issues
show
Bug introduced by
It seems like $parts defined by parse_url($url) on line 217 can also be of type array<string,string>; however, ARC2_Class::v1() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
228
      $h_code = $http_mthd . ' ' . $url;
229
    }
230
    else {
231
      $h_code = $http_mthd . ' ' . $this->v1('path', '/', $parts) . (($v = $this->v1('query', 0, $parts)) ? '?' . $v : '') . (($v = $this->v1('fragment', 0, $parts)) ? '#' . $v : '');
0 ignored issues
show
Bug introduced by
It seems like $parts defined by parse_url($url) on line 217 can also be of type array<string,string>; however, ARC2_Class::v1() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
232
    }
233
    $port_code = ($parts['port'] != 80) ? ':' . $parts['port'] : '';
234
    $h_code .= ' HTTP/1.0' . $nl.
235
      'Host: ' . $parts['host'] . $port_code . $nl .
236
      (($v = $this->http_accept_header) ? $v . $nl : '') .
237
      (($v = $this->http_user_agent_header) && !preg_match('/User\-Agent\:/', $this->http_custom_headers) ? $v . $nl : '') .
238
      (($http_mthd == 'POST') ? 'Content-Length: ' . strlen($this->message_body) . $nl : '') .
239
      ($this->http_custom_headers ? trim($this->http_custom_headers) . $nl : '') .
240
      $nl .
241
    '';
242
    /* post body */
243
    if ($http_mthd == 'POST') {
244
      $h_code .= $this->message_body . $nl;
245
    }
246
    /* connect */
247
    if ($this->useProxy($url)) {
248
      $s = @fsockopen($this->a['proxy_host'], $this->a['proxy_port'], $errno, $errstr, $this->timeout);
249
    }
250
    elseif (($parts['scheme'] == 'https') && function_exists('stream_socket_client')) {
251
      // SSL options via config array, code by Hannes Muehleisen ([email protected])
252
  	  $context = stream_context_create();
253
      foreach ($this->a as $k => $v) {
254
        if (preg_match('/^arc_reader_ssl_(.+)$/', $k, $m)) {
255
          stream_context_set_option($context, 'ssl', $m[1], $v);
256
        }
257
      }
258
      $s = stream_socket_client('ssl://' . $parts['host'] . $port_code, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $context);
259
    }
260
    elseif ($parts['scheme'] == 'https') {
261
      $s = @fsockopen('ssl://' . $parts['host'], $parts['port'], $errno, $errstr, $this->timeout);
262
    }
263
    elseif ($parts['scheme'] == 'http') {
264
      $s = @fsockopen($parts['host'], $parts['port'], $errno, $errstr, $this->timeout);
265
    }
266
    if (!$s) {
267
      return $this->addError('Socket error: Could not connect to "' . $url . '" (proxy: ' . ($this->useProxy($url) ? '1' : '0') . '): ' . $errstr);
268
    }
269
    /* request */
270
    fwrite($s, $h_code);
271
    /* timeout */
272
    if ($this->timeout) {
273
      //stream_set_blocking($s, false);
274
      stream_set_timeout($s, $this->timeout);
275
    }
276
    /* response headers */
277
    $h = array();
278
    $this->response_headers = $h;
279
    if (!$this->ping_only) {
280
      do {
281
        $line = trim(fgets($s, 4096));
282
        $info = stream_get_meta_data($s);
283
        if (preg_match("/^HTTP[^\s]+\s+([0-9]{1})([0-9]{2})(.*)$/i", $line, $m)) {/* response code */
284
          $error = in_array($m[1], array('4', '5')) ? $m[1] . $m[2] . ' ' . $m[3] : '';
285
          $error = ($m[1].$m[2] == '304') ? '304 '.$m[3] : $error;
286
          $h['response-code'] = $m[1] . $m[2];
287
          $h['error'] = $error;
288
          $h['redirect'] = ($m[1] == '3') ? true : false;
289
        }
290
        elseif (preg_match('/^([^\:]+)\:\s*(.*)$/', $line, $m)) {/* header */
291
          $h_name = strtolower($m[1]);
292
          if (!isset($h[$h_name])) {/* 1st value */
293
            $h[$h_name] = trim($m[2]);
294
          }
295
          elseif (!is_array($h[$h_name])) {/* 2nd value */
296
            $h[$h_name] = array($h[$h_name], trim($m[2]));
297
          }
298
          else {/* more values */
299
            $h[$h_name][] = trim($m[2]);
300
          }
301
        }
302
      } while(!$info['timed_out'] && !feof($s) && $line);
303
      $h['format'] = strtolower(preg_replace('/^([^\s]+).*$/', '\\1', $this->v('content-type', '', $h)));
304
      $h['encoding'] = preg_match('/(utf\-8|iso\-8859\-1|us\-ascii)/', $this->v('content-type', '', $h), $m) ? strtoupper($m[1]) : '';
305
      $h['encoding'] = preg_match('/charset=\s*([^\s]+)/si', $this->v('content-type', '', $h), $m) ? strtoupper($m[1]) : $h['encoding'];
306
      $this->response_headers = $h;
307
      /* result */
308
      if ($info['timed_out']) {
309
        return $this->addError('Connection timed out after ' . $this->timeout . ' seconds');
310
      }
311
      /* error */
312
      if ($v = $this->v('error', 0, $h)) {
313
        /* digest auth */
314
        /* 401 received */
315
        if (preg_match('/Digest/i', $this->v('www-authenticate', '', $h)) && !$this->digest_auth) {
316
          $this->setCredentials($url);
317
          $this->digest_auth = 1;
318
          return $this->getHTTPSocket($url);
319
        }
320
        return $this->addError($error . ' "' . (!feof($s) ? trim(strip_tags(fread($s, 128))) . '..."' : ''));
321
      }
322
      /* redirect */
323
      if ($this->v('redirect', 0, $h) && ($new_url = $this->v1('location', 0, $h))) {
324
        fclose($s);
325
        $this->redirects[$url] = $new_url;
326
        $this->base = $new_url;
327
        if ($redirs > $this->max_redirects) {
328
          return $this->addError('Max numbers of redirects exceeded.');
329
        }
330
        return $this->getHTTPSocket($new_url, $redirs+1, $parts);
331
      }
332
    }
333
    if ($this->timeout) {
334
      stream_set_blocking($s, true);
335
    }
336
    return array('type' => 'socket', 'url' => $url, 'socket' =>& $s, 'headers' => $h, 'pos' => 0, 'size' => $this->v('content-length', 0, $h), 'buffer' => '');
337
  }
338
339
  function readStream($buffer_xml = true, $d_size = 1024) {
340
    //if (!$s = $this->v('stream')) return '';
341
    if (!$s = $this->v('stream')) return $this->addError('missing stream in "readStream" ' . $this->uri);
342
    $s_type = $this->v('type', '', $s);
343
    $r = $s['buffer'];
344
    $s['buffer'] = '';
345
    if ($s['size']) $d_size = min($d_size, $s['size'] - $s['pos']);
346
    /* data */
347
    if ($s_type == 'data') {
348
      $d = ($d_size > 0) ? substr($s['data'], $s['pos'], $d_size) : '';
349
    }
350
    /* socket */
351
    elseif ($s_type == 'socket') {
352
      $d = ($d_size > 0) && !feof($s['socket']) ? fread($s['socket'], $d_size) : '';
353
    }
354
    $eof = $d ? false : true;
355
    /* chunked despite HTTP 1.0 request */
356
    if (isset($s['headers']) && isset($s['headers']['transfer-encoding']) && ($s['headers']['transfer-encoding'] == 'chunked')) {
357
      $d = preg_replace('/(^|[\r\n]+)[0-9a-f]{1,4}[\r\n]+/', '', $d);
358
    }
359
    $s['pos'] += strlen($d);
360
    if ($buffer_xml) {/* stop after last closing xml tag (if available) */
361
      if (preg_match('/^(.*\>)([^\>]*)$/s', $d, $m)) {
362
        $d = $m[1];
363
        $s['buffer'] = $m[2];
364
      }
365
      elseif (!$eof) {
366
        $s['buffer'] = $r . $d;
367
        $this->stream = $s;
368
        return $this->readStream(true, $d_size);
369
      }
370
    }
371
    $this->stream = $s;
372
    return $r . $d;
373
  }
374
  
375
  function closeStream() {
376
    if (isset($this->stream)) {
377
      if ($this->v('type', 0, $this->stream) == 'socket') {
378
        @fclose($this->stream['socket']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
379
      }
380
      unset($this->stream);
381
    }
382
  }
383
  
384
  /*  */
385
  
386
  function getFormat() {
387
    if (!$this->format) {
388
      if (!$this->v('stream')) {
389
        return $this->addError('missing stream in "getFormat"');
390
      }
391
      $v = $this->readStream(false);
392
      $mtype = $this->v('format', '', $this->stream['headers']);
393
      $this->stream['buffer'] = $v . $this->stream['buffer'];
394
      $ext = preg_match('/\.([^\.]+)$/', $this->uri, $m) ? $m[1] : '';
395
      $this->format = ARC2::getFormat($v, $mtype, $ext);
396
    }
397
    return $this->format;
398
  }
399
    
400
  /*  */
401
402
  function getResponseHeaders() {
403
    if (isset($this->stream) && isset($this->stream['headers'])) {
404
      return $this->stream['headers'];
405
    }
406
    return $this->response_headers;
407
  }
408
  
409
  function getEncoding($default = 'UTF-8') {
410
    return $this->v1('encoding', $default, $this->stream['headers']);
411
  }
412
413
  function getRedirects() {
414
    return $this->redirects;
415
  }
416
417
  function getAuthInfos() {
418
    return $this->auth_infos;
419
  }
420
  
421
  /*  */
422
  
423
}
424