UNPKG

newsie

Version:

An NNTP Client Library targeting NodeJS. It supports the authentication, TLS encryption, base NNTP commands, and more.

343 lines (335 loc) 10.3 kB
import { TlsOptions, TLSSocket } from 'tls'; import { Socket } from 'net'; /** * TODO: reject on promises on timeout * TODO: support compression (doesn't yet work) */ declare class Connection { private _compress; private _socket; private _tlsPort; private _tlsOptions; private _tlsPromiseReject; private _host; private _port; private _queue; private _frames; constructor(host: string, port: number, tlsPort: boolean, tlsOptions: TlsOptions); connect: () => Promise<Socket | TLSSocket>; disconnect: () => void; write: (str: string) => Promise<void>; addCallback: (callback: Function, resolve: Function, reject: Function) => void; upgradeTls: () => Promise<Socket | TLSSocket>; enableCompression: () => void; private _handler; private _addSocketHandlers; } interface Article { articleNumber?: number; messageId?: string; headers?: { [header: string]: string; }; metadata?: { [metadata: string]: string | number; }; body?: string[]; /** * Part of hdr response */ fieldContents?: string; } interface Group { name: string; low: number; high: number; /** * Part of group() response */ number?: number; /** * Part of listGroup() response */ articleNumbers?: number[]; /** * Part of newgroups() response */ status?: string; /** * Part of listActiveTimes() response */ created?: string; /** * Part of listActiveTimes() response */ creator?: string; /** * Part of listNewsgroups() response */ description?: string; } interface Range { start: number; end?: number; } declare enum Command { ARTICLE = "ARTICLE", BODY = "BODY", CAPABILITIES = "CAPABILITIES", DATE = "DATE", GROUP = "GROUP", HDR = "HDR", HEAD = "HEAD", HELP = "HELP", LAST = "LAST", LIST = "LIST", LIST_ACTIVE_TIMES = "LIST ACTIVE.TIMES", LIST_ACTIVE = "LIST ACTIVE", LIST_DISTRIB_PATS = "LIST DISTRIB.PATS", LIST_HEADERS = "LIST HEADERS", LIST_NEWSGROUPS = "LIST NEWSGROUPS", LIST_OVERVIEW_FMT = "LIST OVERVIEW.FMT", LISTGROUP = "LISTGROUP", MODE_READER = "MODE READER", NEWGROUPS = "NEWGROUPS", NEWNEWS = "NEWNEWS", NEXT = "NEXT", OVER = "OVER", POST = "POST", POST_SEND = "POST_SEND", IHAVE = "IHAVE", IHAVE_SEND = "IHAVE_SEND", QUIT = "QUIT", STAT = "STAT", GREETING = "GREETING", AUTHINFO_USER = "AUTHINFO USER", AUTHINFO_PASS = "AUTHINFO PASS", STARTTLS = "STARTTLS", AUTHINFO_SASL = "AUTHINFO SASL", COMPRESS = "COMPRESS", MODE_STREAM = "MODE STREAM", CHECK = "CHECK", TAKETHIS = "TAKETHIS", SLAVE = "SLAVE" } declare enum CapabilityLabel { STARTTLS = "STARTTLS", AUTHINFO = "AUTHINFO", READER = "READER", NEWNEWS = "NEWNEWS", COMPRESS = "COMPRESS", POST = "POST", IHAVE = "IHAVE", OVER = "OVER", VERSION = "VERSION", LIST = "LIST", IMPLEMENTATION = "IMPLEMENTATION", STREAMING = "STREAMING", HDR = "HDR", MODE_READER = "MODE-READER" } interface NntpResponse { code: number; comment: string; description: string; } interface NntpErrorResponse extends NntpResponse { /** * When the code is 401, the capabilityLabel may be defined to inform the * client to switch modes. */ capabilityLabel?: CapabilityLabel; } interface GreetingResponse extends NntpResponse { /** * 200 => posting allowed * 201 => posting prohibited */ code: 200 | 201; socket: Socket; } interface CapabilitiesResponse extends NntpResponse { code: 101; capabilities: { [Label in CapabilityLabel]: string[]; }; } interface GroupResponse extends NntpResponse { code: 211; group: Group; } interface ArticleResponse extends NntpResponse { /** * 220 => article * 221 => head * 222 => body * 223 => stat */ code: 220 | 221 | 222 | 223; article: Article; } interface PostResponse extends NntpResponse { /** * 335 => ihave * 340 => post */ code: 335 | 340; send: (article: Article) => NntpResponse; } interface DateResponse extends NntpResponse { code: 111; isoDateTime: string; } interface HelpResponse extends NntpResponse { code: 100; text: string[]; } interface GroupsResponse extends NntpResponse { /** * 215 => list * 231 => newgroups */ code: 215 | 231; newsgroups: Group[]; } interface NewnewsResponse extends NntpResponse { code: 230; messageIds: string[]; } interface DistributionPatternsResponse extends NntpResponse { code: 215; distributionPatterns: { weight: number; wildmat: string; distributionHeader: string; }[]; } interface ArticlesResponse extends NntpResponse { /** * 224 => over * 225 => hdr */ code: 224 | 225; articles: Article[]; } interface OverviewFormatResponse extends NntpResponse { code: 215; headerFields: string[]; metadataFields: string[]; } interface ListHeadersResponse extends NntpResponse { code: 215; /** * May include both headers and metadata */ fields: string[]; } interface StartTlsResponse extends NntpResponse { code: 382; socket: TLSSocket; } interface AuthInfoResponse extends NntpResponse { /** * 281 => AUTHINFO USER or AUTHINFO PASS * 381 => AUTHINFO USER */ code: 281 | 381; /** * Defined if code === 381 */ authInfoPass?: (password: string) => Promise<NntpResponse>; } interface AuthInfoSaslResponse extends NntpResponse { code: 281 | 283 | 383; continue: (clientResponse: string) => Promise<AuthInfoSaslResponse>; cancel: () => Promise<NntpErrorResponse>; challenge: string; } interface Options { host: string; port?: number; tlsPort?: boolean; responseInterceptor?: (response: any) => any; tlsOptions?: TlsOptions; } declare class Client { _connection: Connection; private _interceptor; constructor(options: Options); connect: () => Promise<any>; disconnect: () => void; command: (command: Command, ...args: (string | void)[]) => Promise<any>; /** * TODO: should reject an Error object */ sendData: (command: Command, payload?: string) => Promise<any>; } interface Client { slave(): Promise<NntpResponse>; capabilities(keyword?: string): Promise<CapabilitiesResponse>; modeReader(): Promise<NntpResponse>; quit(): Promise<NntpResponse>; group(group?: string): Promise<GroupResponse>; listGroup(group?: string, range?: Range): Promise<GroupResponse>; last(): Promise<ArticleResponse>; next(): Promise<ArticleResponse>; article(articleNumberOrMessageId?: number | string): Promise<ArticleResponse>; head(articleNumberOrMessageId?: number | string): Promise<ArticleResponse>; body(articleNumberOrMessageId?: number | string): Promise<ArticleResponse>; stat(articleNumberOrMessageId?: number | string): Promise<ArticleResponse>; post(): Promise<PostResponse>; ihave(messageId: string): Promise<PostResponse>; date(): Promise<DateResponse>; help(this: Client): Promise<HelpResponse>; newgroups(this: Client, isoDateTime: string): Promise<GroupsResponse>; newnews(this: Client, wildmat: string, isoDateTime: string): Promise<NewnewsResponse>; list(this: Client): Promise<GroupsResponse>; listActive(this: Client, wildmat?: string): Promise<GroupsResponse>; listActiveTimes(this: Client, wildmat?: string): Promise<GroupsResponse>; listDistribPats(this: Client, wildmat?: string): Promise<DistributionPatternsResponse>; listNewsgroups(this: Client, wildmat?: string): Promise<GroupsResponse>; over(messageIdOrRange?: string | Range | number): Promise<GroupsResponse>; listOverviewFmt(wildmat?: string): Promise<OverviewFormatResponse>; hdr(field: string, messageIdOrRange?: string | Range): Promise<ArticleResponse>; listHeaders(argument?: 'MSGID' | 'RANGE'): Promise<ListHeadersResponse>; startTls(): Promise<StartTlsResponse>; authInfoUser(username: string): Promise<AuthInfoResponse>; authInfoSasl(mechanism: string, initialResponse?: string): Promise<AuthInfoSaslResponse>; authInfoSaslPlain(authzid: string | void, authcid: string, passwd: string): Promise<AuthInfoSaslResponse>; compressDeflate(): Promise<any>; modeStream(): Promise<NntpResponse>; check(messageId: string): Promise<ArticleResponse>; takeThis(article: Article): Promise<ArticleResponse>; } interface Block { lines: string[]; _i: number; } interface Handler { multiLine: boolean; numberOfArgs: number; callback: (args: string[], block?: Block) => any; } declare const build: (description: string) => Handler; declare const addHandler: (command: Command, code: number, handler: Handler) => void; /** * Given a URI string using nntp, nntps, news, or snews protocols, attempts * to list a group or fetch an article. Will attempt to use STARTTLS if * protocol is nntps or snews. Will attempt to authenticate with AUTHINFO USER * if an authority is provided. * * A server (host) must be specified in the URI; there is no default host. * * A news or snews URI requesting one or more newsgroups is not supported. * * @example * const article = await fetch('news://news.gmane.org/p0624081dc30b8699bf9b@%5B10.20.30.108%5D') */ declare function fetch(uri: string): Promise<any>; interface Response { _i: number; code: number; comment: string; } declare const parse: (command: Command, buffer: string) => Response | void; export { type Article, type ArticleResponse, type ArticlesResponse, type AuthInfoResponse, type AuthInfoSaslResponse, type Block, type CapabilitiesResponse, CapabilityLabel, Command, type DateResponse, type DistributionPatternsResponse, type GreetingResponse, type Group, type GroupResponse, type GroupsResponse, type Handler, type HelpResponse, type ListHeadersResponse, type NewnewsResponse, type NntpErrorResponse, type NntpResponse, type Options, type OverviewFormatResponse, type PostResponse, type Range, type Response, type StartTlsResponse, addHandler, build, Client as default, fetch, parse };