#!/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