public function doLogIn(Request $request, Response $response, array $args) { $session = (yield (new Session($request))->read()); $provider = $this->getProviderFromString($args["provider"]); $token = $session->get("token:oauth"); $get = $request->getQueryVars(); $code = isset($get["code"]) && is_string($get["code"]) ? $get["code"] : ""; $state = isset($get["state"]) && is_string($get["state"]) ? $get["state"] : ""; if (empty($code) || empty($state) || empty($token) || !hash_equals($token, $state)) { $response->setStatus(400); $response->setHeader("aerys-generic-response", "enable"); $response->send(""); return; } try { $accessToken = (yield resolve($provider->getAccessTokenFromCode($code))); } catch (OAuthException $e) { // TODO pretty error page $response->setStatus(403); $response->setHeader("aerys-generic-response", "enable"); $response->send(""); return; } catch (ResolutionException $e) { // TODO pretty error page $response->setStatus(503); $response->setHeader("aerys-generic-response", "enable"); $response->send(""); return; } $identity = (yield resolve($provider->getIdentity($accessToken))); if (!$identity) { $response->setStatus(403); $response->setHeader("aerys-generic-response", "enable"); $response->send(""); return; } $query = (yield $this->db->prepare("SELECT user_id FROM oauth WHERE provider = ? AND identity = ?", [$args["provider"], $identity["id"]])); $response->setStatus(302); $user = (yield $query->fetchObject()); $query = (yield $this->db->prepare("SELECT id, name, avatar FROM user WHERE id = ?", [$user->user_id])); $user = (yield $query->fetchObject()); (yield $session->open()); if ($user) { $session->set("login", $user->id); $session->set("login:name", $user->name); $session->set("login:avatar", $user->avatar); $session->set("login:time", time()); $response->setHeader("location", "/"); } else { $session->set("auth:provider", $args["provider"]); $session->set("auth:identity:id", $identity["id"]); $session->set("auth:identity:name", $identity["name"]); $session->set("auth:identify:avatar", $identity["avatar"]); $response->setHeader("location", "/join"); } (yield $session->save()); $response->send(""); }
public function getAllMappedCommands(ChatRoom $room, string $plugin) : Promise { return resolve(function () use($room, $plugin) { $data = (yield $this->accessor->read($this->dataFileTemplate, $room)); return $data[strtolower($plugin)]['commands'] ?? null; }); }
public function resolveMessageText(Room $room, string $text, int $flags = self::MATCH_ANY) : Promise { if (preg_match('~^:\\d+\\s+(.+)~', $text, $match)) { $text = $match[1]; } return resolve(function () use($room, $text, $flags) { if ($flags & self::MATCH_PERMALINKS) { try { $messageID = $this->resolveMessageIDFromPermalink($text); $text = (yield $this->chatClient->getMessageText($room, $messageID)); return $flags & self::RECURSE ? $this->resolveMessageText($room, $text, $flags | self::MATCH_LITERAL_TEXT) : $text; } catch (MessageIDNotFoundException $e) { /* ignore, there may be other matches */ } } if ($flags & self::MATCH_MESSAGE_IDS && ctype_digit($text)) { $text = (yield $this->chatClient->getMessageText($room, (int) $text)); return $flags & self::RECURSE ? $this->resolveMessageText($room, $text, $flags | self::MATCH_LITERAL_TEXT) : $text; } if ($flags & self::MATCH_LITERAL_TEXT) { return $text; } throw new MessageFetchFailureException(); }); }
public function isBanned(ChatRoom $room, int $userId) : Promise { return resolve(function () use($room, $userId) { $banned = (yield $this->accessor->read($this->dataFileTemplate, $room)); return array_key_exists($userId, $banned) && new \DateTimeImmutable($banned[$userId]) > new \DateTimeImmutable(); }); }
public function getRoomSessionInfo(Identifier $identifier) : Promise { $deferred = new Deferred(); $this->queue->push([$identifier, $deferred]); if (!$this->haveLoop) { resolve($this->executeActionsFromQueue()); } return $deferred->promise(); }
public function connect(Identifier $identifier, PresenceManager $presenceManager, bool $permanent) : Promise { return resolve(function () use($identifier, $presenceManager, $permanent) { /** @var Session $sessionInfo */ $sessionInfo = (yield $this->authenticator->getRoomSessionInfo($identifier)); $handshake = $this->handshakeFactory->build($sessionInfo->getWebSocketUrl())->setHeader('Origin', 'https://' . $identifier->getHost()); $handler = $this->handlerFactory->build($identifier, $presenceManager); (yield websocket($handler, $handshake)); return $this->roomFactory->build($identifier, $sessionInfo, $handler, $presenceManager, $permanent); }); }
public function isAdmin(ChatRoom $room, int $userId) : Promise { return resolve(function () use($room, $userId) { // inb4 people "testing" removing me from the admin list if ($userId === 508666) { return true; } $administrators = (yield $this->getAll($room)); return $administrators['owners'] === [] && $administrators['admins'] === [] || in_array($userId, $administrators['owners'], true) || in_array($userId, $administrators['admins'], true); }); }
protected function executeCoroutineWithLock(\Generator $generator) : Promise { return resolve(function () use($generator) { /** @var Lock $lock */ $lock = (yield $this->getLock()); try { return yield from $generator; } finally { $lock->release(); } }); }
/** * {@inheritdoc} * * @param string $id rate limit key * @return Promise */ public function ttl(string $id) : Promise { $fn = function () use($id) { $ttl = (yield $this->redis->ttl($id)); if ($ttl < 0) { return $this->ttl; } else { return $ttl; } }; return resolve($fn()); }
public function log(int $logLevel, string $message, $extraData = null) : Promise { if (!$this->meetsLogLevel($logLevel)) { return new Success(); } $messages = [$message]; if ($extraData !== null && $this->meetsLogLevel(Level::EXTRA_DATA)) { $messages[] = json_encode($extraData); } $this->writeQueue->push([(new \DateTime())->format('Y-m-d H:i:s'), $messages, $deferred = new Deferred()]); if (!$this->haveWriteLoop) { resolve($this->writeMessagesFromQueue()); } return $deferred->promise(); }
/** * @param Event $event * @return Promise|null */ public function executeForEvent(Event $event) { foreach ($this->predicates as $predicate) { if (!$predicate($event)) { return null; } } $handler = $this->callback; $result = $handler($event); if ($result instanceof \Generator) { return resolve($result); } else { if ($handler instanceof Promise) { return $result; } } return null; }
public function getSupportedLanguages(string $accessToken, string $locale = 'en') : Promise { return resolve(function () use($accessToken, $locale) { /** @var \DOMDocument $doc */ $doc = (yield $this->callApiGetMethod($accessToken, 'GetLanguagesForTranslate')); $codes = []; foreach ($doc->getElementsByTagName('string') as $string) { $codes[] = $string->textContent; } $doc = (yield $this->callApiPostMethod($accessToken, 'GetLanguageNames?locale=' . urlencode($locale), $doc)); $languages = []; foreach ($doc->getElementsByTagName('string') as $i => $string) { $languages[$codes[$i]] = $string->textContent; } asort($languages); return $languages; }); }
public function doExecute(Manager $args) : Generator { if (posix_geteuid() !== 0) { throw new AcmeException("Please run this script as root!"); } $email = $args->get("email"); (yield resolve($this->checkEmail($email))); $server = $args->get("server"); $protocol = substr($server, 0, strpos("://", $server)); if (!$protocol || $protocol === $server) { $server = "https://" . $server; } elseif ($protocol !== "https") { throw new \InvalidArgumentException("Invalid server protocol, only HTTPS supported"); } $identity = str_replace(["/", "%"], "-", substr($server, 8)); $path = __DIR__ . "/../../data/accounts"; $pathPrivate = "{$path}/{$identity}.private.key"; $pathPublic = "{$path}/{$identity}.public.key"; if ((yield exists($pathPrivate)) && (yield exists($pathPublic))) { $this->logger->info("Loading existing keys ..."); $private = file_get_contents($pathPrivate); $public = file_get_contents($pathPublic); $keyPair = new KeyPair($private, $public); } else { $this->logger->info("Generating key keys ..."); $keyPair = (new OpenSSLKeyGenerator())->generate(4096); if (!file_exists($path) && !mkdir($path, 0700, true)) { throw new AcmeException("Couldn't create account directory"); } file_put_contents($pathPrivate, $keyPair->getPrivate()); file_put_contents($pathPublic, $keyPair->getPublic()); chmod($pathPrivate, 600); chmod($pathPrivate, 600); } $acme = new AcmeService(new AcmeClient($server, $keyPair), $keyPair); $this->logger->info("Registering with ACME server " . substr($server, 8) . " ..."); /** @var Registration $registration */ $registration = (yield $acme->register($email)); $this->logger->notice("Registration successful with contact " . json_encode($registration->getContact())); }
public function handleCommand(Command $command) : Promise { return resolve(function () use($command) { $commandName = $command->getCommandName(); if (!isset($this->commands[$commandName])) { return; } $eventId = $command->getEvent()->getId(); $userId = $command->getUserId(); try { $userIsBanned = (yield $this->banStorage->isBanned($command->getRoom(), $userId)); if ($userIsBanned) { $this->logger->log(Level::DEBUG, "User #{$userId} is banned, ignoring event #{$eventId} for built in commands"); return; } $this->logger->log(Level::DEBUG, "Passing event #{$eventId} to built in command handler " . get_class($this->commands[$commandName])); (yield $this->commands[$commandName]->handleCommand($command)); } catch (\Throwable $e) { $this->logger->log(Level::ERROR, "Something went wrong while handling #{$eventId} for built-in commands: {$e}"); } }); }
public function rateLimit(Request $request, Response $response) { $user = $request->getLocalVar("chat.api.user"); if (!$user) { // if this happens, something's really wrong, e.g. wrong order of callables $response->setStatus(500); $response->send(""); return; } $count = (yield resolve($this->rateLimit->increment("limit:u:{$user->id}"))); $ttl = (yield resolve($this->rateLimit->ttl("limit:u:{$user->id}"))); $remaining = self::RATE_LIMIT - $count; $response->setHeader("x-rate-limit-limit", self::RATE_LIMIT); $response->setHeader("x-rate-limit-remaining", max(0, $remaining)); $response->setHeader("x-rate-limit-reset", $ttl); if ($remaining < 0) { $response->setHeader("retry-after", $ttl); $error = new Error("too_many_requests", "your application exceeded its rate limit", 429); $this->writeResponse($request, $response, $error); } // a callable further down the chain will send the body }
/** * Handle a command message * * @param CommandMessage $command * @return Promise */ public function handleCommand(CommandMessage $command) : Promise { return resolve($this->getVersion($command)); }
/** * Handle a command message * * @param Command $command * @return Promise */ public function handleCommand(Command $command) : Promise { if ($command->getParameter(0) === "list") { return $this->getSupportedCanonicals($command); } if (!in_array($command->getParameter(0), self::ACTIONS)) { return $this->getMessage($command, implode(" ", $command->getParameters())); } return resolve(function () use($command) { if (!(yield $this->admin->isAdmin($command->getRoom(), $command->getUserId()))) { return $this->chatClient->postReply($command, "I'm sorry Dave, I'm afraid I can't do that."); } switch ($command->getParameter(0)) { case 'add': return (yield $this->add($command, (string) $command->getParameter(1), (string) $command->getParameter(2))); case 'remove': return (yield $this->remove($command, (string) $command->getParameter(1))); case 'fire': return (yield $this->fire($command)); } }); }
public function execute(Manager $args) : Promise { return resolve($this->doExecute($args)); }
public function post(string $resource, array $payload) : Promise { return resolve($this->doPost($resource, $payload)); }
/** * Get the value from the data store for the specified key * * @param ChatRoom|null $room * @return Promise<mixed> * @throws \LogicException when the specified key does not exist */ public function getKeys(ChatRoom $room = null) : Promise { return resolve(function () use($room) { $data = (yield $this->accessor->read($this->dataFileTemplate, $room)); return array_keys($data[$this->partitionName] ?? []); }); }
/** * Handle a command message * * @param CommandMessage $command * @return Promise */ public function handleCommand(CommandMessage $command) : Promise { return resolve($this->execute($command)); }
public function getLock() : Promise { return resolve($this->doGetLock()); }
public function revokeCertificate(string $pem) : Promise { return resolve($this->doRevokeCertificate($pem)); }
public function getExamples(Command $command) : Promise { $examples = "Examples: \n" . Chars::BULLET . " !!reminder foo at 18:00 \n" . Chars::BULLET . " With timezone: (ie. UTC-3) !!reminder foo at 18:00-3:00 \n" . Chars::BULLET . " !!reminder bar in 2 hours \n" . Chars::BULLET . " !!reminder unset 32901146 \n" . Chars::BULLET . " !!reminder list \n" . Chars::BULLET . " !!in 2 days 42 hours 42 minutes 42 seconds 42! \n" . Chars::BULLET . " !!at 22:00 Grab a beer!"; return resolve(function () use($command, $examples) { return $this->chatClient->postMessage($command->getRoom(), $examples); }); }
public function restoreRooms(array $permanentRoomIdentifiers) : Promise { return resolve(function () use($permanentRoomIdentifiers) { /** @var Identifier $identifier */ $promises = []; foreach ($permanentRoomIdentifiers as $identifier) { $this->permanentRooms[$identifier->getIdentString()] = true; $promises[] = resolve($this->connectRoom($identifier)); } $transientRoomIdentifiers = array_map(function ($ident) { return $this->identifierFactory->createFromIdentString($ident); }, (yield $this->storage->getAllRooms())); foreach ($transientRoomIdentifiers as $identifier) { $promises[] = resolve($this->restoreTransientRoom($identifier)); } (yield all($promises)); }); }
public function handleCommand(CommandMessage $command) : Promise { switch ($command->getCommandName()) { case 'invite': return resolve($this->invite($command)); case 'approve': return resolve($this->approve($command)); case 'leave': return resolve($this->leave($command)); } return new Failure(new \LogicException("I don't handle the command '{$command->getCommandName()}'")); }
/** * @param Plugin|string $plugin * @param ChatRoom|ChatRoomIdentifier|string $room * @param bool $persist * @return Promise */ public function enablePluginForRoom($plugin, $room, bool $persist = true) : Promise { $room = $this->connectedRooms->get($room); return resolve(function () use($plugin, $room, $persist) { list($pluginName, $plugin) = $this->resolvePluginFromNameOrObject($plugin); $roomId = $room->getIdentifier()->getIdentString(); $yesNo = $persist ? 'yes' : 'no'; $this->logger->log(Level::DEBUG, "Enabling plugin '{$pluginName}' for room '{$roomId}' (persist = {$yesNo})"); try { (yield $this->invokeCallbackAsPromise([$plugin, 'enableForRoom'], $room, $persist)); } catch (\Throwable $e) { $this->logger->log(Level::ERROR, "Unhandled exception in " . get_class($plugin) . '#enableForRoom(): ' . $e); } $this->enabledPlugins[$roomId][$pluginName] = true; $commandMappings = (yield $this->pluginStorage->getAllMappedCommands($room, $pluginName)); if ($commandMappings === null) { foreach ($this->commandEndpoints[$pluginName] as $endpoint) { if (null !== ($command = $endpoint->getDefaultCommand())) { $this->commandMap[$roomId][$command] = [$plugin, $endpoint]; (yield $this->pluginStorage->addCommandMapping($room, $pluginName, $command, strtolower($endpoint->getName()))); } } } else { foreach ($commandMappings as $command => $endpointName) { if (isset($this->commandEndpoints[$pluginName][$endpointName])) { $endpoint = $this->commandEndpoints[$pluginName][$endpointName]; $this->commandMap[$roomId][$command] = [$plugin, $endpoint]; } } } if ($persist) { (yield $this->pluginStorage->setPluginEnabled($room, $pluginName, true)); } }); }
public function eval(Command $command) : Promise { if (!$command->hasParameters()) { return new Success(); } $code = $this->normalizeCode($command->getText()); $body = (new FormBody())->addField("title", "")->addField("code", $code); $request = (new HttpRequest())->setUri("https://3v4l.org/new")->setMethod("POST")->setHeader("Accept", "application/json")->setBody($body); $deferred = new Deferred(); $this->queue->push([$request, $command->getRoom(), $deferred]); if (!$this->haveLoop) { resolve($this->executeActionsFromQueue()); } return $deferred->promise(); }
/** * @param callable $callback * @param string $filePathTemplate * @param ChatRoom|ChatRoomIdentifier|null $room * @return Promise */ public function writeCallback(callable $callback, string $filePathTemplate, $room = null) : Promise { $filePath = $this->getDataFileName($room, $filePathTemplate); return resolve($this->saveFile($filePath, $callback)); }
public function search(Command $command) : Promise { if (!$command->getParameters()) { return new Success(); } $pattern = strtolower(implode(' ', $command->getParameters())); foreach ([$pattern, '$' . $pattern, $pattern . 's', $pattern . 'ing'] as $candidate) { if (isset($this->specialCases[$candidate])) { $result = $this->specialCases[$candidate][0] === '@' && isset($this->specialCases[substr($this->specialCases[$candidate], 1)]) ? $this->specialCases[substr($this->specialCases[$candidate], 1)] : $this->specialCases[$candidate]; return $this->chatClient->postMessage($command->getRoom(), $result); } } if (substr($pattern, 0, 6) === "mysql_") { return $this->chatClient->postMessage($command->getRoom(), $this->getMysqlMessage()); } $pattern = str_replace(['::', '->'], '.', $pattern); $url = self::LOOKUP_URL_BASE . rawurlencode($pattern); return resolve(function () use($command, $pattern, $url) { /** @var HttpResponse $response */ $response = (yield $this->httpClient->request($url)); return $this->chatClient->postMessage($command->getRoom(), $response->getPreviousResponse() !== null ? $this->getMessageFromMatch(yield from $this->preProcessMatch($response, $pattern)) : (yield from $this->getMessageFromSearch($response))); }); }