Class: Net::NNTP

Inherits:
Object
  • Object
show all
Defined in:
lib/nntp.rb

Overview

Net::NNTP

What is This Library?

This library provides functionality to retrieve and, or post Usenet news articles via NNTP, the Network News Transfer Protocol. The Usenet is a world-wide distributed discussion system. It consists of a set of “newsgroups” with names that are classified hierarchically by topic. “articles” or “messages” are “posted” to these newsgroups by people on computers with the appropriate software – these articles are then broadcast to other interconnected NNTP servers via a wide variety of networks. For details of NNTP itself, see [RFC977] (www.ietf.org/rfc/rfc977.txt).

What is This Library NOT?

This library does NOT provide functions to compose Usenet news. You must create and, or format them yourself as per guidelines per Standard for Interchange of Usenet messages, see [RFC850], [RFC2047] and a fews other RFC’s (www.ietf.org/rfc/rfc850.txt), (www.ietf.org/rfc/rfc2047.txt).

FYI: the official documentation on Usenet news extentions is: [RFC2980] (www.ietf.org/rfc/rfc2980.txt).

Examples

Posting Messages

You must open a connection to an NNTP server before posting messages. The first argument is the address of your NNTP server, and the second argument is the port number. Using NNTP.start with a block is the simplest way to do this. This way, the NNTP connection is closed automatically after the block is executed.

require 'rubygems'
require 'nntp'
Net::NNTP.start('your.nntp.server', 119) do |nntp|
  # Use the NNTP object nntp only in this block.
end

Replace ‘your.nntp.server’ with your NNTP server. Normally your system manager or internet provider supplies a server for you.

Then you can post messages.

require 'date'
date = DateTime.now().strftime(fmt='%a, %d %b %Y %T %z')

msgstr = <<END_OF_MESSAGE
From: Your Name <[email protected]>
Newsgroups: news.group.one, news.group.two ...
Subject: test message

Date: ##date

This is a test message.
END_OF_MESSAGE

require 'rubygems'
require 'nntp'
Net::NNTP.start('your.nntp.server', 119) do |nntp|
  nntp.post msgstr
end

NOTE: The NNTP message headers such as Date:, Message-ID: and, or Path: if ommited, may also be generated and added by your Usenet news server; better you verify the behavior of your news server.

Closing the Session

You MUST close the NNTP session after posting messages, by calling the Net::NNTP#finish method:

# using NNTP#finish
nntp = Net::NNTP.start('your.nntp.server', 119)
nntp.post msgstr
nntp.finish

You can also use the block form of NNTP.start/NNTP#start. This closes the NNTP session automatically:

# using block form of NNTP.start
Net::NNTP.start('your.nntp.server', 119) do |nntp|
  nntp.post msgstr
end

I strongly recommend this scheme. This form is simpler and more robust.

NNTP Authentication

The Net::NNTP class may support various authentication schemes depending on your news server’s reponse to CAPABILITIES command. To use NNTP authentication, pass extra arguments to NNTP.start/NNTP#start.

See NNTP Extension for Authentication: (www.ietf.org/internet-drafts/draft-ietf-nntpext-authinfo-07.txt)

Net::NNTP.start('your.nntp.server', 119,
                'YourAccountName', 'YourPassword', :method)

Where :method can be one of the ‘gassapi’, ‘digest_md5’, ‘cram_md5’, ‘starttls’, ‘external’, ‘plain’, ‘generic’, ‘simple’ or ‘original’; the later and, or unencrypted ones are less secure!

In the case of method :generic argumnents should be passed to a format string as follows:

Net::NNTP.start('your.nntp.server', 119,
                "format", *arguments, :generic)

NOTE: The Authentication mechanism will fallback to a lesser secure scheme, if your Usenet server does not supports method opted by you, except for the :generic option.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(address, port = nil) ⇒ NNTP

Creates a new Net::NNTP object.

address is the hostname or ip address of your NNTP server. port is the port to connect to; it defaults to port 119.

This method does not opens any TCP connection. You can use NNTP.start instead of NNTP.new if you want to do everything at once. Otherwise, follow NNTP.new with optional changes to :open_timeout, :read_timeout and, or NNTP#set_debug_output and then NNTP#start.



