Gangly bones of the new sever logic
This commit is contained in:
parent
0e5bcd5f77
commit
282f43679c
4 changed files with 183 additions and 0 deletions
15
lib/nntp/tiny/request.py
Normal file
15
lib/nntp/tiny/request.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class NNTPRequest():
|
||||||
|
__slots__ = 'buf', 'offset', 'sock',
|
||||||
|
|
||||||
|
BUFFER_SIZE = 4096
|
||||||
|
|
||||||
|
def __init__(self, sock: socket.socket):
|
||||||
|
|
||||||
|
self.buf: bytearray = bytearray(self.BUFFER_SIZE)
|
||||||
|
self.offset: int = 0
|
||||||
|
self.sock: socket.socket = sock
|
||||||
|
|
85
lib/nntp/tiny/response.py
Normal file
85
lib/nntp/tiny/response.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import enum
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class ResponseCode(enum.Enum):
|
||||||
|
NNTP_HELP_FOLLOWS = 100
|
||||||
|
NNTP_CAPABILITIES_FOLLOW = 101
|
||||||
|
NNTP_DATE = 111
|
||||||
|
NNTP_SERVICE_READY_POST_ALLOWED = 200
|
||||||
|
NNTP_SERVICE_READY_POST_PROHIBITED = 201
|
||||||
|
NNTP_CONNECTION_CLOSING = 205
|
||||||
|
NNTP_GROUP_LISTING = 211
|
||||||
|
NNTP_INFORMATION_FOLLOWS = 215
|
||||||
|
NNTP_ARTICLE_BODY = 220
|
||||||
|
NNTP_ARTICLE_LISTING = 221
|
||||||
|
NNTP_BODY_LISTING = 222
|
||||||
|
NNTP_ARTICLE_STAT_RESPONSE = 223
|
||||||
|
NNTP_OVERVIEW_FOLLOWS = 224
|
||||||
|
NNTP_HEADERS_FOLLOW = 225
|
||||||
|
NNTP_ARTICLE_LISTING_ID_FOLLOWS = 230
|
||||||
|
NNTP_GROUPS_NEW_FOLLOW = 231
|
||||||
|
NNTP_ARTICLE_RECEIVED = 240
|
||||||
|
NNTP_AUTH_ACCEPTED = 281
|
||||||
|
NNTP_INQUIRY_ARTICLE = 340
|
||||||
|
NNTP_INQUIRY_PASSPHRASE = 381
|
||||||
|
NNTP_NEWSGROUP_NOT_FOUND = 411
|
||||||
|
NNTP_NEWSGROUP_NOT_SELECTED = 412
|
||||||
|
NNTP_ARTICLE_INVALID_NUMBER = 420
|
||||||
|
NNTP_ARTICLE_NOT_FOUND_NUM = 423
|
||||||
|
NNTP_ARTICLE_NOT_FOUND_ID = 430
|
||||||
|
NNTP_POST_PROHIBITED = 440
|
||||||
|
NNTP_POST_FAILED = 441
|
||||||
|
NNTP_AUTH_FAILED = 481
|
||||||
|
NNTP_AUTH_BAD_SEQUENCE = 482
|
||||||
|
NNTP_COMMAND_UNKNOWN = 500
|
||||||
|
NNTP_SYNTAX_ERROR = 501
|
||||||
|
NNTP_COMMAND_UNAVAILABLE = 502
|
||||||
|
NNTP_GROUPS_UNAVAILABLE = 503
|
||||||
|
|
||||||
|
def message(self):
|
||||||
|
return {
|
||||||
|
100: "Help text follows",
|
||||||
|
101: "Capabilities follow",
|
||||||
|
200: "NNTP Service Ready, posting allowed",
|
||||||
|
201: "NNTP Service Ready, posting prohibited",
|
||||||
|
205: "Connection closing",
|
||||||
|
215: "Information follows",
|
||||||
|
224: "Overview information follows (multi-line)",
|
||||||
|
225: "Headers follow (multi-line)",
|
||||||
|
231: "List of new newsgroups follows",
|
||||||
|
240: "Article received OK",
|
||||||
|
281: "Authentication accepted",
|
||||||
|
340: "Input article; end with <CR><LF>.<CR><LF>",
|
||||||
|
381: "Enter passphrase",
|
||||||
|
411: "No such newsgroup",
|
||||||
|
412: "No newsgroup selected",
|
||||||
|
420: "Current article number is invalid",
|
||||||
|
423: "No article found by that number",
|
||||||
|
430: "No article found by that message ID",
|
||||||
|
440: "Posting prohibited",
|
||||||
|
441: "Posting failed",
|
||||||
|
481: "Authentication failed",
|
||||||
|
482: "Authentication commands issued out of sequence",
|
||||||
|
500: "Unknown command",
|
||||||
|
501: "Syntax error",
|
||||||
|
502: "Command unavailable",
|
||||||
|
503: "No list of recommended newsgroups available"
|
||||||
|
}.get(self.value)
|
||||||
|
|
||||||
|
class Response():
|
||||||
|
__slots__ = 'code', 'message', 'body',
|
||||||
|
|
||||||
|
def __init__(self, code: ResponseCode, message: Optional[str]=None, body: Optional[str]=None):
|
||||||
|
self.code = code
|
||||||
|
self.message = message or code.message() or "Unknown response"
|
||||||
|
self.body = body
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
ret = "%d %s" % (self.code.value, self.message)
|
||||||
|
|
||||||
|
if self.body:
|
||||||
|
ret += "\r\n" + self.body
|
||||||
|
|
||||||
|
return ret
|
10
lib/nntp/tiny/server.py
Normal file
10
lib/nntp/tiny/server.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import enum
|
||||||
|
|
||||||
|
class ServerCapability(enum.Flag):
|
||||||
|
NONE = 0
|
||||||
|
AUTH = enum.auto()
|
||||||
|
POST = enum.auto()
|
||||||
|
|
||||||
|
class Server():
|
||||||
|
def __init_(self):
|
||||||
|
self.capabilities = NNTPServerCapability.NONE
|
73
lib/nntp/tiny/session.py
Normal file
73
lib/nntp/tiny/session.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import enum
|
||||||
|
import socket
|
||||||
|
import re
|
||||||
|
|
||||||
|
from nntp.tiny.server import Server, ServerCapability
|
||||||
|
from nntp.tiny.response import Response
|
||||||
|
|
||||||
|
class SessionState(enum.Flag):
|
||||||
|
NONE = 0
|
||||||
|
AUTH_OK = enum.auto()
|
||||||
|
AUTH_POST = enum.auto()
|
||||||
|
|
||||||
|
from nntp.tiny.buffer import LineBuffer, BufferOverflow
|
||||||
|
|
||||||
|
class Session():
|
||||||
|
RE_SPLIT = re.compile(r'\s+')
|
||||||
|
|
||||||
|
NNTP_VERSION = 2
|
||||||
|
|
||||||
|
NNTP_CAPABILITIES = [
|
||||||
|
'VERSION %d' % (self.NNTP_VERSION),
|
||||||
|
'READER',
|
||||||
|
'HDR',
|
||||||
|
'NEWNEWS',
|
||||||
|
'LIST ACTIVE NEWSGROUP OVERVIEW.FMT SUBSCRIPTIONS',
|
||||||
|
'OVER MSGID'
|
||||||
|
]
|
||||||
|
|
||||||
|
COMMANDS = {
|
||||||
|
'capabilities':
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, server: Server, sock: socket.socket):
|
||||||
|
self.server: Server = server
|
||||||
|
self.state: SessionState = SessionState.NONE
|
||||||
|
self.sock: socket.socket = sock
|
||||||
|
self.buf: LineBuffer = LineBuffer()
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
return self.buf.readline(self.sock)
|
||||||
|
|
||||||
|
def print(self, text: str, end: str="\r\n"):
|
||||||
|
return self.sock.send(bytes(text + end, 'ascii'))
|
||||||
|
|
||||||
|
def end(self):
|
||||||
|
return self.print('.')
|
||||||
|
|
||||||
|
def respond(self, code: ResponseCode, message: str=None, body=None):
|
||||||
|
response = Response(code, message, body)
|
||||||
|
|
||||||
|
return self.print(str(response))
|
||||||
|
|
||||||
|
def _cmd_capabilities(self, *args):
|
||||||
|
self.respond(ResponseCode.NNTP_CAPABILITIES_FOLLOW)
|
||||||
|
|
||||||
|
if self.state & SessionState.AUTH_POST:
|
||||||
|
self.print('POST')
|
||||||
|
|
||||||
|
if self.state & SessionState.AUTH_OK:
|
||||||
|
self.print('AUTHUSER INFO')
|
||||||
|
|
||||||
|
for item in self.NNTP_CAPABILITIES:
|
||||||
|
self.print(item)
|
||||||
|
|
||||||
|
self.end()
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
line = self.readline()
|
||||||
|
|
||||||
|
if line == '':
|
||||||
|
return
|
||||||
|
|
||||||
|
command, args = self.RE_SPLIT.split(line.rstrip())
|
Loading…
Add table
Reference in a new issue