# -*-perl-*-
#
# Copyright (c) 1997 Kevin Johnson <kjj@pobox.com>.
#
# All rights reserved. This program is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: NNTP.pm,v 1.1 1997/03/18 02:37:38 kjj Exp $

package Mail::Folder::NNTP;
use strict;
use vars qw($VERSION @ISA);
use Net::NNTP;
use Mail::Header;

@ISA = qw(Mail::Folder);
$VERSION = '0.05';

Mail::Folder::register_folder_type('Mail::Folder::NNTP', 'news');

=head1 NAME

Mail::Folder::NNTP - An NNTP folder interface for Mail::Folder.

B<WARNING: This code is in alpha release.  Expect the interface to change.>

=head1 SYNOPSIS

C<use Mail::Folder::Maildir;>

=head1 DESCRIPTION

This module provides an interface to newsgroups accessible via the
NNTP protocol.

=cut

use Mail::Folder;
use Mail::Internet;
use Mail::Header;
use MIME::Head;

use Carp;

=head1 METHODS

=head2 open($foldername)

Populates the C<Mail::Folder> object with information about the folder.

The given foldername needs to be of the format
C<news://NEWSHOST/NEWSGROUP> where C<NEWSHOST> is the nntp host and
C<NEWSGROUP> is the news group of interest.  The folder naming issues
have not completely been ironed out yet.

Please note that it opens an NNTP connection for each open NNTP
folder.

If no C<Timeout> option is specified, it defaults to a timeout of 120
seconds.

=over 2

=item * Call the superclass C<open> method.

=item * Make sure it is a valid NNTP foldername.

=item * Connect to the NNTP server referenced in $foldername.

=item * Perform an NNTP C<group> command to determine quantity and
range of articles available.

=item * Loop through available article numbers, retrieve and cache the
headers.

=item * Set C<current_message> to C<first_message>.

=back

=cut

sub open {
  my $self = shift;
  my $foldername = shift;

  return 0 unless $self->SUPER::open($foldername);

  is_valid_folder_format($foldername)
    or croak "$foldername isn't valid for an news folder";

  # these two extractions should never be fatal since is_valid_folder_format
  # should have detected any structural problems with the folder name
  $self->{NNTP_Host} = _extract_hostname($foldername)
    or croak "can't extract hostname from $foldername";
  $self->{NNTP_Newsgroup} = _extract_newsgroup_name($foldername)
    or croak "can't extract newsgroup from $foldername";

  my $timeout = $self->get_options('Timeout');
  $timeout ||= 120;		# default it if no Timeout option specified
  $self->{NNTP_Connection} = new Net::NNTP($self->{NNTP_Host},
					   Timeout => $timeout)
    or return 0;

  returned 0 if (!defined($self->_absorb_folder($foldername)));

  $self->current_message($self->first_message);

  return 1;
}

=head2 close

Calls the superclass C<get_message> method and shuts down the
connection to the NNTP server.

=cut

sub close {
  my $self = shift;

  $self->{NNTP_Connection}->quit;
  return $self->SUPER::close;
}

=head2 sync

Currently a no-op and returns C<0>.

Eventually will expunge articles marked as seen, look for new
articles, update the C<.newsrc> (or equivalent) file, and return the
number of new articles found.

=cut

sub sync {
  my $self = shift;

  return 0;
}

=head2 pack

Since the association between article and article number is determined
by the server, this method is a no-op.

It return C<1>.

=cut

sub pack {
  my $self = shift;

  return 1;
}

=head2 get_message($msg_number)

Calls the superclass C<get_message> method.

Retrieves the contents of the news article pointed to by the given
C<$msg_number> into a B<Mail::Internet> object reference, caches the
header, marks the message as 'C<seen>', and returns the reference.

It returns C<undef> on failure.

=cut

sub get_message {
  my $self = shift;
  my $key = shift;

  return undef unless $self->SUPRT::get_message($key);

  my $article = $self->{NNTP_Connection}->article($key)
    or return undef;

  my $mref = new Mail::Internet($article,
				Modify => 0)
    or return undef;

  my $href = $mref->head;
  $self->cache_header($key, $href);
  $self->add_label($key, 'seen');

  return $mref;
}

=head2 get_message_file($msg_number)

Not currently implemented.  Returns C<undef>.

=cut

sub get_message_file {
  my $self = shift;
  my $key = shift;

  return undef;
}

=head2 get_header($msg_number)

If the particular header has never been retrieved then C<get_header>
retrieves the header for the given news article from the news server,
converts it into a C<Mail::Header> object and returns a reference to
the object.

If the header has already been retrieved in a prior call to
C<get_header>, then the cached entry is returned.

It returns C<undef> on failure.

=cut

sub get_header {
  my $self = shift;
  my $key = shift;

  return undef unless ($self->SUPER::get_header($key));

  return $self->{Messages}{$key}{Header} if ($self->{Messages}{$key}{Header});

  if (my $header = $self->{NNTP_Connection}->head($key)) {
    my $href = new Mail::Header($header, Modify => 0) or return undef;
    $self->cache_header($key, $href);
    return $href;
  }

  return undef;
}

=head2 append_message($mref)

Not currently implemented.  Returns C<0>.

=cut

sub append_message {
  my $self = shift;
  my $mref = shift;

  return 0;
}

=head2 update_message($msg_number, $mref)

Not currently implemented.  Returns C<0>.

=cut

sub update_message {
  my $self = shift;
  my $key = shift;
  my $mref = shift;

  return 0;
}

=head2 is_valid_folder_format($foldername)

Returns C<1> if the foldername starts with the strings 'C<news://>'
otherwise return 0;

=cut

sub is_valid_folder_format($foldername) {
  my $foldername = shift;

  return ($foldername =~ /^news:\/\/[^\/]+\/.+$/);
}

=head2 create($foldername)

Not currently implemented.  Returns C<0>.

=cut

sub create {
  my $self = shift;
  my $foldername = shift;

  return 0;
}
###############################################################################
sub _absorb_folder {
  my $self = shift;
  my $foldername = shift;

  my $qty_new_articles = 0;

  my @group = $self->{NNTP_Connection}->group($self->{NNTP_Newsgroup})
    or return undef;

  for my $msg ($group[1] .. $group[2]) {
    next if defined($self->{Messages}{$msg});
    $self->remember_message($msg);
    $self->get_header($msg);
    $qty_new_articles++;
  }

  return $qty_new_articles;
}

sub _extract_hostname {
  my $foldername = shift;

  $foldername =~ /^news:\/\/([^\/]+)\//;
  return $1;
}

sub _extract_newsgroup_name {
  my $foldername = shift;

  $foldername =~ /^news:\/\/[^\/]+\/(.+)$/;
  return $1;
}
###############################################################################

=head1 AUTHOR

Kevin Johnson E<lt>F<kjj@pobox.com>E<gt>

=head1 COPYRIGHT

Copyright (c) 1997 Kevin Johnson <kjj@pobox.com>.

All rights reserved. This program is free software; you can
redistribute it and/or modify it under the same terms as Perl itself.

=cut

1;