215
216
217
218
219
220
221
222
223
224
# File 'lib/nntp.rb', line 215

def initialize(address, port = nil)
  @address = address
  @port = (port || NNTP.default_port)
  @socket = nil
  @started = false
  @open_timeout = 30
  @read_timeout = 60
  @error_occured = false
  @debug_output = nil
end

Instance Attribute Details

#addressObject (readonly)

The address of the NNTP server to connect to.



232
233
234
# File 'lib/nntp.rb', line 232

def address
  @address
end

#open_timeoutObject

Seconds to wait while attempting to open a connection. If the connection cannot be opened within this time, a TimeoutError is raised.



239
240
241
# File 'lib/nntp.rb', line 239

def open_timeout
  @open_timeout
end

#portObject (readonly)

The port number of the NNTP server to connect to.



235
236
237
# File 'lib/nntp.rb', line 235

def port
  @port
end

#read_timeoutObject

Seconds to wait while reading one block (by one read(2) call). If the read(2) call does not complete within this time, a TimeoutError is raised.



244
245
246
# File 'lib/nntp.rb', line 244

def read_timeout
  @read_timeout
end

Class Method Details

.default_portObject

The default NNTP port, port 119.



201
202
203
# File 'lib/nntp.rb', line 201

def NNTP.default_port
  119
end

.start(address, port = nil, user = nil, secret = nil, method = nil, &block) ⇒ Object

Creates a new Net::NNTP object and connects to the server.

This method is equivalent to:

Net::NNTP.new(address, port).start(, password, :method)

Example

Net::NNTP.start('your.nntp.server') do |nntp|
  nntp.post msgstr
end

Block Usage

If called with a block, the newly-opened Net::NNTP object is yielded to the block, and automatically closed when the block finishes. If called without a block, the newly-opened Net::NNTP object is returned to the caller, and it is the caller’s responsibility to close it when finished.

Parameters

address is the hostname or ip address of your nntp server.

port is the port to connect to; it defaults to port 119.

The remaining arguments are used for NNTP authentication, if required or desired. user is the account name, secret is your password or other authentication token, and method is the authentication type; defaults to ‘original’. Please read the discussion of NNTP Authentication in the overview notes above.

Errors

This method may raise:

  • Net::NNTPAuthenticationError

  • Net::NNTPFatalError

  • Net::NNTPServerBusy

  • Net::NNTPSyntaxError

  • Net::NNTPUnknownError

  • IOError

  • TimeoutError



318
319
320
321
322
# File 'lib/nntp.rb', line 318

def NNTP.start(address, port = nil,
               user = nil, secret = nil, method = nil,
               &block) # :yield: nntp
  new(address, port).start(user, secret, method, &block)
end

Instance Method Details

#article(id_num = nil) ⇒ Object

ARTICLE [<Message-ID>|<Number>]



528
529
530
531
# File 'lib/nntp.rb', line 528

def article(id_num = nil)
  stat, text = longcmd("ARTICLE #{id_num}".strip)
  return stat[0..2], text
end

#body(id_num = nil) ⇒ Object

BODY [<Message-ID>|<Number>]



534
535
536
537
# File 'lib/nntp.rb', line 534

def body(id_num = nil)
  stat, text = longcmd("BODY #{id_num}".strip)
  return stat[0..2], text
end

#dateObject

DATE

Raises:



546
547
548
549
550
551
552
553
# File 'lib/nntp.rb', line 546

def date
  text = []
  stat = shortcmd("DATE")
  text << stat[4...12]
  text << stat[12...18]
  raise NNTPDataError, stat, caller unless text[0].length == 8 and text[1].length == 6
  return stat[0..2], text
end

#finishObject

Finishes the NNTP session and closes TCP connection. Raises IOError if not started.

Raises:

  • (IOError)


432
433
434
435
# File 'lib/nntp.rb', line 432

def finish
  raise IOError, 'not yet started' unless started?
  do_finish
end

#group(ng) ⇒ Object

GROUP <Newsgroup>



556
557
558
559
# File 'lib/nntp.rb', line 556

def group(ng)
  stat = shortcmd("GROUP %s", ng)
  return stat[0..2], stat[4..-1].chop
