/** * Run the watcher. * * Listenable events: * • new, when a file is new, i.e. found by the finder; * • modify, when a file has been modified; * • move, when a file has moved, i.e. no longer found by the finder. * * @return void */ public function run() { $iterator = $this->getIterator(); $previous = iterator_to_array($iterator); $current = $previous; while (true) { foreach ($current as $name => $c) { if (!isset($previous[$name])) { $this->_on->fire('new', new Core\Event\Bucket(['file' => $c])); continue; } if (null === $c->getHash()) { unset($current[$name]); continue; } if ($previous[$name]->getHash() != $c->getHash()) { $this->_on->fire('modify', new Core\Event\Bucket(['file' => $c])); } unset($previous[$name]); } foreach ($previous as $p) { $this->_on->fire('move', new Core\Event\Bucket(['file' => $p])); } usleep($this->getLatency() * 1000000); $previous = $current; $current = iterator_to_array($iterator); } return; }
/** * Run the server. * * @return void */ public function run() { $this->_server->considerRemoteAddress(true); $this->_server->connectAndWait(); while (true) { $buffer = $this->_server->read(1024); if (empty($buffer)) { continue; } // Skip header. $handle = substr($buffer, 12); $domain = null; // QNAME. for ($i = 0, $m = strlen($handle); $i < $m; ++$i) { if (0 === ($length = ord($handle[$i]))) { break; } if (null !== $domain) { $domain .= '.'; } $domain .= substr($handle, $i + 1, $length); $i += $length; } // QTYPE. $i += 2; $qtype = (int) (string) ord($handle[$i]) + (int) (string) ord($handle[$i + 1]); $type = array_search($qtype, static::$_types) ?: $qtype; // QCLASS. $i += 2; $qclass = (int) (string) ord($handle[$i]); $class = array_search($qclass, static::$_classes) ?: $qclass; $ips = $this->_on->fire('query', new Core\Event\Bucket(['domain' => $domain, 'type' => $type, 'class' => $class])); $ip = null; if (false === $ips[0]) { $this->_server->writeAll($buffer[0] . $buffer[1] . pack('C', 1 << 7 | 1) . pack('C', 0 | 3) . pack('n', 0) . pack('n', 0) . pack('n', 0) . pack('n', 0)); continue; } foreach (explode('.', $ips[0]) as $foo) { $ip .= pack('C', $foo); } $this->_server->writeAll($buffer[0] . $buffer[1] . pack('C', 1 << 7 | 1) . pack('C', 0) . $buffer[4] . $buffer[5] . pack('n', 1) . pack('n', 0) . pack('n', 0) . $handle . pack('CC', 192, 12) . pack('n', $qtype) . pack('n', $qclass) . pack('N', 60) . pack('n', 4) . $ip); } $this->_server->disconnect(); return; }
/** * Run a node. * * @param \Hoa\Socket\Node $node Node. * @return void * @throws \Hoa\Irc\Exception */ protected function _run(Socket\Node $node) { if (false === $node->hasJoined()) { $node->setJoined(true); $this->_on->fire('open', new Core\Event\Bucket()); return; } try { $line = $node->getConnection()->readLine(); preg_match('#^(?::(?<prefix>[^\\s]+)\\s+)?(?<command>[^\\s]+)\\s+(?<middle>[^:]+)?(:\\s*(?<trailing>.+))?$#', $line, $matches); if (!isset($matches['command'])) { $matches['command'] = null; } switch ($matches['command']) { case 366: // RPL_ENDOFNAMES list($nickname, $channel) = explode(' ', $matches['middle'], 2); $node->setChannel($channel); $listener = 'join'; $bucket = ['nickname' => $nickname, 'channel' => trim($channel)]; break; case 433: // ERR_NICKNAMEINUSE throw new Exception('Nickname %s is already in use.', 0, $node->getUsername()); case 'PRIVMSG': $middle = trim($matches['middle']); $message = $matches['trailing']; $username = $node->getUsername(); if ($username === $middle) { $listener = 'private-message'; } elseif (false !== strpos($message, $username)) { $node->setChannel($middle); $listener = 'mention'; } else { $node->setChannel($middle); $listener = 'message'; } $bucket = ['from' => $this->parseNick($matches['prefix']), 'message' => $message]; break; case 'PING': $daemons = explode(' ', $matches['trailing']); $listener = 'ping'; $bucket = ['daemons' => $daemons]; if (isset($daemons[1])) { $this->pong($daemons[0], $daemons[1]); } else { $this->pong($daemons[0]); } break; case 'KICK': list($channel, ) = explode(' ', $matches['middle'], 2); $node->setChannel($channel); $listener = 'kick'; $bucket = ['from' => $this->parseNick($matches['prefix']), 'channel' => trim($channel)]; break; case 'INVITE': list($channel, ) = explode(' ', $matches['middle'], 2); $node->setChannel($channel); $listener = 'invite'; $bucket = ['from' => $this->parseNick($matches['prefix']), 'channel' => trim($channel), 'invitation_channel' => trim($matches['trailing'])]; break; default: $listener = 'other-message'; $bucket = ['line' => $line, 'parsed_line' => $matches]; } $this->_on->fire($listener, new Core\Event\Bucket($bucket)); } catch (Core\Exception\Idle $e) { $this->_on->fire('error', new Core\Event\Bucket(['exception' => $e])); } return; }
/** * Notification callback. * * @param int $ncode Notification code. Please, see * STREAM_NOTIFY_* constants. * @param int $severity Severity. Please, see * STREAM_NOTIFY_SEVERITY_* constants. * @param string $message Message. * @param int $code Message code. * @param int $transferred If applicable, the number of transferred * bytes. * @param int $max If applicable, the number of max bytes. * @return void */ public function _notify($ncode, $severity, $message, $code, $transferred, $max) { static $_map = [STREAM_NOTIFY_AUTH_REQUIRED => 'authrequire', STREAM_NOTIFY_AUTH_RESULT => 'authresult', STREAM_NOTIFY_COMPLETED => 'complete', STREAM_NOTIFY_CONNECT => 'connect', STREAM_NOTIFY_FAILURE => 'failure', STREAM_NOTIFY_MIME_TYPE_IS => 'mimetype', STREAM_NOTIFY_PROGRESS => 'progress', STREAM_NOTIFY_REDIRECTED => 'redirect', STREAM_NOTIFY_RESOLVE => 'resolve', STREAM_NOTIFY_FILE_SIZE_IS => 'size']; $this->_on->fire($_map[$ncode], new Core\Event\Bucket(['code' => $code, 'severity' => $severity, 'message' => $message, 'code' => $code, 'transferred' => $transferred, 'max' => $max])); return; }
/** * Run the shared worker. * It creates a zombie with \Hoa\Zombie. * * @return void * @throws \Hoa\Worker\Backend\Exception */ public function run() { $server = new Socket\Server($this->_socket); $server->connectAndWait(); Zombie::fork(); $_eom = pack('C', 0); while (true) { foreach ($server->select() as $node) { $request = unpack('nr', $server->read(2)); $length = unpack('Nl', $server->read(4)); $message = unserialize($server->read($length['l'])); $eom = unpack('Ce', $server->read(1)); if ($eom['e'] != $_eom) { $server->disconnect(); continue; } switch ($request['r']) { case static::TYPE_MESSAGE: $this->_on->fire('message', new Core\Event\Bucket(['message' => $message])); ++$this->_messages; $this->_lastMessage = time(); break; case static::TYPE_STOP: if ($this->_password === $message) { $server->disconnect(); break 3; } break; case static::TYPE_INFORMATION: $message = ['id' => $this->_wid, 'socket' => $this->_socket, 'start' => $this->_startTime, 'pid' => getmypid(), 'memory' => memory_get_usage(true), 'memory_allocated' => memory_get_usage(), 'memory_peak' => memory_get_peak_usage(true), 'memory_allocated_peak' => memory_get_usage(), 'messages' => $this->_messages, 'last_message' => $this->_lastMessage, 'filename' => $_SERVER['SCRIPT_FILENAME']]; $server->writeAll(static::pack(static::TYPE_MESSAGE, $message)); break; } $server->disconnect(); } } $server->disconnect(); if (null !== $this->_wid) { Worker\Run::unregister($this->_wid); } return; }
/** * Run a node. * * @param \Hoa\Socket\Node $node Node. * @return void */ protected function _run(Socket\Node $node) { try { if (FAILED === $node->getHandshake()) { $this->doHandshake(); $this->_on->fire('open', new Core\Event\Bucket()); return; } try { $frame = $node->getProtocolImplementation()->readFrame(); } catch (Exception\CloseError $e) { $this->close($e->getErrorCode(), $e->getMessage()); return; } if (false === $frame) { return; } if ($this instanceof Server && isset($frame['mask']) && 0x0 === $frame['mask']) { $this->close(self::CLOSE_MESSAGE_ERROR, 'All messages from the client must be masked.'); return; } $fromText = false; $fromBinary = false; switch ($frame['opcode']) { case self::OPCODE_BINARY_FRAME: $fromBinary = true; case self::OPCODE_TEXT_FRAME: if (0x1 === $frame['fin']) { if (0 < $node->getNumberOfFragments()) { $this->close(self::CLOSE_PROTOCOL_ERROR); break; } if (true === $fromBinary) { $fromBinary = false; $this->_on->fire('binary-message', new Core\Event\Bucket(['message' => $frame['message']])); break; } if (false === (bool) preg_match('//u', $frame['message'])) { $this->close(self::CLOSE_MESSAGE_ERROR); break; } $this->_on->fire('message', new Core\Event\Bucket(['message' => $frame['message']])); break; } else { $node->setComplete(false); } $fromText = true; case self::OPCODE_CONTINUATION_FRAME: if (false === $fromText) { if (0 === $node->getNumberOfFragments()) { $this->close(self::CLOSE_PROTOCOL_ERROR); break; } } else { $fromText = false; if (true === $fromBinary) { $node->setBinary(true); $fromBinary = false; } } $node->appendMessageFragment($frame['message']); if (0x1 === $frame['fin']) { $message = $node->getFragmentedMessage(); $isBinary = $node->isBinary(); $node->clearFragmentation(); if (true === $isBinary) { $this->_on->fire('binary-message', new Core\Event\Bucket(['message' => $message])); break; } if (false === (bool) preg_match('//u', $message)) { $this->close(self::CLOSE_MESSAGE_ERROR); break; } $this->_on->fire('message', new Core\Event\Bucket(['message' => $message])); } else { $node->setComplete(false); } break; case self::OPCODE_PING: $message =& $frame['message']; if (0x0 === $frame['fin'] || 0x7d < $frame['length']) { $this->close(self::CLOSE_PROTOCOL_ERROR); break; } $node->getProtocolImplementation()->writeFrame($message, self::OPCODE_PONG, true); $this->_on->fire('ping', new Core\Event\Bucket(['message' => $message])); break; case self::OPCODE_PONG: if (0 === $frame['fin']) { $this->close(self::CLOSE_PROTOCOL_ERROR); break; } break; case self::OPCODE_CONNECTION_CLOSE: $length =& $frame['length']; if (1 === $length || 0x7d < $length) { $this->close(self::CLOSE_PROTOCOL_ERROR); break; } $code = self::CLOSE_NORMAL; $reason = null; if (0 < $length) { $message =& $frame['message']; $_code = unpack('nc', substr($message, 0, 2)); $code =& $_code['c']; if (1000 > $code || 1004 <= $code && $code <= 1006 || 1012 <= $code && $code <= 1016 || 5000 <= $code) { $this->close(self::CLOSE_PROTOCOL_ERROR); break; } if (2 < $length) { $reason = substr($message, 2); if (false === (bool) preg_match('//u', $reason)) { $this->close(self::CLOSE_MESSAGE_ERROR); break; } } } $this->close(self::CLOSE_NORMAL); $this->_on->fire('close', new Core\Event\Bucket(['code' => $code, 'reason' => $reason])); break; default: $this->close(self::CLOSE_PROTOCOL_ERROR); } } catch (Core\Exception\Idle $e) { try { $this->close(self::CLOSE_SERVER_ERROR); $exception = $e; } catch (Core\Exception\Idle $ee) { $this->getConnection()->disconnect(); $exception = new Core\Exception\Group('An exception has been thrown. We have tried to close ' . 'the connection but another exception has been thrown.', 42); $exception[] = $e; $exception[] = $ee; } $this->_on->fire('error', new Core\Event\Bucket(['exception' => $exception])); } return; }