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