end

#head(id_num = nil) ⇒ Object

HEAD [<Message-ID>|<Number>]



562
563
564
565
# File 'lib/nntp.rb', line 562

def head(id_num = nil)
  stat, text = longcmd("HEAD #{id_num}".strip)
  return stat[0..2], text
end

#helpObject

HELP



568
569
570
571
572
573
574
# File 'lib/nntp.rb', line 568

def help
  stat, text = longcmd('HELP')
  text.each_with_index do |line, index|
    text[index] = line.gsub(/\A\s+/, '')
  end
  return stat[0..2], text
end

#inspectObject

Provide human-readable stringification of class state.



227
228
229
# File 'lib/nntp.rb', line 227

def inspect  #:nodoc:
  "#<#{self.class} #{@address}:#{@port} started=#{@started}>"
end

#io_body(io_output, id_num = nil) ⇒ Object

IO_BODY <output IO object> [<Message-ID>|<Number>]



540
541
542
543
# File 'lib/nntp.rb', line 540

def io_body (io_output, id_num = nil)
  stat = io_longcmd(io_output, "BODY #{id_num}".strip)
  return stat[0..2], io_output
end

#lastObject

LAST



577
578
579
580
# File 'lib/nntp.rb', line 577

def last
  stat = shortcmd('LAST')
  return stat[0..2], stat[4..-1].chop
end

#list(opts = nil) ⇒ Object

LIST [ACTIVE|NEWSGROUPS] [<Wildmat>]]:br: LIST [ACTIVE.TIMES|EXTENSIONS|SUBSCRIPTIONS|OVERVIEW.FMT]



584
585
586
587
# File 'lib/nntp.rb', line 584

def list(opts = nil)
  stat, text = longcmd("LIST #{opts}".strip)
  return stat[0..2], text
end

#listgroup(ng) ⇒ Object

LISTGROUP <Newsgroup>



590
591
592
593
# File 'lib/nntp.rb', line 590

def listgroup(ng)
  stat, text = longcmd("LISTGROUP #{ng}".strip)
  return stat[0..2], text
end

#newgroups(date, time, tzone = nil) ⇒ Object

NEWGROUPS <yymmdd> <hhmmss> [GMT]



603
604
605
606
# File 'lib/nntp.rb', line 603

def newgroups(date, time, tzone = nil)
  stat, text = longcmd("NEWGROUPS #{date} #{time} #{tzone}".strip)
  return stat[0..2], text
end

#nextObject

NEXT



609
610
611
612
# File 'lib/nntp.rb', line 609

def next
  stat = shortcmd('NEXT')
  return stat[0..2], stat[4..-1].chop
end

#open_message_stream(&block) ⇒ Object

Opens a message writer stream and gives it to the block. The stream is valid only in the block, and has these methods:

puts(str = ”)

outputs STR and CR LF.

print(str)

outputs STR.

printf(fmt, *args)

outputs sprintf(fmt,*args).

write(str)

outputs STR and returns the length of written bytes.

<<(str)

outputs STR and returns self.

If a single CR (“r”) or LF (“n”) is found in the message, it is converted to the CR LF pair. You cannot post a binary message with this method.

Parameters

Block

Example

Net::NNTP.start('nntp.example.com', 119) do |nntp|
  nntp.open_message_stream do |f|
    f.puts 'From: [email protected]'
    f.puts 'Newsgroups: news.group.one, news.group.two ...'
    f.puts 'Subject: test message'
    f.puts
    f.puts 'This is a test message.'
  end
end

Errors

This method may raise:

  • Net::NNTPFatalError

  • Net::NNTPPostingNotAllowed

  • Net::NNTPServerBusy

  • Net::NNTPSyntaxError

  • Net::NNTPUnknownError

  • IOError

  • TimeoutError



523
524
525
# File 'lib/nntp.rb', line 523

def open_message_stream(&block) # :yield: stream
  post0 { @socket.write_message_by_block(&block) }
end

#over(range) ⇒ Object

OVER <Range> # e.g first[-]



615
616
617
618
# File 'lib/nntp.rb', line 615

def over(range)
  stat, text = longcmd("OVER #{range}".strip)
  return stat[0..2], text
