#!/usr/bin/env tclsh ## -*- tcl -*- package require Tcl 8.5 package require nntp package require fileutil # This application, derived from its sibling 'postnews', takes a # message file and directly posts it to a given server, and group. All # other information, like destination group, subject, sender, etc. are # expected to be in the message itself. This means that the message # file is expected to have the proper format for a mail/news posting. # # Using "-" for the message file causes the command to read the # message from stdin. proc main {} { if {![cmdline]} usage checkmessage postmessage } proc cmdline {} { global argv newsserver message user password if {[lindex $argv 0] eq "-via"} { if {[llength $argv] != 4} {return 0} set argv [lassign $argv _ accountfile] lassign [split [validatefile {account file} $accountfile] \n] user password } if {[llength $argv] != 2} {return 0} # Retrieve arguments lassign $argv newsserver messagefile # Validate messagefile if {$messagefile eq "-"} { set message [read stdin] } else { set message [validatefile {message file} $messagefile] } return 1 } proc validatefile {which path} { if {![file exists $path]} { stop "$which does not exist: $path" } if {![file isfile $path]} { stop "$which not a file: $path" } if {![file readable $path]} { stop "$which not readable: $path" } return [fileutil::cat $path] } proc usage {} { global argv0 puts stderr "$argv0: wrong # args, should be \"$argv0 ?-via accountfile? server messagefile\"" exit 1 } proc stop {text} { global argv0 puts stderr "$argv0: $text" exit 1 } proc checkmessage {} { processmessage need Newsgroups need Subject need From add "X-Posting-Engine" "Tcllib nntp/postmsg on Tcl [info patchlevel]" # Some news-servers handle the adding of the Lines: header itself #add Lines [llength $body] add "Content-Type" "text/plain; charset=iso-8859-1" regenerate return } proc processmessage {} { global message head body array set head {} set body {} set inBody 0 set lastheader {} foreach line [split $message "\n"] { if {$inBody} { lappend body $line } elseif {[string length $line] == 0} { set inBody 1 } elseif {[regexp {^([^ :]+): +(.*)} $line => header value]} { set header [string tolower $header] set value [string trim $value] if {[string length $value]} { set head($header) "$value " } set lastheader $header } else { append head($lastheader) "[string trim $line] " } } return } proc need {header} { global head if {[info exist head([string tolower $header])]} return stop "Required header \"${header}:\" is missing" } # Add the given header to the message to be posted, if not already present. proc add {header value} { global head set header [string tolower $header] if {[info exist head($header)]} return set head($header) $value return } proc regenerate {} { global message head body foreach {header value} [array get head] { lappend lines "[capitalise $header]: [string trim $value]" } lappend lines {} lappend lines $body set message [join $lines \n] return } proc capitalise {string} { set result {} foreach word [split $string "-"] {lappend result [capitalise1 $word]} join $result "-" } proc capitalise1 {word} { set c0 [string index $word 0] set cr [string range $word 1 end] return [string toupper $c0][string tolower $cr] } proc postmessage {} { global newsserver message user password nntp_cmd 1 {open } {set news [nntp::nntp $newsserver]} nntp_cmd 1 {mode reader} {$news mode_reader} if {[info exists user]} { nntp_cmd 1 {authinfo } {$news authinfo $user $password} } puts stdout "post [llength [split $message \n]] lines" nntp_cmd 0 {post } {$news post $message} nntp_cmd 1 {quit } {$news quit} return } proc nntp_cmd {exit title cmd {oktitle {}}} { global argv0 puts -nonewline stdout $title flush stdout if {[catch { set res [uplevel 1 $cmd] } msg]} { puts stdout " error: $msg" #puts stderr "$argv0: nntp error: $msg" if {$exit} { exit 1 } return 0 } else { if {$oktitle != {}} { puts stdout " $res $oktitle" } else { puts stdout " $res" } return 1 } } main exit