public function handle(Command $command, ClientContext $context) : Generator { yield from $context->writeResponse(new Response(101, 'Capability list follows (multi-line)')); // Generate the list of capabilities that we support. $capabilities = ['VERSION 2', 'READER', 'POST', 'NEWNEWS', 'LIST ACTIVE', 'IMPLEMENTATION coderstephen/nntp server']; // If TLS is not already active, the STARTTLS is available. if (!$context->getSocket()->isCryptoEnabled()) { //array_splice($capabilities, -2, 0, ['STARTTLS']); } $data = implode("\r\n", $capabilities); yield from $context->writeData($data); }
public function handle(Command $command, ClientContext $context) : Generator { yield from $context->writeResponse(new Response(215, 'List of newsgroups follows')); $groups = (yield from $context->getAccessLayer()->getGroups()); $data = array_reduce($groups, function (string $s, Group $group) { switch ($group->status()) { case Group::POSTING_PERMITTED: $status = 'y'; break; case Group::POSTING_NOT_PERMITTED: $status = 'n'; break; case Group::POSTING_FORWARDED: $status = 'm'; break; default: $status = 'u'; break; } return $s . sprintf("%s %d %d %s\r\n", $group->name(), $group->highWaterMark(), $group->lowWaterMark(), $status); }, ''); yield from $context->writeData($data); }
public function handle(Command $command, ClientContext $context) : Generator { if (!$context->getAccessLayer()->isPostingAllowed()) { yield from $context->writeResponse(new Response(440, 'Posting not permitted')); return; } yield from $context->writeResponse(new Response(340, 'Send article to be posted')); $data = (yield from $context->readData()); $article = Article::parse($data); try { yield from $context->getAccessLayer()->postArticle($article); yield from $context->writeResponse(new Response(240, 'Article received OK')); } catch (\Throwable $e) { yield from $context->writeResponse(new Response(441, 'Posting failed')); } }
public function handle(Command $command, ClientContext $context) : Generator { // Make sure we have a valid cursor. if (!$context->getCursor()->valid()) { yield from $context->writeResponse(new Response(412, 'No newsgroup selected')); return; } // Try to move to the next article. if (!(yield from $context->getCursor()->next())) { yield from $context->writeResponse(new Response(421, 'No next article in this group')); return; } // Spit out the current article info. $article = $context->getCursor()->getArticle(); yield from $context->writeResponse(new Response(223, '%d %s Article found', $article->number(), $article->id())); }
public function handle(Command $command, ClientContext $context) : Generator { if ($command->argCount() !== 1) { yield from $context->writeResponse(new Response(501, 'Invalid number of arguments')); return; } try { $name = $command->arg(0); $cursor = (yield from $context->getAccessLayer()->getGroupCursor($name)); $context->setCursor($cursor); } catch (NotFoundException $e) { yield from $context->writeResponse(new Response(411, 'No such newsgroup')); return; } $group = $context->getCursor()->getGroup(); yield from $context->writeResponse(new Response(211, $group->count() . ' ' . $group->lowWaterMark() . ' ' . $group->highWaterMark() . ' ' . $group->name() . ' Group successfully selected')); }
public function handle(Command $command, ClientContext $context) : Generator { if ($command->argCount() === 0) { $group = $this->currentGroup; } $name = $command->arg(0); $group = (yield from $context->getAccessLayer()->getGroupByName($name)); if (!$group) { yield from $context->writeResponse(new Response(411, 'No such newsgroup')); return; } $context->setCurrentGroup($group->name()); $context->setCurrentArticle($group->lowWaterMark()); yield from $context->writeResponse(new Response(211, $group->count() . ' ' . $group->lowWaterMark() . ' ' . $group->highWaterMark() . ' ' . $group->name() . ' Group successfully selected')); }
public function handle(Command $command, ClientContext $context) : Generator { yield from $context->writeResponse(new Response(200, 'Reader mode already active')); }
public function handle(Command $command, ClientContext $context) : Generator { yield from $context->writeResponse(new Response(100, 'Help text follows (multi-line)')); $data = implode("\r\n", ['article [message-id|number]', 'body [message-id|number]', 'capabilities', 'date', 'group group', 'head [message-id|number]', 'help', 'last', 'list active', 'listgroup', 'newgroups', 'newnews', 'next', 'post', 'quit', 'stat']); yield from $context->writeData($data); }
protected function fetchArticle(Command $command, ClientContext $context) : Generator { // Current article? if ($command->argCount() === 0) { if (!$context->getCursor()->valid()) { yield from $context->writeResponse(new Response(412, 'No newsgroup selected')); return; } return $context->getCursor()->getArticle(); } elseif (is_numeric($command->arg(0))) { $number = (int) $command->arg(0); if (!$context->getCursor()->valid()) { yield from $context->writeResponse(new Response(412, 'No newsgroup selected')); return; } if (!(yield from $context->getCursor()->seek($number))) { yield from $context->writeResponse(new Response(423, 'No article with that number')); return; } return $context->getCursor()->getArticle(); } else { $article = (yield from $context->getAccessLayer()->getArticleById($command->arg(0))); if (!$article) { yield from $context->writeResponse(new Response(430, 'No article with that message-id')); return; } } return $article; }
/** * Services requests for a connected client. * * Manages the server-side state for the remote client and interprets client commands. */ private function handleClient(Socket $socket) : Generator { yield from log()->log(Log::INFO, 'Accepted client from %s:%d on %s:%d', $socket->getRemoteAddress(), $socket->getRemotePort(), $socket->getLocalAddress(), $socket->getLocalPort()); // Create a new context object for this client. $context = new ClientContext($socket, $this->encoder, $this->accessLayer); try { // Send a welcome message. $welcome = new Response(200, 'Ready'); yield from $context->writeResponse($welcome); // Command loop while ($socket->isOpen()) { // Parse incoming commands. $command = (yield from $context->readCommand()); // Determine the command name and choose how to handle it. $handler = null; switch ($command->name()) { // Mandatory commands case 'CAPABILITIES': $handler = $this->getHandler(handlers\CapabilitiesHandler::class); break; case 'HEAD': $handler = $this->getHandler(handlers\HeadHandler::class); break; case 'HELP': $handler = $this->getHandler(handlers\HelpHandler::class); break; // We don't advertise MODE support, but some readers need it // We don't advertise MODE support, but some readers need it case 'MODE': $handler = $this->getHandler(handlers\ModeHandler::class); break; case 'STAT': $handler = $this->getHandler(handlers\StatHandler::class); break; case 'QUIT': yield from $context->writeResponse(new Response(205, 'Closing connection')); break 2; // stop command loop // LIST commands // stop command loop // LIST commands case 'LIST': $handler = $this->getHandler(handlers\ListHandler::class); break; // NEWNEWS commands // NEWNEWS commands case 'NEWNEWS': $handler = $this->getHandler(handlers\NewNewsHandler::class); break; // POST commands // POST commands case 'POST': $handler = $this->getHandler(handlers\PostHandler::class); break; // READER commands // READER commands case 'ARTICLE': $handler = $this->getHandler(handlers\ArticleHandler::class); break; case 'BODY': $handler = $this->getHandler(handlers\BodyHandler::class); break; case 'DATE': $handler = $this->getHandler(handlers\DateHandler::class); break; case 'GROUP': $handler = $this->getHandler(handlers\GroupHandler::class); break; case 'LAST': $handler = $this->getHandler(handlers\LastHandler::class); break; case 'LISTGROUP': $handler = $this->getHandler(handlers\ListGroupHandler::class); break; case 'NEWGROUPS': $handler = $this->getHandler(handlers\NewGroupsHandler::class); break; case 'NEXT': $handler = $this->getHandler(handlers\NextHandler::class); break; } // Unknown command if (!$handler) { yield from $context->writeResponse(new Response(500, 'Unknown command')); } else { // Execute the selected command handler. try { yield from $handler->handle($command, $context); } catch (\Throwable $e) { yield from log()->log(Log::ERROR, 'Error handling command: ' . $e); yield from $context->writeResponse(new Response(502, 'Server error')); } } } } catch (UnreadableException $e) { // Client disconnected. } catch (UnwritableException $e) { // Client disconnected. } catch (ClosedException $e) { // Client disconnected. } yield from log()->log(Log::INFO, 'Disconnected client from %s:%d on %s:%d', $socket->getRemoteAddress(), $socket->getRemotePort(), $socket->getLocalAddress(), $socket->getLocalPort()); // Close the connection to the client. if ($socket->isOpen()) { $socket->close(); } }
public function handle(Command $command, ClientContext $context) : Generator { yield from $context->writeResponse(new Response(111, date('YmdHis'))); }
public function handle(Command $command, ClientContext $context) : Generator { yield from $context->writeResponse(new Response(382, 'Continue with TLS negotiation')); yield from $context->getSocket()->enableCrypto(self::CRYPTO_METHOD); }