summaryrefslogtreecommitdiff
path: root/lib/Kolab/FreeBusy/Utils.php
blob: ba1e736951dfdd82fa98e1cec020e5e34a10c3f2 (plain)
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?php

namespace Kolab\FreeBusy;

/**
 * Static calss providing utility functions for the Free/Busy service
 */
class Utils
{
	/**
	 * Resolve the given directory to a real path ending with $append
	 *
	 * @param string Arbitrary directory directory path
	 * @param string Make path end with this string/character
	 * @return string Absolute file system path
	 */
	public static function abspath($dirname, $append = '')
	{
		if ($dirname[0] != '/')
			$dirname = realpath(KOLAB_FREEBUSY_ROOT . '/' . $dirname);

		return rtrim($dirname, '/') . $append;
	}

	/**
	 * Returns remote IP address and forwarded addresses if found
	 *
	 * @return string Remote IP address(es)
	 */
	public static function remoteIP()
	{
		$address = $_SERVER['REMOTE_ADDR'];

		// use the NGINX X-Real-IP header, if set
		if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
			$address = $_SERVER['HTTP_X_REAL_IP'];
		}
		// use the X-Forwarded-For header, if set
		if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
			$address = $_SERVER['HTTP_X_FORWARDED_FOR'];
		}

		return $address;
	}


	/**
	 * Checks if the given IP address is in one of the provided ranges
	 *
	 * @param string   IP address
	 * @param array    List of IP ranges/subnets to check against
	 * @return boolean True if in range, False if not
	 */
	public static function checkIPRange($ip, $ranges)
	{
		$ipv6 = strpos($ip, ':') !== false;
		$ipbin = $ipv6 ? self::ip6net2bits($ip) : ip2long($ip);

		foreach ((array)$ranges as $range) {
			// don't compare IPv4 and IPv6 addresses/ranges
			$rangev6 = strpos($range, ':') !== false;
			if ($ipv6 != $rangev6) {
				continue;
			}

			// quick substring check (e.g. 192.168.0.)
			if (( $ipv6 && strpos($ipbin, self::ip6net2bits($range)) === 0) ||
				(!$ipv6 && strpos($ip, rtrim($range, '*')) === 0)) {
				return true;
			}

			// range from-to specified (IPv4 only)
			list($lower, $upper) = explode('-', $range);
			if (strlen($upper) && !$ipv6) {
				if ($ipbin >= ip2long(trim($lower)) && $ipbin <= ip2long(trim($upper))) {
					return true;
				}
			}

			// subnet/length is given
			list($subnet, $bits) = explode('/', $range);

			// IPv6 subnet
			if (strlen($bits) && $ipv6) {
				$subnetbin = self::ip6net2bits($subnet);
				if (substr($ipbin, 0, $bits) === substr($subnetbin, 0, $bits)) {
					return true;
				}
			}
			// IPv4 subnet
			else if (strlen($bits)) {
				$subnet = ip2long($subnet);
				$mask = -1 << $bits;
				$subnet &= $mask;  // just in case the supplied subnet wasn't correctly aligned
				if (($ipbin & $mask) == $subnet) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Convert the given IPv6 address to a binary string representation.
	 * (from http://stackoverflow.com/questions/7951061/matching-ipv6-address-to-a-cidr-subnet)
	 */
	public static function ip6net2bits($inet)
	{
		$binaryip = '';
		$unpacked = @unpack('A16', inet_pton($inet));
		foreach (str_split($unpacked[1]) as $char) {
			$binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
		}
		return $binaryip;
	}

}