27 class Identity implements \Erebot\Interfaces\Identity
51 if (!is_string($user)) {
52 throw new \Erebot\InvalidValueException(
'Not a valid identity');
58 $pos = strpos($user,
'!');
60 $parts = explode(
'@', substr($user, $pos + 1));
61 if (count($parts) != 2) {
62 throw new \Erebot\InvalidValueException(
'Invalid mask');
65 $nick = substr($user, 0, $pos);
69 if ($nick ===
false || $ident ==
'' || $host ==
'') {
70 throw new \Erebot\InvalidValueException(
'Invalid mask');
72 } elseif (strpos($user,
'@') !==
false) {
74 throw new \Erebot\InvalidValueException(
'Invalid mask');
80 $this->ident = $ident;
85 $this->host = self::canonicalizeHost(
87 \Erebot\Interfaces\Identity::CANON_IPV6,
93 public function getNick()
98 public function getIdent()
124 $stripped = ltrim($number,
'0');
125 $number = ($stripped ==
'' ?
'0' : $stripped);
167 if ($c10n != \Erebot\Interfaces\Identity::CANON_IPV4 &&
168 $c10n != \Erebot\Interfaces\Identity::CANON_IPV6) {
169 throw new \Erebot\InvalidValueException(
170 'Invalid canonicalization value'
174 $decOctet =
'(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])';
175 $dotAddress = $decOctet.
'(?:\\.'.$decOctet.
'){3}';
179 if (preg_match(
'/^'.$dotAddress.
'$/Di', $host)) {
180 $parts = explode(
'.', $host, 4);
181 $prefix = ($uncompressed ?
'0:0:0:0:0' :
':');
182 if ($c10n == \Erebot\Interfaces\Identity::CANON_IPV4) {
183 array_walk($parts, array(
'self',
'stripLeading'));
184 return $prefix.
':ffff:'.implode(
'.', $parts);
188 sprintf(
'%02x%02x', $parts[0], $parts[1]),
189 sprintf(
'%02x%02x', $parts[2], $parts[3]),
191 array_walk($mapped, array(
'self',
'stripLeading'));
192 return $prefix.
':ffff:'.implode(
':', $mapped);
198 $label =
'[A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9]?';
199 $hostname =
'(?:'.$label.
'\.)*'.$label;
202 if (preg_match(
'/^'.$hostname.
'$/Di', $host)) {
206 $last = strrchr($host,
'.');
207 if ($last ===
false || strspn($last,
'.1234567890') != strlen($last)) {
208 return strtolower($host);
212 $half =
'[[:xdigit:]]{1,4}';
213 $long =
'(?:'.$half.
':'.$half.
'|('.$dotAddress.
'))';
216 '(?:'.$half.
':){6}'.$long.
'|'.
217 '::(?:'.$half.
':){5}'.$long.
'|'.
218 '(?:'.$half.
')?::(?:'.$half.
':){4}'.$long.
'|'.
219 '(?:(?:'.$half.
':)?'.$half.
')?::(?:'.$half.
':){3}'.$long.
'|'.
220 '(?:(?:'.$half.
':){0,2}'.$half.
')?::(?:'.$half.
':){2}'.$long.
'|'.
221 '(?:(?:'.$half.
':){0,3}'.$half.
')?::'.$half.
':'.$long.
'|'.
222 '(?:(?:'.$half.
':){0,4}'.$half.
')?::'.$long.
'|'.
223 '(?:(?:'.$half.
':){0,5}'.$half.
')?::'.$half.
'|'.
224 '(?:(?:'.$half.
':){0,6}'.$half.
')?::'.
228 if (!preg_match(
'/^'.$colonAddress.
'$/Di', $host, $matches)) {
229 throw new \Erebot\InvalidValueException(
230 'Unrecognized "host" ('.$host.
')'
235 if (count($matches) > 1) {
237 $mapped = end($matches);
238 $parts = explode(
'.', $mapped, 4);
240 sprintf(
'%02x%02x', $parts[0], $parts[1]),
241 sprintf(
'%02x%02x', $parts[2], $parts[3]),
243 array_walk($mapped, array(
'self',
'stripLeading'));
244 $host = str_replace(end($matches), implode(
':', $mapped), $host);
248 $pos = strpos($host,
'::');
249 if ($pos !==
false) {
250 if (substr($host, 0, 2) ==
'::') {
253 if (substr($host, -2) ==
'::') {
256 $repeat = 8 - substr_count($host,
':');
257 $host = str_replace(
'::',
':'.str_repeat(
'0:', $repeat), $host);
261 $parts = explode(
':', $host, 8);
262 array_walk($parts, array(
'self',
'stripLeading'));
263 if ($c10n == \Erebot\Interfaces\Identity::CANON_IPV4) {
264 $parts[7] = (hexdec($parts[6]) << 16) + hexdec($parts[7]);
265 $parts[6] = long2ip(array_pop($parts));
269 return strtolower(implode(
':', $parts));
273 $host =
'x:' . implode(
':', $parts) .
':x';
274 for ($i = 8; $i > 1; $i--) {
275 $s =
':'.str_repeat(
'0:', $i);
276 $pos = strpos($host, $s);
277 if ($pos !==
false) {
278 $host = (string) substr($host, 0, $pos) .
'::' .
279 (string) substr($host, $pos + strlen($s));
284 $host = str_replace(array(
'x::',
'::x'),
'::', $host);
285 $host = str_replace(array(
'x:',
':x'),
'', $host);
286 return strtolower($host);
289 public function getHost($c10n)
291 if ($this->host === null) {
294 if ($c10n == \Erebot\Interfaces\Identity::CANON_IPV6) {
297 return self::canonicalizeHost($this->host, $c10n,
false);
300 public function getMask($c10n)
302 $ident = ($this->ident === null) ?
'*' : $this->ident;
303 $host = ($this->host === null) ?
'*' : $this->getHost($c10n);
304 return $this->nick.
'!'.$ident.
'@'.$host;
307 public function __toString()
326 public function match($pattern, \Erebot\Interfaces\IrcCollator $collator)
328 $nick = explode(
'!', $pattern, 2);
329 if (count($nick) != 2) {
333 $ident = explode(
'@', $nick[1], 2);
334 if (count($ident) != 2) {
342 if ($ident ==
'' || $host ==
'') {
346 $nick = $collator->normalizeNick($nick);
347 $thisNick = $collator->normalizeNick($this->nick);
348 if (!preg_match(self::patternize($nick,
true), $thisNick)) {
352 $thisIdent = ($this->ident === null) ?
'' : $this->ident;
353 if (!preg_match(self::patternize($ident,
true), $thisIdent)) {
358 ($this->host === null) ?
360 self::canonicalizeHost(
362 \Erebot\Interfaces\Identity::CANON_IPV6,
370 $decOctet =
'(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5]|\\*)';
371 $dotAddress = $decOctet.
'(?:\\.'.$decOctet.
'){3}(?:/[0-9]*)?';
372 $dotAddress =
'#^'.$dotAddress.
'$#Di';
373 $isDottedAddress = (bool) preg_match($dotAddress, $host);
376 if (strpos($host,
':') ===
false && !$isDottedAddress) {
377 return (
bool) preg_match(
378 self::patternize($host,
false),
384 $host = explode(
'/', $host, 2);
385 if (strpos($host[0],
'*') !==
false) {
386 if (count($host) == 2) {
387 throw new \Erebot\InvalidValueException(
388 "Wildcard characters and netmasks ".
389 "don't go together very well"
395 for ($i = 0; $i < 4; $i++) {
396 $sep = (($i == 3) ? ($isDottedAddress ?
'' :
':') :
'.');
397 if (substr($host[0], -2) == $sep .
'*') {
399 $host[0] = substr($host[0], 0, -2);
400 $replace = $sep.
'0'.$replace;
403 $host[0] .= $replace;
407 } elseif (count($host) == 1) {
411 $host[1] = ((int) $host[1]) + ($isDottedAddress ? 96 : 0);
414 if ($host[1] < 0 || $host[1] > 128) {
415 throw new \Erebot\InvalidValueException(
416 'Invalid netmask value ('.$host[1].
')'
420 $host[0] = self::canonicalizeHost(
422 \Erebot\Interfaces\Identity::CANON_IPV6,
426 $pattParts = explode(
':', $host[0]);
427 $thisParts = explode(
':', $thisHost);
428 while ($host[1] > 0) {
429 $mask = 0x10000 - (1 << (16 - min($host[1], 16)));
430 $pattValue = hexdec(array_shift($pattParts)) & $mask;
431 $thisValue = hexdec(array_shift($thisParts)) & $mask;
432 if ($pattValue != $thisValue) {
460 $mapping = array(
'[^\\.]',
'.');
461 for ($i = 0, $len = strlen($pattern); $i < $len; $i++) {
462 switch ($pattern[$i]) {
466 $realPattern .= $mapping[1];
471 if ((($i + 1) < $len && $pattern[$i + 1] ==
'*')) {
472 $realPattern .= $mapping[1];
475 $realPattern .= $mapping[0];
479 if ($pattern[$i] ==
'*') {
485 $realPattern .= preg_quote($pattern[$i],
'#');
488 return '#^'.$realPattern.
'$#Di';
static patternize($pattern, $matchDot)
$host
Host part for this user identity, either a string or null.
$nick
Nickname for this user identity, either a string or null.
static stripLeading(&$number, $key)
static canonicalizeHost($host, $c10n, $uncompressed)
Represents the identity of an IRC user.
$ident
Identity string for this user identity, either a string or null.
match($pattern,\Erebot\Interfaces\IrcCollator $collator)