end

#post(msgstr) ⇒ Object

POST

Posts msgstr as a message. Single CR (“r”) and LF (“n”) found in the msgstr, are converted into the CR LF pair. You cannot post a binary message with this method. msgstr _should include both the message headers and body_. All non US-ASCII, binary and, or multi-part messages should be submitted in an encoded form as per MIME standards.

Example

Net::NNTP.start('nntp.example.com') do |nntp|
  nntp.post msgstr
end

Errors

This method may raise:

  • Net::NNTPFatalError

  • Net::NNTPPostingNotAllowed

  • Net::NNTPServerBusy

  • Net::NNTPSyntaxError

  • Net::NNTPUnknownError

  • IOError

  • TimeoutError



475
476
477
478
479
480
# File 'lib/nntp.rb', line 475

def post(msgstr)
  stat = post0 {
    @socket.write_message msgstr
  }
  return stat[0..3], stat[4..-1].chop
end

#set_debug_output(arg) ⇒ Object

Set an output stream for debug logging. You must call this before #start.

Example

nntp = Net::NNTP.new(addr, port)
nntp.set_debug_output $stderr
nntp.start do |nntp|
  ....
end

WARNING: This method causes serious security holes. Use this method for only debugging.



266
267
268
# File 'lib/nntp.rb', line 266

def set_debug_output(arg)
  @debug_output = arg
end

#slaveObject

SLAVE



627
628
629
630
# File 'lib/nntp.rb', line 627

def slave
  stat = shortcmd('SLAVE')
  return stat[0..2], stat[4..-1].chop
end

#start(user = nil, secret = nil, method = nil) ⇒ Object

Opens a TCP connection and starts the NNTP session.

Parameters

If both of user and secret are given, NNTP authentication will be attempted using the AUTH command. The method specifies the type of authentication to attempt; it must be one of :original, :simple, :generic, :plain, :starttls, :external, :cram_md5, :digest_md5 and, or :gassapi may be used. See the discussion of NNTP Authentication in the overview notes.

Block Usage

When this methods is called with a block, the newly-started NNTP object is yielded to the block, and automatically closed after the block call finishes. Otherwise, it is the caller’s responsibility to close the session when finished.

Example

This is very similar to the class method NNTP.start.

require 'rubygems'
require 'nntp'
nntp = Net::NNTP.new('nntp.news.server', 119)
nntp.start(, password, method) do |nntp|
  nntp.post msgstr
end

The primary use of this method (as opposed to NNTP.start) is probably to set debugging (#set_debug_output), which must be done before the session is started.

Errors

If session has already been started, an IOError will be raised.

This method may raise:

  • Net::NNTPAuthenticationError

  • Net::NNTPFatalError

  • Net::NNTPServerBusy

  • Net::NNTPSyntaxError

  • Net::NNTPUnknownError

  • IOError

  • TimeoutError



377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/nntp.rb', line 377

def start(user = nil, secret = nil, method = nil) # :yield: nntp
  if block_given?
    begin
      do_start(user, secret, method)
      return yield(self)
    ensure
      do_finish
    end
  else
    do_start(user, secret, method)
    return self
  end
end

#started?Boolean

true if the NNTP session has been started.

Returns:

  • (Boolean)


325
326
327
# File 'lib/nntp.rb', line 325

def started?
  @started
end

#stat(id_num = nil) ⇒ Object

STAT [<Message-ID>|<Number>]



633
634
635
636
# File 'lib/nntp.rb', line 633

def stat(id_num = nil)
  stat = shortcmd("STAT #{id_num}".strip)
  return stat[0..2], stat[4..-1].chop
end

#xhdr(header, id_range) ⇒ Object

XHDR <Header> <Message-ID>|<Range> # e.g first[-]



639
640
641
642
# File 'lib/nntp.rb', line 639

def xhdr(header, id_range)
  stat, text = longcmd("XHDR #{header} #{id_range}".strip)
  return stat[0..2], text
end

#xover(range) ⇒ Object

XOVER <Range> # e.g first[-]



645
646
647
648
# File 'lib/nntp.rb', line 645

def xover(range)
  stat, text = longcmd("XOVER #{range}".strip)
  return stat[0..2], text
end