From 4652c09617588b72ccda83de2ebd07bf81242978 Mon Sep 17 00:00:00 2001 From: "Jakob L. Kreuze" Date: Sun, 28 Apr 2019 11:19:23 -0400 Subject: Generate headers for 'post-article --- brisket.hy | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/brisket.hy b/brisket.hy index f42db86..1b88e76 100644 --- a/brisket.hy +++ b/brisket.hy @@ -22,12 +22,14 @@ (setv __license__ "GPL") (setv __version__ "0.1.0 'Kansas City-style'") +(import [binascii [hexlify]]) (import [configparser [ConfigParser]]) +(import [nntplib [NNTP-SSL]]) (import [os [getcwd getenv makedirs]]) (import [os.path [dirname expanduser join]]) -(import [sys [argv exit stderr stdin]]) -(import [nntplib [NNTP-SSL]]) (import [subprocess [Popen PIPE]]) +(import [sys [argv exit stderr stdin]]) +(import [time [strftime]]) (require [hy.contrib.walk [let]]) @@ -165,22 +167,65 @@ usage: fetch-posts (group [optional]) (article-id [optional])" [group (fetch-group-articles session dest write-to-stdout group)] [True (fetch-all-articles session dest write-to-stdout)]))) +;; RFC 5536, the Netnews Article Format specifies the following headers as +;; mandatory an article. +;; +;; - Date +;; - From +;; - Message-ID +;; - Newsgroups +;; - Path +;; - Subject +;; +;; The following headers, though not mandatory, are handled as well by brisket. +;; +;; - User-Agent +;; - Mime-Version +;; - Content-Type +;; - Content-Transfer-Encoding +;; - Content-Language + +(defn generate-message-id [posting-address] + (let [random (with [rand (open "/dev/urandom" "rb")] + (hexlify (.read rand 16))) + host (second (.split posting-address "@"))] + (.format "" random host))) + +(defn generate-headers [posting-address subject group] + "Return a list of headers to prepend to an article." + [(.format "Date: {}" (strftime "%a, %d %b %Y %H:%M:%S %z")) + (.format "From: {}" posting-address) + (.format "Message-ID: {}" (generate-message-id posting-address)) + (.format "Newsgroups: {}" group) + (.format "Path: {}" "not-for-mail") + (.format "Subject: {}" subject) + (.format "User-Agent: {} {}" "brisket" __version__) + (.format "Mime-Version: {}" "1.0") + (.format "Content-Type: {}" "text/plain; charset=utf-8") + (.format "Content-Transfer-Encoding: {}" "7bit") + (.format "Content-Language: {}" "en-US")]) + (defn post-article [session &rest args] "Post an article to the server. Reads from stdin if the path of a file is not specified. -usage: post-article [group] (path [optional])" - (when (= (len args) 0) - (err "usage: post-article [group] (path [optional])") +usage: post-article [posting-address] [subject] [group] (path [optional])" + (when (< (len args) 3) + (err "usage: post-article [posting-address] [subject] [group] (path [optional])") (return)) - (let [group (first args) - path (if (> (len args) 1) (second args))] - (.group session group) - (if path - (with [f (open path "rb")] - (.post session)) - (.post session (list (map str.encode (.split (.read stdin) "\n"))))))) + (let [posting-address (nth args 0) + subject (nth args 1) + group (nth args 2) + headers (generate-headers posting-address subject group) + path (if (> (len args) 3) (nth args 3)) + content (if path + (with [f (open path)] + (+ (list (map str.encode headers)) + (list (map str.encode (.split (.read f) "\n"))))) + (+ (list (map str.encode headers)) + (list (map str.encode (.split (.read stdin) "\n")))))] + (.post session content))) (defn make-config [] "Create a default configuration file. -- cgit v1.3