Recipe 18.4 Reading and Posting Usenet News Messages

18.4.1 Problem

You want to connect to a Usenet news server to read and post messages. Your program could send a periodic posting to a newsgroup,[1] summarize a newsgroup, or identify first-time contributors in a newsgroup so you can send them a helpful welcome message.

[1] If so, be sure to check out Ian Kluft's auto-faq program at

18.4.2 Solution

Use the Net::NNTP module:

use Net::NNTP;

$server = Net::NNTP->new("")
    or die "Can't connect to news server: $@\n";
($narticles, $first, $last, $name) = $server->group( "misc.test" )
    or die "Can't select misc.test\n";
$headers  = $server->head($first)
    or die "Can't get headers from article $first in $name\n";
$bodytext = $server->body($first)
    or die "Can't get body from article $first in $name\n";
$article  = $server->article($first)
    or die "Can't get article $first from $name\n";

$server->postok( )
    or warn "Server didn't tell me I could post.\n";

$server->post( [ @lines ] )
    or die "Can't post: $!\n";

18.4.3 Discussion

Usenet is a distributed bulletin board system. Servers exchange messages to ensure that each server gets all messages for the newsgroups it carries. Each server sets its own expiration criteria for how long messages stay on the server. Client newsreaders connect to their designated server (usually belonging to their company, ISP, or university) and can read existing postings and contribute new ones.

Each message (or article, as they're also known) has a set of headers and a body, separated by a blank line. Articles are identified in two ways: the message ID header and an article number within a newsgroup. An article's message ID is stored in the message itself and is guaranteed unique no matter which news server the article was read from. When an article references others, it does so by message ID. A message ID is a string like:


An article can also be identified by its newsgroup and article number within the group. Each news server assigns its own article numbers, so they're valid only for the news server you got them from.

The Net::NNTP constructor connects to the specified news server. If the connection couldn't be made, it returns undef and sets $@ to an error message. If the connection was successful, new returns a new Net::NNTP object:

$server = Net::NNTP->new("")
    or die "Couldn't connect to $@\n";

Once connected, fetch a list of newsgroups with the list method. This returns a reference to a hash whose keys are newsgroup names. Each value is a reference to an array consisting of the first valid article number in the group, the last valid article number in the group, and a string of flags. Flags are typically "y", meaning you may post, but could be "m" for moderated or "=NAME", meaning that the group is an alias for the newsgroup "NAME". Your server might carry over 60,000 newsgroups, so fetching a list of all groups can take a while.

$grouplist = $server->list( )
    or die "Couldn't fetch group list\n";

foreach $group (keys %$grouplist) {
    if ($grouplist->{$group}->[2] eq 'y') {
        # I can post to $group

Much as FTP has the concept of a current directory, the Network News Transfer Protocol (NNTP) has the concept of a current group. Set the current group with the group method:

($narticles, $first, $last, $name) = $server->group("comp.lang.perl.misc")
    or die "Can't select comp.lang.perl.misc\n";

The group method returns a four-element list: the number of articles in the group, the first article number, the last article number, and the name of the group. If the group does not exist, it returns an empty list.

There are two ways to retrieve articles: call article with a message ID, or select a group with group and then call article with an article number. In scalar context, article returns a reference to an array of lines. In list context, it returns a list of lines. If an error occurs, article returns false:

@lines = $server->article($message_id)
    or die "Can't fetch article $message_id: $!\n";

Fetch an article's header or body with the head and body methods. Like article, these methods take an article number or message ID and return a list of lines or an array reference.

@group = $server->group("comp.lang.perl.misc")
    or die "Can't select group comp.lang.perl.misc\n";
@lines = $server->head($group[1])
    or die "Can't get headers from first article in comp.lang.perl.misc\n";

To post an article, give the post method a list of lines or a reference to an array of lines. It returns true if the post succeeded, false otherwise.

    or die "Can't post\n";

Use the postok method to find out whether you may post to that server:

unless ($server->postok( )) {
    warn "You may not post.\n";

Read the manpage for Net::NNTP for a complete list of methods.

18.4.4 See Also

The documentation for the Net::NNTP module; RFC 977, Network News Transfer Protocol; your system's trn(1) and innd(8) manpages (if you have them)