summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/nntp
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 19:39:39 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 19:39:39 (GMT)
commitea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch)
tree6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/modules/nntp
parentb5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff)
parent7c621da28f07e449ad90c387344f07a453927569 (diff)
downloadblt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip
blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz
blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/modules/nntp')
-rw-r--r--tcllib/modules/nntp/ChangeLog154
-rw-r--r--tcllib/modules/nntp/nntp.man338
-rw-r--r--tcllib/modules/nntp/nntp.tcl979
-rw-r--r--tcllib/modules/nntp/pkgIndex.tcl12
4 files changed, 1483 insertions, 0 deletions
diff --git a/tcllib/modules/nntp/ChangeLog b/tcllib/modules/nntp/ChangeLog
new file mode 100644
index 0000000..29fcc8b
--- /dev/null
+++ b/tcllib/modules/nntp/ChangeLog
@@ -0,0 +1,154 @@
+2013-03-11 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * rfc977.txt: Removed copies of RFC documents. Keep only links.
+
+2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.15 ========================
+ *
+
+2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.14 ========================
+ *
+
+2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.13 ========================
+ *
+
+2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.12 ========================
+ *
+
+2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.11.1 ========================
+ *
+
+2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.11 ========================
+ *
+
+2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.10 ========================
+ *
+
+2007-03-21 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * nntp.man: Fixed all warnings due to use of now deprecated
+ commands. Added a section about how to give feedback.
+
+2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.9 ========================
+ *
+
+2005-10-06 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.8 ========================
+ *
+
+2004-10-05 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.7 ========================
+ *
+
+2004-05-23 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.6.1 ========================
+ *
+
+2004-02-15 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.6 ========================
+ *
+
+2003-05-05 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.4 ========================
+ *
+
+2003-04-11 Andreas Kupries <andreask@activestate.com>
+
+ * nntp.man:
+ * nntp.tcl:
+ * pkgIndex.tcl: Set version of the package to to 0.2.1
+
+2003-02-24 David N. Welton <davidw@dedasys.com>
+
+ * nntp.tcl (::nntp::squirt): Use if, string match instead of
+ regsub.
+
+2003-02-06 David N. Welton <davidw@dedasys.com>
+
+ * nntp.tcl (::nntp::fetch): Use 'string match' instead of regexp.
+ Use if string match ... string range instead of regsub (it's
+ about twice as fast in a small test I ran).
+
+2003-01-16 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * nntp.man: More semantic markup, less visual one.
+
+2002-08-19 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * nntp.man: Added example, updated reference from rfc 850 to rfc
+ 1036. See Tcllib SF #597102, by Jussi Kuosa
+ <Jussi.Kuosa@tellabs.com>.
+ * nntp.n: Out of date. Deprecated.
+
+2002-03-25 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * nntp.man: New file, doctools manpage.
+
+2002-01-16 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * Bumped version to 0.2
+
+2002-01-16 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * nntp.tcl: Fixed bug #502250 reported by Andreas Otto
+ <aotto@t-online.de> which caused the package to wrap each
+ message into braces, causing nntp servers to reject the data.
+
+2001-07-10 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * nntp.tcl: Frink 2.2 run, fixed dubious code.
+
+2001-06-21 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * nntp.tcl: Fixed dubious code reported by frink.
+
+2000-06-20 Dan Kuchler <kuchler@ajubasolutions.com>
+
+ * Code cleanup and bug fixes
+
+2000-06-18 Dan Kuchler <kuchler@ajubasolutions.com>
+
+ * Fixed documentation bug in man page for xpat
+
+2000-06-16 Dan Kuchler <kuchler@ajubasolutions.com>
+
+ * rfc977.txt: RFC for NNTP
+
+ * pkgIndex.tcl
+ * nntp.tcl: Initial implementation of a nntp client package.
+
+ * nntp.n: Initial documentation for the package.
+
diff --git a/tcllib/modules/nntp/nntp.man b/tcllib/modules/nntp/nntp.man
new file mode 100644
index 0000000..8b06ec4
--- /dev/null
+++ b/tcllib/modules/nntp/nntp.man
@@ -0,0 +1,338 @@
+[comment {-*- tcl -*- doctools manpage}]
+[manpage_begin nntp n 1.5.1]
+[keywords news]
+[keywords nntp]
+[keywords nntpclient]
+[keywords {rfc 977}]
+[keywords {rfc 1036}]
+[moddesc {Tcl NNTP Client Library}]
+[titledesc {Tcl client for the NNTP protocol}]
+[category Networking]
+[require Tcl 8.2]
+[require nntp [opt 0.2.1]]
+[description]
+
+The package [package nntp] provides a simple Tcl-only client library
+for the NNTP protocol. It works by opening the standard NNTP socket
+on the server, and then providing a Tcl API to access the NNTP
+protocol commands. All server errors are returned as Tcl errors
+(thrown) which must be caught with the Tcl [cmd catch] command.
+
+[section COMMANDS]
+
+[list_begin definitions]
+
+[call [cmd ::nntp::nntp] [opt [arg host]] [opt [arg port]] [opt [arg nntpName]]]
+
+The command opens a socket connection to the specified NNTP server and
+creates a new nntp object with an associated global Tcl command whose
+name is [arg nntpName]. This command may be used to access the various
+NNTP protocol commands for the new connection. The default [arg port]
+number is "119" and the default [arg host] is "news". These defaults
+can be overridden with the environment variables [var NNTPPORT] and
+[var NNTPHOST] respectively.
+
+[para]
+
+Some of the commands supported by this package are not part of the
+nntp rfc 977 ([uri http://www.rfc-editor.org/rfc/rfc977.txt]) and will
+not be available (or implemented) on all nntp servers.
+
+[para]
+
+The access command [arg nntpName] has the following general form:
+
+[list_begin definitions]
+
+[call [arg nntpName] [method method] [opt [arg "arg arg ..."]]]
+
+[arg Option] and the [arg arg]s determine the exact behavior of the
+command.
+
+[list_end]
+
+[call [arg nntpName] [method article] [opt [arg msgid]]]
+
+Query the server for article [arg msgid] from the current group. The article
+is returned as a valid tcl list which contains the headers, followed by
+a blank line, and then followed by the body of the article. Each element
+in the list is one line of the article.
+
+[call [arg nntpName] [method authinfo] [opt [arg user]] [opt [arg pass]]]
+
+Send authentication information (username and password) to the server.
+
+[call [arg nntpName] [method body] [opt [arg msgid]]]
+
+Query the server for the body of the article [arg msgid] from the current
+group. The body of the article is returned as a valid tcl list. Each element
+in the list is one line of the body of the article.
+
+[call [arg nntpName] [method configure]]
+[call [arg nntpName] [method configure] [arg option]]
+[call [arg nntpName] [method configure] [arg option] [arg value] ...]
+[call [arg nntpName] [method cget] [arg option]]
+
+Query and configure options of the nntp connection object. Currently
+only one option is supported, [option -binary]. When set articles are
+retrieved as binary data instead of text. The only methods affected by
+this are [method article] and [method body].
+
+[para]
+
+One application of this option would be the download of articles
+containing yEnc encoded images. Although encoded the data is still
+mostly binary and retrieving it as text will corrupt the information.
+
+[para]
+
+See package [package yencode] for both encoder and decoder of such data.
+
+[call [arg nntpName] [method date]]
+
+Query the server for the servers current date. The date is returned in the
+format [emph YYYYMMDDHHMMSS].
+
+[call [arg nntpName] [method group] [opt [arg group]]]
+
+Optionally set the current group, and retrieve information about the
+currently selected group. Returns the estimated number of articles in
+the group followed by the number of the first article in the group, followed
+by the last article in the group, followed by the name of the group.
+
+[call [arg nntpName] [method head] [opt [arg msgid]]]
+
+Query the server for the headers of the article [arg msgid] from the current
+group. The headers of the article are returned as a valid tcl list. Each element
+in the list is one line of the headers of the article.
+
+[call [arg nntpName] [method help]]
+
+Retrieves a list of the commands that are supported by the news server that
+is currently attached to.
+
+[call [arg nntpName] [method last]]
+
+Sets the current article pointer to point to the previous message (if there is
+one) and returns the msgid of that message.
+
+[call [arg nntpName] [method list]]
+
+Returns a tcl list of valid newsgroups and associated information. Each
+newsgroup is returned as an element in the tcl list with the following format:
+[example {
+ group last first p
+}]
+where <group> is the name of the newsgroup, <last> is the number of
+the last known article currently in that newsgroup, <first> is the
+number of the first article currently in the newsgroup, and <p> is
+either 'y' or 'n' indicating whether posting to this newsgroup is
+allowed ('y') or prohibited ('n').
+[para]
+The <first> and <last> fields will always be numeric. They may have
+leading zeros. If the <last> field evaluates to less than the
+<first> field, there are no articles currently on file in the
+newsgroup.
+
+[call [arg nntpName] [method listgroup] [opt [arg group]]]
+
+Query the server for a list of all the messages (message numbers) in the
+group specified by the argument [arg group] or by the current group if
+the [arg group] argument was not passed.
+
+[call [arg nntpName] [method mode_reader]]
+
+Query the server for its nntp 'MODE READER' response string.
+
+[call [arg nntpName] [method newgroups] [arg since]]
+
+Query the server for a list of all the new newsgroups created since the time
+specified by the argument [arg since]. The argument [arg since] can be any
+time string that is understood by [cmd {clock scan}]. The tcl list of newsgroups
+is returned in a similar form to the list of groups returned by the
+[cmd {nntpName list}] command. Each element of the list has the form:
+
+[example {
+ group last first p
+}]
+where <group> is the name of the newsgroup, <last> is the number of
+the last known article currently in that newsgroup, <first> is the
+number of the first article currently in the newsgroup, and <p> is
+either 'y' or 'n' indicating whether posting to this newsgroup is
+allowed ('y') or prohibited ('n').
+
+[call [arg nntpName] [method newnews]]
+
+Query the server for a list of new articles posted to the current group in the
+last day.
+
+[call [arg nntpName] [method newnews] [arg since]]
+
+Query the server for a list of new articles posted to the current group since
+the time specified by the argument [arg since]. The argument [arg since] can
+be any time string that is understood by [cmd {clock scan}].
+
+[call [arg nntpName] [method newnews] [arg group] [opt [arg since]]]
+
+Query the server for a list of new articles posted to the group specified by
+the argument [arg group] since the time specified by the argument [arg since]
+(or in the past day if no [arg since] argument is passed. The argument
+[arg since] can be any time string that is understood by [cmd {clock scan}].
+
+[call [arg nntpName] [method next]]
+
+Sets the current article pointer to point to the next message (if there is
+one) and returns the msgid of that message.
+
+[call [arg nntpName] [method post] [arg article]]
+
+Posts an article of the form specified in
+RFC 1036 ([uri http://www.rfc-editor.org/rfc/rfc1036.txt], successor
+to RFC 850) to the current news group.
+
+[call [arg nntpName] [method slave]]
+
+Identifies a connection as being made from a slave nntp server. This might
+be used to indicate that the connection is serving multiple people and should
+be given priority. Actual use is entirely implementation dependent and may
+vary from server to server.
+
+[call [arg nntpName] [method stat] [opt [arg msgid]]]
+
+The stat command is similar to the article command except that no
+text is returned. When selecting by message number within a group,
+the stat command serves to set the current article pointer without
+sending text. The returned acknowledgment response will contain the
+message-id, which may be of some value. Using the stat command to
+select by message-id is valid but of questionable value, since a
+selection by message-id does NOT alter the "current article pointer"
+
+[call [arg nntpName] [method quit]]
+
+Gracefully close the connection after sending a NNTP QUIT command down
+the socket.
+
+[call [arg nntpName] [method xgtitle] [opt [arg group_pattern]]]
+
+Returns a tcl list where each element is of the form:
+[example {
+newsgroup description
+}]
+If a [arg group_pattern] is specified then only newsgroups that match
+the pattern will have their name and description returned.
+
+[call [arg nntpName] [method xhdr] [arg field] [opt [arg range]]]
+
+Returns the specified header field value for the current message or for a
+list of messages from the current group. [arg field] is the title of a
+field in the header such as from, subject, date, etc. If [arg range] is
+not specified or is "" then the current message is queried. The command
+returns a list of elements where each element has the form of:
+[example {
+ msgid value
+}]
+Where msgid is the number of the message and value is the value set for the
+queried field. The [arg range] argument can be in any of the following forms:
+
+[list_begin definitions]
+
+[def [const {""}]]
+
+The current message is queried.
+
+[def [arg msgid1]-[arg msgid2]]
+
+All messages between [arg msgid1] and [arg msgid2]
+(including [arg msgid1] and [arg msgid2]) are queried.
+
+[def "[arg msgid1] [arg msgid2]"]
+
+All messages between [arg msgid1] and [arg msgid2]
+(including [arg msgid1] and [arg msgid2]) are queried.
+
+[list_end]
+
+[call [arg nntpName] [method xover] [opt [arg range]]]
+
+Returns header information for the current message or for a range of messages
+from the current group. The information is returned in a tcl list
+where each element is of the form:
+[example {
+ msgid subject from date idstring bodysize headersize xref
+}]
+If [arg range] is not specified or is "" then the current message is queried.
+The [arg range] argument can be in any of the following forms:
+
+[list_begin definitions]
+
+[def [const {""}]]
+
+The current message is queried.
+
+[def [arg msgid1]-[arg msgid2]]
+
+All messages between [arg msgid1] and [arg msgid2]
+(including [arg msgid1] and [arg msgid2]) are queried.
+
+[def "[arg msgid1] [arg msgid2]"]
+
+All messages between [arg msgid1] and [arg msgid2]
+(including [arg msgid1] and [arg msgid2]) are queried.
+
+[list_end]
+
+[call [arg nntpName] [method xpat] [arg field] [arg range] [opt [arg pattern_list]]]
+
+Returns the specified header field value for a specified message or for a
+list of messages from the current group where the messages match the
+pattern(s) given in the pattern_list. [arg field] is the title of a
+field in the header such as from, subject, date, etc. The information is
+returned in a tcl list where each element is of the form:
+[example {
+ msgid value
+}]
+Where msgid is the number of the message and value is the value set for the
+queried field. The [arg range] argument can be in any of the following forms:
+
+[list_begin definitions]
+
+[def [arg msgid]]
+
+The message specified by [arg msgid] is queried.
+
+[def [arg msgid1]-[arg msgid2]]
+
+All messages between [arg msgid1] and [arg msgid2]
+(including [arg msgid1] and [arg msgid2]) are queried.
+
+[def "[arg msgid1] [arg msgid2]"]
+
+All messages between [arg msgid1] and [arg msgid2]
+(including [arg msgid1] and [arg msgid2]) are queried.
+
+[list_end]
+[list_end]
+
+[section EXAMPLE]
+
+A bigger example for posting a single article.
+
+[para]
+[example {
+ package require nntp
+ set n [nntp::nntp NNTP_SERVER]
+ $n post "From: USER@DOMAIN.EXT (USER_FULL)
+ Path: COMPUTERNAME!USERNAME
+ Newsgroups: alt.test
+ Subject: Tcl test post -ignore
+ Message-ID: <[pid][clock seconds]
+ @COMPUTERNAME>
+ Date: [clock format [clock seconds] -format "%a, %d %
+ b %y %H:%M:%S GMT" -gmt true]
+
+ Test message body"
+}]
+
+[vset CATEGORY nntp]
+[include ../doctools2base/include/feedback.inc]
+[manpage_end]
diff --git a/tcllib/modules/nntp/nntp.tcl b/tcllib/modules/nntp/nntp.tcl
new file mode 100644
index 0000000..58b1c73
--- /dev/null
+++ b/tcllib/modules/nntp/nntp.tcl
@@ -0,0 +1,979 @@
+# nntp.tcl --
+#
+# nntp implementation for Tcl.
+#
+# Copyright (c) 1998-2000 by Ajuba Solutions.
+# All rights reserved.
+#
+# RCS: @(#) $Id: nntp.tcl,v 1.13 2004/05/03 22:56:25 andreas_kupries Exp $
+
+package require Tcl 8.2
+package provide nntp 0.2.1
+
+namespace eval ::nntp {
+ # The socks variable holds the handle to the server connections
+ variable socks
+
+ # The counter is used to help create unique connection names
+ variable counter 0
+
+ # commands is the list of subcommands recognized by nntp
+ variable commands [list \
+ "article" \
+ "authinfo" \
+ "body" \
+ "date" \
+ "group" \
+ "head" \
+ "help" \
+ "last" \
+ "list" \
+ "listgroup" \
+ "mode_reader" \
+ "newgroups" \
+ "newnews" \
+ "next" \
+ "post" \
+ "stat" \
+ "quit" \
+ "xgtitle" \
+ "xhdr" \
+ "xover" \
+ "xpat" \
+ ]
+
+ set ::nntp::eol "\n"
+
+ # only export one command, the one used to instantiate a new
+ # nntp connection
+ namespace export nntp
+
+}
+
+# ::nntp::nntp --
+#
+# Create a new nntp connection.
+#
+# Arguments:
+# server - The name of the nntp server to connect to (optional).
+# port - The port number to connect to (optional).
+# name - The name of the nntp connection to create (optional).
+#
+# Results:
+# Creates a connection to the a nntp server. By default the
+# connection is established with the machine 'news' at port '119'
+# These defaults can be overridden with the environment variables
+# NNTPPORT and NNTPHOST, or can be passed as optional arguments
+
+proc ::nntp::nntp {{server ""} {port ""} {name ""}} {
+ global env
+ variable connections
+ variable counter
+ variable socks
+
+ # If a name wasn't specified for the connection, create a new 'unique'
+ # name for the connection
+
+ if { [llength [info level 0]] < 4 } {
+ set counter 0
+ set name "nntp${counter}"
+ while {[lsearch -exact [info commands] $name] >= 0} {
+ incr counter
+ set name "nntp${counter}"
+ }
+ }
+
+ if { ![string equal [info commands ::$name] ""] } {
+ error "command \"$name\" already exists, unable to create nntp connection"
+ }
+
+ upvar 0 ::nntp::${name}data data
+
+ set socks($name) [list ]
+
+ # Initialize instance specific variables
+
+ set data(debug) 0
+ set data(eol) "\n"
+
+ # Logic to determine whether to use the specified nntp server, or to use
+ # the default
+
+ if {$server == ""} {
+ if {[info exists env(NNTPSERVER)]} {
+ set data(host) "$env(NNTPSERVER)"
+ } else {
+ set data(host) "news"
+ }
+ } else {
+ set data(host) $server
+ }
+
+ # Logic to determine whether to use the specified nntp port, or to use the
+ # default.
+
+ if {$port == ""} {
+ if {[info exists env(NNTPPORT)]} {
+ set data(port) $env(NNTPPORT)
+ } else {
+ set data(port) 119
+ }
+ } else {
+ set data(port) $port
+ }
+
+ set data(code) 0
+ set data(mesg) ""
+ set data(addr) ""
+ set data(binary) 0
+
+ set sock [socket $data(host) $data(port)]
+
+ set data(sock) $sock
+
+ # Create the command to manipulate the nntp connection
+
+ interp alias {} ::$name {} ::nntp::NntpProc $name
+
+ ::nntp::response $name
+
+ return $name
+}
+
+# ::nntp::NntpProc --
+#
+# Command that processes all nntp object commands.
+#
+# Arguments:
+# name name of the nntp object to manipulate.
+# args command name and args for the command.
+#
+# Results:
+# Calls the appropriate nntp procedure for the command specified in
+# 'args' and passes 'args' to the command/procedure.
+
+proc ::nntp::NntpProc {name {cmd ""} args} {
+
+ # Do minimal args checks here
+
+ if { [llength [info level 0]] < 3 } {
+ error "wrong # args: should be \"$name option ?arg arg ...?\""
+ }
+
+ # Split the args into command and args components
+
+ if { [llength [info commands ::nntp::_$cmd]] == 0 } {
+ variable commands
+ set optlist [join $commands ", "]
+ set optlist [linsert $optlist "end-1" "or"]
+ error "bad option \"$cmd\": must be $optlist"
+ }
+
+ # Call the appropriate command with its arguments
+
+ return [eval [linsert $args 0 ::nntp::_$cmd $name]]
+}
+
+# ::nntp::okprint --
+#
+# Used to test the return code stored in data(code) to
+# make sure that it is alright to right to the socket.
+#
+# Arguments:
+# name name of the nntp object.
+#
+# Results:
+# Either throws an error describing the failure, or
+# 'args' and passes 'args' to the command/procedure or
+# returns 1 for 'OK' and 0 for error states.
+
+proc ::nntp::okprint {name} {
+ upvar 0 ::nntp::${name}data data
+
+ if {$data(code) >=400} {
+ set val [expr {(0 < $data(code)) && ($data(code) < 400)}]
+ error "NNTPERROR: $data(code) $data(mesg)"
+ }
+
+ # Codes less than 400 are good
+
+ return [expr {(0 < $data(code)) && ($data(code) < 400)}]
+}
+
+# ::nntp::message --
+#
+# Used to format data(mesg) for printing to the socket
+# by appending the appropriate end of line character which
+# is stored in data(eol).
+#
+# Arguments:
+# name name of the nntp object.
+#
+# Results:
+# Returns a string containing the message from data(mesg) followed
+# by the eol character(s) stored in data(eol)
+
+proc ::nntp::message {name} {
+ upvar 0 ::nntp::${name}data data
+
+ return "$data(mesg)$data(eol)"
+}
+
+#################################################
+#
+# NNTP Methods
+#
+
+proc ::nntp::_cget {name option} {
+ upvar 0 ::nntp::${name}data data
+
+ if {[string equal $option -binary]} {
+ return $data(binary)
+ } else {
+ return -code error \
+ "Illegal option \"$option\", expected \"-binary\""
+ }
+}
+
+proc ::nntp::_configure {name args} {
+ upvar 0 ::nntp::${name}data data
+
+ if {[llength $args] == 0} {
+ return [list -binary $data(binary)]
+ }
+ if {[llength $args] == 1} {
+ return [_cget $name [lindex $args 0]]
+ }
+ if {([llength $args] % 2) == 1} {
+ return -code error \
+ "wrong#args: expected even number of elements"
+ }
+ foreach {o v} $args {
+ if {[string equal $o -binary]} {
+ if {![string is boolean -strict $v]} {
+ return -code error \
+ "Expected boolean, got \"$v\""
+ }
+ set data(binary) $v
+ } else {
+ return -code error \
+ "Illegal option \"$o\", expected \"-binary\""
+ }
+ }
+ return {}
+}
+
+
+# ::nntp::_article --
+#
+# Internal article proc. Called by the 'nntpName article' command.
+# Retrieves the article specified by msgid, in the group specified by
+# the 'nntpName group' command. If no msgid is specified the current
+# (or first) article in the group is retrieved
+#
+# Arguments:
+# name name of the nntp object.
+# msgid The article number to retrieve
+#
+# Results:
+# Returns the message (if there is one) from the specified group as
+# a valid tcl list where each element is a line of the message.
+# If no article is found, the "" string is returned.
+#
+# According to RFC 977 the responses are:
+#
+# 220 n article retrieved - head and body follow
+# (n = article number, = message-id)
+# 221 n article retrieved - head follows
+# 222 n article retrieved - body follows
+# 223 n article retrieved - request text separately
+# 412 no newsgroup has been selected
+# 420 no current article has been selected
+# 423 no such article number in this group
+# 430 no such article found
+#
+
+proc ::nntp::_article {name {msgid ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "ARTICLE $msgid"]
+}
+
+# ::nntp::_authinfo --
+#
+# Internal authinfo proc. Called by the 'nntpName authinfo' command.
+# Passes the username and password for a nntp server to the nntp server.
+#
+# Arguments:
+# name Name of the nntp object.
+# user The username for the nntp server.
+# pass The password for 'username' on the nntp server.
+#
+# Results:
+# Returns the result of the attempts to set the username and password
+# on the nntp server ( 1 if successful, 0 if failed).
+
+proc ::nntp::_authinfo {name {user "guest"} {pass "foobar"}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) ""
+ set res [::nntp::command $name "AUTHINFO USER $user"]
+ if {$res} {
+ set res [expr {$res && [::nntp::command $name "AUTHINFO PASS $pass"]}]
+ }
+ return $res
+}
+
+# ::nntp::_body --
+#
+# Internal body proc. Called by the 'nntpName body' command.
+# Retrieves the body of the article specified by msgid from the group
+# specified by the 'nntpName group' command. If no msgid is specified
+# the current (or first) message body is returned
+#
+# Arguments:
+# name Name of the nntp object.
+# msgid The number of the body of the article to retrieve
+#
+# Results:
+# Returns the body of article 'msgid' from the group specified through
+# 'nntpName group'. If msgid is not specified or is "" then the body of
+# the current (or the first) article in the newsgroup will be returned
+# as a valid tcl list. The "" string will be returned if there is no
+# article 'msgid' or if no group has been specified.
+
+proc ::nntp::_body {name {msgid ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "BODY $msgid"]
+}
+
+# ::nntp::_group --
+#
+# Internal group proc. Called by the 'nntpName group' command.
+# Sets the current group on the nntp server to the group passed in.
+#
+# Arguments:
+# name Name of the nntp object.
+# group The name of the group to set as the default group.
+#
+# Results:
+# Sets the default group to the group specified. If no group is specified
+# or if an invalid group is specified an error is thrown.
+#
+# According to RFC 977 the responses are:
+#
+# 211 n f l s group selected
+# (n = estimated number of articles in group,
+# f = first article number in the group,
+# l = last article number in the group,
+# s = name of the group.)
+# 411 no such news group
+
+proc ::nntp::_group {name {group ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "groupinfo"
+ if {$group == ""} {
+ set group $data(group)
+ }
+ return [::nntp::command $name "GROUP $group"]
+}
+
+# ::nntp::_head --
+#
+# Internal head proc. Called by the 'nntpName head' command.
+# Retrieves the header of the article specified by msgid from the group
+# specified by the 'nntpName group' command. If no msgid is specified
+# the current (or first) message header is returned
+#
+# Arguments:
+# name Name of the nntp object.
+# msgid The number of the header of the article to retrieve
+#
+# Results:
+# Returns the header of article 'msgid' from the group specified through
+# 'nntpName group'. If msgid is not specified or is "" then the header of
+# the current (or the first) article in the newsgroup will be returned
+# as a valid tcl list. The "" string will be returned if there is no
+# article 'msgid' or if no group has been specified.
+
+proc ::nntp::_head {name {msgid ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "HEAD $msgid"]
+}
+
+# ::nntp::_help --
+#
+# Internal help proc. Called by the 'nntpName help' command.
+# Retrieves a list of the valid nntp commands accepted by the server.
+#
+# Arguments:
+# name Name of the nntp object.
+#
+# Results:
+# Returns the NNTP commands expected by the NNTP server.
+
+proc ::nntp::_help {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "HELP"]
+}
+
+proc ::nntp::_ihave {name {msgid ""} args} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ if {![::nntp::command $name "IHAVE $msgid"]} {
+ return ""
+ }
+ return [::nntp::squirt $name "$args"]
+}
+
+# ::nntp::_last --
+#
+# Internal last proc. Called by the 'nntpName last' command.
+# Sets the current message to the message before the current message.
+#
+# Arguments:
+# name Name of the nntp object.
+#
+# Results:
+# None.
+
+proc ::nntp::_last {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "msgid"
+ return [::nntp::command $name "LAST"]
+}
+
+# ::nntp::_list --
+#
+# Internal list proc. Called by the 'nntpName list' command.
+# Lists all groups or (optionally) all groups of a specified type.
+#
+# Arguments:
+# name Name of the nntp object.
+# Type The type of groups to return (active active.times newsgroups
+# distributions distrib.pats moderators overview.fmt
+# subscriptions) - optional.
+#
+# Results:
+# Returns a tcl list of all groups or the groups that match 'type' if
+# a type is specified.
+
+proc ::nntp::_list {name {type ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "LIST $type"]
+}
+
+# ::nntp::_newgroups --
+#
+# Internal newgroups proc. Called by the 'nntpName newgroups' command.
+# Lists all new groups since a specified time.
+#
+# Arguments:
+# name Name of the nntp object.
+# since The time to find new groups since. The time can be in any
+# format that is accepted by 'clock scan' in tcl.
+#
+# Results:
+# Returns a tcl list of all new groups added since the time specified.
+
+proc ::nntp::_newgroups {name since args} {
+ upvar 0 ::nntp::${name}data data
+
+ set since [clock format [clock scan "$since"] -format "%y%m%d %H%M%S"]
+ set dist ""
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "NEWGROUPS $since $dist"]
+}
+
+# ::nntp::_newnews --
+#
+# Internal newnews proc. Called by the 'nntpName newnews' command.
+# Lists all new news in the specified group since a specified time.
+#
+# Arguments:
+# name Name of the nntp object.
+# group Name of the newsgroup to query.
+# since The time to find new groups since. The time can be in any
+# format that is accepted by 'clock scan' in tcl. Defaults to
+# "1 day ago"
+#
+# Results:
+# Returns a tcl list of all new messages since the time specified.
+
+proc ::nntp::_newnews {name {group ""} {since ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ if {$group != ""} {
+ if {[regexp -- {^[\w\.\-]+$} $group] == 0} {
+ set since $group
+ set group ""
+ }
+ }
+ if {![info exists group] || ($group == "")} {
+ if {[info exists data(group)] && ($data(group) != "")} {
+ set group $data(group)
+ } else {
+ set group "*"
+ }
+ }
+ if {"$since" == ""} {
+ set since [clock format [clock scan "now - 1 day"]]
+ }
+ set since [clock format [clock scan $since] -format "%y%m%d %H%M%S"]
+ set dist ""
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "NEWNEWS $group $since $dist"]
+}
+
+# ::nntp::_next --
+#
+# Internal next proc. Called by the 'nntpName next' command.
+# Sets the current message to the next message after the current message.
+#
+# Arguments:
+# name Name of the nntp object.
+#
+# Results:
+# None.
+
+proc ::nntp::_next {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "msgid"
+ return [::nntp::command $name "NEXT"]
+}
+
+# ::nntp::_post --
+#
+# Internal post proc. Called by the 'nntpName post' command.
+# Posts a message to a newsgroup.
+#
+# Responses (according to RFC 977) to a post request:
+# 240 article posted ok
+# 340 send article to be posted. End with .
+# 440 posting not allowed
+# 441 posting failed
+#
+# Arguments:
+# name Name of the nntp object.
+# article A message of the form specified in RFC 850
+#
+# Results:
+# None.
+
+proc ::nntp::_post {name article} {
+
+ if {![::nntp::command $name "POST"]} {
+ return ""
+ }
+ return [::nntp::squirt $name "$article"]
+}
+
+# ::nntp::_slave --
+#
+# Internal slave proc. Called by the 'nntpName slave' command.
+# Identifies a connection as being made from a slave nntp server.
+# This might be used to indicate that the connection is serving
+# multiple people and should be given priority. Actual use is
+# entirely implementation dependant and may vary from server to
+# server.
+#
+# Arguments:
+# name Name of the nntp object.
+#
+# Results:
+# None.
+#
+# According to RFC 977 the only response is:
+#
+# 202 slave status noted
+
+proc ::nntp::_slave {name} {
+ return [::nntp::command $name "SLAVE"]
+}
+
+# ::nntp::_stat --
+#
+# Internal stat proc. Called by the 'nntpName stat' command.
+# The stat command is similar to the article command except that no
+# text is returned. When selecting by message number within a group,
+# the stat command serves to set the current article pointer without
+# sending text. The returned acknowledgement response will contain the
+# message-id, which may be of some value. Using the stat command to
+# select by message-id is valid but of questionable value, since a
+# selection by message-id does NOT alter the "current article pointer"
+#
+# Arguments:
+# name Name of the nntp object.
+# msgid The number of the message to stat (optional) default is to
+# stat the current article
+#
+# Results:
+# Returns the statistics for the article.
+
+proc ::nntp::_stat {name {msgid ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "status"
+ return [::nntp::command $name "STAT $msgid"]
+}
+
+# ::nntp::_quit --
+#
+# Internal quit proc. Called by the 'nntpName quit' command.
+# Quits the nntp session and closes the socket. Deletes the command
+# that was created for the connection.
+#
+# Arguments:
+# name Name of the nntp object.
+#
+# Results:
+# Returns the return value from the quit command.
+
+proc ::nntp::_quit {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set ret [::nntp::command $name "QUIT"]
+ close $data(sock)
+ rename ${name} {}
+ return $ret
+}
+
+#############################################################
+#
+# Extended methods (not available on all NNTP servers
+#
+
+proc ::nntp::_date {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "msg"
+ return [::nntp::command $name "DATE"]
+}
+
+proc ::nntp::_listgroup {name {group ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "LISTGROUP $group"]
+}
+
+proc ::nntp::_mode_reader {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "msg"
+ return [::nntp::command $name "MODE READER"]
+}
+
+proc ::nntp::_xgtitle {name {group_pattern ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "XGTITLE $group_pattern"]
+}
+
+proc ::nntp::_xhdr {name {header "message-id"} {list ""} {last ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ if {![regexp -- {\d+-\d+} $list]} {
+ if {"$last" != ""} {
+ set list "$list-$last"
+ } else {
+ set list ""
+ }
+ }
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "XHDR $header $list"]
+}
+
+proc ::nntp::_xindex {name {group ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ if {("$group" == "") && [info exists data(group)]} {
+ set group $data(group)
+ }
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "XINDEX $group"]
+}
+
+proc ::nntp::_xmotd {name {since ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ if {"$since" != ""} {
+ set since [clock seconds]
+ }
+ set since [clock format [clock scan $since] -format "%y%m%d %H%M%S"]
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "XMOTD $since"]
+}
+
+proc ::nntp::_xover {name {list ""} {last ""}} {
+ upvar 0 ::nntp::${name}data data
+ if {![regexp -- {\d+-\d+} $list]} {
+ if {"$last" != ""} {
+ set list "$list-$last"
+ } else {
+ set list ""
+ }
+ }
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "XOVER $list"]
+}
+
+proc ::nntp::_xpat {name {header "subject"} {list 1} {last ""} args} {
+ upvar 0 ::nntp::${name}data data
+
+ set patterns ""
+
+ if {![regexp -- {\d+-\d+} $list]} {
+ if {("$last" != "") && ([string is digit $last])} {
+ set list "$list-$last"
+ }
+ } elseif {"$last" != ""} {
+ set patterns "$last"
+ }
+
+ if {[llength $args] > 0} {
+ set patterns "$patterns $args"
+ }
+
+ if {"$patterns" == ""} {
+ set patterns "*"
+ }
+
+ set data(cmnd) "fetch"
+ return [::nntp::command $name "XPAT $header $list $patterns"]
+}
+
+proc ::nntp::_xpath {name {msgid ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(cmnd) "msg"
+ return [::nntp::command $name "XPATH $msgid"]
+}
+
+proc ::nntp::_xsearch {name args} {
+ set res [::nntp::command $name "XSEARCH"]
+ if {!$res} {
+ return ""
+ }
+ return [::nntp::squirt $name "$args"]
+}
+
+proc ::nntp::_xthread {name args} {
+ upvar 0 ::nntp::${name}data data
+
+ if {[llength $args] > 0} {
+ set filename "dbinit"
+ } else {
+ set filename "thread"
+ }
+ set data(cmnd) "fetchbinary"
+ return [::nntp::command $name "XTHREAD $filename"]
+}
+
+######################################################
+#
+# Helper methods
+#
+
+proc ::nntp::cmd {name cmd} {
+ upvar 0 ::nntp::${name}data data
+
+ set eol "\015\012"
+ set sock $data(sock)
+ if {$data(debug)} {
+ puts stderr "$sock command $cmd"
+ }
+ puts $sock "$cmd"
+ flush $sock
+ return
+}
+
+proc ::nntp::command {name args} {
+ set res [eval [linsert $args 0 ::nntp::cmd $name]]
+
+ return [::nntp::response $name]
+}
+
+proc ::nntp::msg {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set res [::nntp::okprint $name]
+ if {!$res} {
+ return ""
+ }
+ return $data(mesg)
+}
+
+proc ::nntp::groupinfo {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set data(group) ""
+
+ if {[::nntp::okprint $name] && [regexp -- {(\d+)\s+(\d+)\s+(\d+)\s+([\w\.]+)} \
+ $data(mesg) match count first last data(group)]} {
+ return [list $count $first $last $data(group)]
+ }
+ return ""
+}
+
+proc ::nntp::msgid {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set result ""
+ if {[::nntp::okprint $name] && \
+ [regsub -- {\s+<[^>]+>} $data(mesg) {} result]} {
+ return $result
+ } else {
+ return ""
+ }
+}
+
+proc ::nntp::status {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set result ""
+ if {[::nntp::okprint $name] && \
+ [regexp -- {\d+\s+<[^>]+>} $data(mesg) result]} {
+ return $result
+ } else {
+ return ""
+ }
+}
+
+proc ::nntp::fetch {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set eol "\012"
+
+ if {![::nntp::okprint $name]} {
+ return ""
+ }
+ set sock $data(sock)
+
+ if {$data(binary)} {
+ set oldenc [fconfigure $sock -encoding]
+ fconfigure $sock -encoding binary
+ }
+
+ set result [list ]
+ while {![eof $sock]} {
+ gets $sock line
+ regsub -- {\015?\012$} $line $data(eol) line
+
+ if {[string match "." $line]} {
+ break
+ }
+ if { [string match "..*" $line] } {
+ lappend result [string range $line 1 end]
+ } else {
+ lappend result $line
+ }
+ }
+
+ if {$data(binary)} {
+ fconfigure $sock -encoding $oldenc
+ }
+
+ return $result
+}
+
+proc ::nntp::response {name} {
+ upvar 0 ::nntp::${name}data data
+
+ set eol "\012"
+
+ set sock $data(sock)
+
+ gets $sock line
+ set data(code) 0
+ set data(mesg) ""
+
+ if {$line == ""} {
+ error "nntp: unexpected EOF on $sock\n"
+ }
+
+ regsub -- {\015?\012$} $line "" line
+
+ set result [regexp -- {^((\d\d)(\d))\s*(.*)} $line match \
+ data(code) val1 val2 data(mesg)]
+
+ if {$result == 0} {
+ puts stderr "nntp garbled response: $line\n";
+ return ""
+ }
+
+ if {$val1 == 20} {
+ set data(post) [expr {!$val2}]
+ }
+
+ if {$data(debug)} {
+ puts stderr "val1 $val1 val2 $val2"
+ puts stderr "code '$data(code)'"
+ puts stderr "mesg '$data(mesg)'"
+ if {[info exists data(post)]} {
+ puts stderr "post '$data(post)'"
+ }
+ }
+
+ return [::nntp::returnval $name]
+}
+
+proc ::nntp::returnval {name} {
+ upvar 0 ::nntp::${name}data data
+
+ if {([info exists data(cmnd)]) \
+ && ($data(cmnd) != "")} {
+ set command $data(cmnd)
+ } else {
+ set command okprint
+ }
+
+ if {$data(debug)} {
+ puts stderr "returnval command '$command'"
+ }
+
+ set data(cmnd) ""
+ return [::nntp::$command $name]
+}
+
+proc ::nntp::squirt {name {body ""}} {
+ upvar 0 ::nntp::${name}data data
+
+ set body [split $body \n]
+
+ if {$data(debug)} {
+ puts stderr "$data(sock) sending [llength $body] lines\n";
+ }
+
+ foreach line $body {
+ # Print each line, possibly prepending a dot for lines
+ # starting with a dot and trimming any trailing \n.
+ if { [string match ".*" $line] } {
+ set line ".$line"
+ }
+ puts $data(sock) $line
+ }
+ puts $data(sock) "."
+ flush $data(sock)
+
+ if {$data(debug)} {
+ puts stderr "$data(sock) is finished sending"
+ }
+ return [::nntp::response $name]
+}
+#eof
+
diff --git a/tcllib/modules/nntp/pkgIndex.tcl b/tcllib/modules/nntp/pkgIndex.tcl
new file mode 100644
index 0000000..ac36bfe
--- /dev/null
+++ b/tcllib/modules/nntp/pkgIndex.tcl
@@ -0,0 +1,12 @@
+# Tcl package index file, version 1.1
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script. It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands. When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+if {![package vsatisfies [package provide Tcl] 8.2]} {return}
+package ifneeded nntp 0.2.1 [list source [file join $dir nntp.tcl]]