~jakob/brisket

4652c09617588b72ccda83de2ebd07bf81242978 — Jakob L. Kreuze 3 years ago dd11ee4 master v0.1.0
Generate headers for 'post-article
1 files changed, 57 insertions(+), 12 deletions(-)

M brisket.hy
M brisket.hy => brisket.hy +57 -12
@@ 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 "<brisket-{}@{}>" 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.