1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php
function curl_exec_follow($ch, $max=5) {
	$response = false;
	
	// Common cURL options that need to be turned on for this to work and return consistently
	curl_setopt($ch, CURLOPT_HEADER,true);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
		
	if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
		// Can just use FOLLOWLOCATION, still need to parse the headers/status/content though.
		// to maintain a consistent return.
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_MAXREDIRS, $max);
		$response = curl_exec($ch);
		
		if ($response !== false) {
			$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
			$response = array(
				'headers' => substr($response, 0, $headerSize),
				'content' => substr($response, $headerSize),
				'status' => curl_getinfo($ch, CURLINFO_HTTP_CODE)
			);
		}
	} 
	else {
		// explicitly turn FOLLOWLOCATION off, just in case servers do weird things.
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
		// use a local copy of the cURL handle.
		$local = curl_copy_handle($ch);
		$redirected = false;
		
		$target = curl_getinfo($local, CURLINFO_EFFECTIVE_URL);
		$last = parse_url($target); // need to maintain a reference to the last redirect in case following redirects are relative paths.
		if (empty($last['hostname'])) {
			// relative path on the server, very trivial attempt to build a correct path so it may not work in all cases
			$last = parse_url("http" . (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 's://' : '://') . $_SERVER['HTTP_HOST'] . $target);
		}
		
		$redirectCodes = array(301,302,307); // no support for 305 proxy redirects...yet :)
		$headers = array(); // maintain the headers throughout the redirection.
		
		do {
			curl_setopt($local, CURLOPT_URL, $target);
			if (!($response = curl_exec($local))) {
				break;
			}
			
			$code = curl_getinfo($local, CURLINFO_HTTP_CODE);
			$headerSize = curl_getinfo($local, CURLINFO_HEADER_SIZE);
			$header = substr($response, 0, $headerSize);
			$headers[] = rtrim($header);
			$redirected = in_array($code, $redirectCodes);
			
			if ($redirected) {
				if (preg_match('/^(?:Location|URI):\\s*([^\\r\\n]+)/m', $header, $matches) !== 1) {
					trigger_error("Requested resource is redirecting incorrectly.", E_USER_WARNING);
					$redirected = false; // set the loop-breaking condition.
					$response = false; // invalidate the response
				}
				
				$target = parse_url(trim($matches[1]));
				if (empty($target['scheme'])) { // relative redirect, use the last url
					$target = $last['scheme'] . '://' . $last['host'] . $target['path'];
					$last = parse_url($target);
				}
				elseif ($target['scheme'] === 'file') {
					// Redirecting into the file:// protocol, can be unsafe
					trigger_error("Requested resource is attempting to redirect to files on the local system.", E_USER_WARNING);
					$redirected = false; // set the loop-breaking condition.
					$response = false; // invalidate the response
				}
				else {
					$target = trim($matches[1]);
					$last = parse_url($target);
				}
				
				if ($code === 302) {
					// Many browsers implemented the 302-redirect in this way. i.e subsequent redirects
					// used the GET method, even when the original method used POST. 
					// See http://en.wikipedia.org/wiki/HTTP_302 for more information.
					curl_setopt($local, CURLOPT_HTTPGET, true);
				}	
			}
		} while($redirected && --$max >= 0);

		if ($response !== false) {
			$response = array(
				'headers' => implode("\r\n\r\n", $headers), // standard is an CRLF seperator between headers as far as I know.
				'content' => substr($response, curl_getinfo($local, CURLINFO_HEADER_SIZE)),
				'status' => curl_getinfo($local, CURLINFO_HTTP_CODE)
			);
		}
		
		curl_close($local);
	}
	
	return $response;
}
?>