xref: /openbsd/gnu/usr.bin/perl/cpan/libnet/lib/Net/POP3.pm (revision e0680481)
1# Net::POP3.pm
2#
3# Copyright (C) 1995-2004 Graham Barr.  All rights reserved.
4# Copyright (C) 2013-2016, 2020 Steve Hay.  All rights reserved.
5# This module is free software; you can redistribute it and/or modify it under
6# the same terms as Perl itself, i.e. under the terms of either the GNU General
7# Public License or the Artistic License, as specified in the F<LICENCE> file.
8
9package Net::POP3;
10
11use 5.008001;
12
13use strict;
14use warnings;
15
16use Carp;
17use IO::Socket;
18use Net::Cmd;
19use Net::Config;
20
21our $VERSION = "3.15";
22
23# Code for detecting if we can use SSL
24my $ssl_class = eval {
25  require IO::Socket::SSL;
26  # first version with default CA on most platforms
27  no warnings 'numeric';
28  IO::Socket::SSL->VERSION(2.007);
29} && 'IO::Socket::SSL';
30
31my $nossl_warn = !$ssl_class &&
32  'To use SSL please install IO::Socket::SSL with version>=2.007';
33
34# Code for detecting if we can use IPv6
35my $family_key = 'Domain';
36my $inet6_class = eval {
37  require IO::Socket::IP;
38  no warnings 'numeric';
39  IO::Socket::IP->VERSION(0.25) || die;
40  $family_key = 'Family';
41} && 'IO::Socket::IP' || eval {
42  require IO::Socket::INET6;
43  no warnings 'numeric';
44  IO::Socket::INET6->VERSION(2.62);
45} && 'IO::Socket::INET6';
46
47
48sub can_ssl   { $ssl_class };
49sub can_inet6 { $inet6_class };
50
51our @ISA = ('Net::Cmd', $inet6_class || 'IO::Socket::INET');
52
53sub new {
54  my $self = shift;
55  my $type = ref($self) || $self;
56  my ($host, %arg);
57  if (@_ % 2) {
58    $host = shift;
59    %arg  = @_;
60  }
61  else {
62    %arg  = @_;
63    $host = delete $arg{Host};
64  }
65  my $hosts = defined $host ? [$host] : $NetConfig{pop3_hosts};
66  my $obj;
67
68  if ($arg{SSL}) {
69    # SSL from start
70    die $nossl_warn if !$ssl_class;
71    $arg{Port} ||= 995;
72  }
73
74  $arg{Timeout} = 120 if ! defined $arg{Timeout};
75
76  foreach my $h (@{$hosts}) {
77    $obj = $type->SUPER::new(
78      PeerAddr => ($host = $h),
79      PeerPort => $arg{Port} || 'pop3(110)',
80      Proto => 'tcp',
81      $family_key => $arg{Domain} || $arg{Family},
82      LocalAddr => $arg{LocalAddr},
83      LocalPort => exists($arg{ResvPort}) ? $arg{ResvPort} : $arg{LocalPort},
84      Timeout => $arg{Timeout},
85      )
86      and last;
87  }
88
89  return
90    unless defined $obj;
91
92  ${*$obj}{'net_pop3_arg'} = \%arg;
93  ${*$obj}{'net_pop3_host'} = $host;
94  if ($arg{SSL}) {
95    Net::POP3::_SSL->start_SSL($obj,%arg) or return;
96  }
97
98  $obj->autoflush(1);
99  $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
100
101  unless ($obj->response() == CMD_OK) {
102    $obj->close();
103    return;
104  }
105
106  ${*$obj}{'net_pop3_banner'} = $obj->message;
107
108  $obj;
109}
110
111
112sub host {
113  my $me = shift;
114  ${*$me}{'net_pop3_host'};
115}
116
117##
118## We don't want people sending me their passwords when they report problems
119## now do we :-)
120##
121
122
123sub debug_text { $_[2] =~ /^(pass|rpop)/i ? "$1 ....\n" : $_[2]; }
124
125
126sub login {
127  @_ >= 1 && @_ <= 3 or croak 'usage: $pop3->login([$user[, $pass]])';
128  my ($me, $user, $pass) = @_;
129
130  if (@_ <= 2) {
131    ($user, $pass) = $me->_lookup_credentials($user);
132  }
133
134  $me->user($user)
135    and $me->pass($pass);
136}
137
138sub starttls {
139  my $self = shift;
140  $ssl_class or die $nossl_warn;
141  $self->_STLS or return;
142  Net::POP3::_SSL->start_SSL($self,
143    %{ ${*$self}{'net_pop3_arg'} }, # (ssl) args given in new
144    @_   # more (ssl) args
145  ) or return;
146  return 1;
147}
148
149sub apop {
150  @_ >= 1 && @_ <= 3 or croak 'usage: $pop3->apop([$user[, $pass]])';
151  my ($me, $user, $pass) = @_;
152  my $banner;
153  my $md;
154
155  if (eval { local $SIG{__DIE__}; require Digest::MD5 }) {
156    $md = Digest::MD5->new();
157  }
158  elsif (eval { local $SIG{__DIE__}; require MD5 }) {
159    $md = MD5->new();
160  }
161  else {
162    carp "You need to install Digest::MD5 or MD5 to use the APOP command";
163    return;
164  }
165
166  return
167    unless ($banner = (${*$me}{'net_pop3_banner'} =~ /(<.*>)/)[0]);
168
169  if (@_ <= 2) {
170    ($user, $pass) = $me->_lookup_credentials($user);
171  }
172
173  $md->add($banner, $pass);
174
175  return
176    unless ($me->_APOP($user, $md->hexdigest));
177
178  $me->_get_mailbox_count();
179}
180
181
182sub user {
183  @_ == 2 or croak 'usage: $pop3->user($user)';
184  $_[0]->_USER($_[1]) ? 1 : undef;
185}
186
187
188sub pass {
189  @_ == 2 or croak 'usage: $pop3->pass($pass)';
190
191  my ($me, $pass) = @_;
192
193  return
194    unless ($me->_PASS($pass));
195
196  $me->_get_mailbox_count();
197}
198
199
200sub reset {
201  @_ == 1 or croak 'usage: $obj->reset()';
202
203  my $me = shift;
204
205  return 0
206    unless ($me->_RSET);
207
208  if (defined ${*$me}{'net_pop3_mail'}) {
209    local $_;
210    foreach (@{${*$me}{'net_pop3_mail'}}) {
211      delete $_->{'net_pop3_deleted'};
212    }
213  }
214}
215
216
217sub last {
218  @_ == 1 or croak 'usage: $obj->last()';
219
220  return
221    unless $_[0]->_LAST && $_[0]->message =~ /(\d+)/;
222
223  return $1;
224}
225
226
227sub top {
228  @_ == 2 || @_ == 3 or croak 'usage: $pop3->top($msgnum[, $numlines])';
229  my $me = shift;
230
231  return
232    unless $me->_TOP($_[0], $_[1] || 0);
233
234  $me->read_until_dot;
235}
236
237
238sub popstat {
239  @_ == 1 or croak 'usage: $pop3->popstat()';
240  my $me = shift;
241
242  return ()
243    unless $me->_STAT && $me->message =~ /(\d+)\D+(\d+)/;
244
245  ($1 || 0, $2 || 0);
246}
247
248
249sub list {
250  @_ == 1 || @_ == 2 or croak 'usage: $pop3->list([$msgnum])';
251  my $me = shift;
252
253  return
254    unless $me->_LIST(@_);
255
256  if (@_) {
257    $me->message =~ /\d+\D+(\d+)/;
258    return $1 || undef;
259  }
260
261  my $info = $me->read_until_dot
262    or return;
263
264  my %hash = map { (/(\d+)\D+(\d+)/) } @$info;
265
266  return \%hash;
267}
268
269
270sub get {
271  @_ == 2 or @_ == 3 or croak 'usage: $pop3->get($msgnum[, $fh])';
272  my $me = shift;
273
274  return
275    unless $me->_RETR(shift);
276
277  $me->read_until_dot(@_);
278}
279
280
281sub getfh {
282  @_ == 2 or croak 'usage: $pop3->getfh($msgnum)';
283  my $me = shift;
284
285  return unless $me->_RETR(shift);
286  return $me->tied_fh;
287}
288
289
290sub delete {
291  @_ == 2 or croak 'usage: $pop3->delete($msgnum)';
292  my $me = shift;
293  return 0 unless $me->_DELE(@_);
294  ${*$me}{'net_pop3_deleted'} = 1;
295}
296
297
298sub uidl {
299  @_ == 1 || @_ == 2 or croak 'usage: $pop3->uidl([$msgnum])';
300  my $me = shift;
301  my $uidl;
302
303  $me->_UIDL(@_)
304    or return;
305  if (@_) {
306    $uidl = ($me->message =~ /\d+\s+([\041-\176]+)/)[0];
307  }
308  else {
309    my $ref = $me->read_until_dot
310      or return;
311    $uidl = {};
312    foreach my $ln (@$ref) {
313      my ($msg, $uid) = $ln =~ /^\s*(\d+)\s+([\041-\176]+)/;
314      $uidl->{$msg} = $uid;
315    }
316  }
317  return $uidl;
318}
319
320
321sub ping {
322  @_ == 2 or croak 'usage: $pop3->ping($user)';
323  my $me = shift;
324
325  return () unless $me->_PING(@_) && $me->message =~ /(\d+)\D+(\d+)/;
326
327  ($1 || 0, $2 || 0);
328}
329
330
331sub _lookup_credentials {
332  my ($me, $user) = @_;
333
334  require Net::Netrc;
335
336       $user ||= eval { local $SIG{__DIE__}; (getpwuid($>))[0] }
337    || $ENV{NAME}
338    || $ENV{USER}
339    || $ENV{LOGNAME};
340
341  my $m = Net::Netrc->lookup(${*$me}{'net_pop3_host'}, $user);
342  $m ||= Net::Netrc->lookup(${*$me}{'net_pop3_host'});
343
344  my $pass = $m
345    ? $m->password || ""
346    : "";
347
348  ($user, $pass);
349}
350
351
352sub _get_mailbox_count {
353  my ($me) = @_;
354  my $ret = ${*$me}{'net_pop3_count'} =
355    ($me->message =~ /(\d+)\s+message/io) ? $1 : ($me->popstat)[0];
356
357  $ret ? $ret : "0E0";
358}
359
360
361sub _STAT { shift->command('STAT'       )->response() == CMD_OK }
362sub _LIST { shift->command('LIST',    @_)->response() == CMD_OK }
363sub _RETR { shift->command('RETR', $_[0])->response() == CMD_OK }
364sub _DELE { shift->command('DELE', $_[0])->response() == CMD_OK }
365sub _NOOP { shift->command('NOOP'       )->response() == CMD_OK }
366sub _RSET { shift->command('RSET'       )->response() == CMD_OK }
367sub _QUIT { shift->command('QUIT'       )->response() == CMD_OK }
368sub _TOP  { shift->command( 'TOP',    @_)->response() == CMD_OK }
369sub _UIDL { shift->command('UIDL',    @_)->response() == CMD_OK }
370sub _USER { shift->command('USER', $_[0])->response() == CMD_OK }
371sub _PASS { shift->command('PASS', $_[0])->response() == CMD_OK }
372sub _APOP { shift->command('APOP',    @_)->response() == CMD_OK }
373sub _PING { shift->command('PING', $_[0])->response() == CMD_OK }
374sub _RPOP { shift->command('RPOP', $_[0])->response() == CMD_OK }
375sub _LAST { shift->command('LAST'       )->response() == CMD_OK }
376sub _CAPA { shift->command('CAPA'       )->response() == CMD_OK }
377sub _STLS { shift->command("STLS",     )->response() == CMD_OK }
378
379
380sub quit {
381  my $me = shift;
382
383  $me->_QUIT;
384  $me->close;
385}
386
387
388sub DESTROY {
389  my $me = shift;
390
391  if (defined fileno($me) and ${*$me}{'net_pop3_deleted'}) {
392    $me->reset;
393    $me->quit;
394  }
395}
396
397##
398## POP3 has weird responses, so we emulate them to look the same :-)
399##
400
401
402sub response {
403  my $cmd  = shift;
404  my $str  = $cmd->getline() or return;
405  my $code = "500";
406
407  $cmd->debug_print(0, $str)
408    if ($cmd->debug);
409
410  if ($str =~ s/^\+OK\s*//io) {
411    $code = "200";
412  }
413  elsif ($str =~ s/^\+\s*//io) {
414    $code = "300";
415  }
416  else {
417    $str =~ s/^-ERR\s*//io;
418  }
419
420  ${*$cmd}{'net_cmd_resp'} = [$str];
421  ${*$cmd}{'net_cmd_code'} = $code;
422
423  substr($code, 0, 1);
424}
425
426
427sub capa {
428  my $this = shift;
429  my ($capa, %capabilities);
430
431  # Fake a capability here
432  $capabilities{APOP} = '' if ($this->banner() =~ /<.*>/);
433
434  if ($this->_CAPA()) {
435    $capabilities{CAPA} = 1;
436    $capa = $this->read_until_dot();
437    %capabilities = (%capabilities, map {/^\s*(\S+)\s*(.*)/} @$capa);
438  }
439  else {
440
441    # Check AUTH for SASL capabilities
442    if ($this->command('AUTH')->response() == CMD_OK) {
443      my $mechanism = $this->read_until_dot();
444      $capabilities{SASL} = join " ", map {m/([A-Z0-9_-]+)/} @{$mechanism};
445    }
446  }
447
448  return ${*$this}{'net_pop3e_capabilities'} = \%capabilities;
449}
450
451
452sub capabilities {
453  my $this = shift;
454
455  ${*$this}{'net_pop3e_capabilities'} || $this->capa;
456}
457
458
459sub auth {
460  my ($self, $username, $password) = @_;
461
462  eval {
463    require MIME::Base64;
464    require Authen::SASL;
465  } or $self->set_status(500, ["Need MIME::Base64 and Authen::SASL todo auth"]), return 0;
466
467  my $capa       = $self->capa;
468  my $mechanisms = $capa->{SASL} || 'CRAM-MD5';
469
470  my $sasl;
471
472  if (ref($username) and UNIVERSAL::isa($username, 'Authen::SASL')) {
473    $sasl = $username;
474    my $user_mech = $sasl->mechanism || '';
475    my @user_mech = split(/\s+/, $user_mech);
476    my %user_mech;
477    @user_mech{@user_mech} = ();
478
479    my @server_mech = split(/\s+/, $mechanisms);
480    my @mech = @user_mech
481      ? grep { exists $user_mech{$_} } @server_mech
482      : @server_mech;
483    unless (@mech) {
484      $self->set_status(
485        500,
486        [ 'Client SASL mechanisms (',
487          join(', ', @user_mech),
488          ') do not match the SASL mechnism the server announces (',
489          join(', ', @server_mech), ')',
490        ]
491      );
492      return 0;
493    }
494
495    $sasl->mechanism(join(" ", @mech));
496  }
497  else {
498    die "auth(username, password)" if not length $username;
499    $sasl = Authen::SASL->new(
500      mechanism => $mechanisms,
501      callback  => {
502        user     => $username,
503        pass     => $password,
504        authname => $username,
505      }
506    );
507  }
508
509  # We should probably allow the user to pass the host, but I don't
510  # currently know and SASL mechanisms that are used by smtp that need it
511  my ($hostname) = split /:/, ${*$self}{'net_pop3_host'};
512  my $client = eval { $sasl->client_new('pop', $hostname, 0) };
513
514  unless ($client) {
515    my $mech = $sasl->mechanism;
516    $self->set_status(
517      500,
518      [ " Authen::SASL failure: $@",
519        '(please check if your local Authen::SASL installation',
520        "supports mechanism '$mech'"
521      ]
522    );
523    return 0;
524  }
525
526  my ($token) = $client->client_start
527    or do {
528    my $mech = $client->mechanism;
529    $self->set_status(
530      500,
531      [ ' Authen::SASL failure:  $client->client_start ',
532        "mechanism '$mech' hostname #$hostname#",
533        $client->error
534      ]
535    );
536    return 0;
537    };
538
539  # We don't support sasl mechanisms that encrypt the socket traffic.
540  # todo that we would really need to change the ISA hierarchy
541  # so we don't inherit from IO::Socket, but instead hold it in an attribute
542
543  my @cmd = ("AUTH", $client->mechanism);
544  my $code;
545
546  push @cmd, MIME::Base64::encode_base64($token, '')
547    if defined $token and length $token;
548
549  while (($code = $self->command(@cmd)->response()) == CMD_MORE) {
550
551    my ($token) = $client->client_step(MIME::Base64::decode_base64(($self->message)[0])) or do {
552      $self->set_status(
553        500,
554        [ ' Authen::SASL failure:  $client->client_step ',
555          "mechanism '", $client->mechanism, " hostname #$hostname#, ",
556          $client->error
557        ]
558      );
559      return 0;
560    };
561
562    @cmd = (MIME::Base64::encode_base64(defined $token ? $token : '', ''));
563  }
564
565  $code == CMD_OK;
566}
567
568
569sub banner {
570  my $this = shift;
571
572  return ${*$this}{'net_pop3_banner'};
573}
574
575{
576  package Net::POP3::_SSL;
577  our @ISA = ( $ssl_class ? ($ssl_class):(), 'Net::POP3' );
578  sub starttls { die "POP3 connection is already in SSL mode" }
579  sub start_SSL {
580    my ($class,$pop3,%arg) = @_;
581    delete @arg{ grep { !m{^SSL_} } keys %arg };
582    ( $arg{SSL_verifycn_name} ||= $pop3->host )
583        =~s{(?<!:):[\w()]+$}{}; # strip port
584    $arg{SSL_hostname} = $arg{SSL_verifycn_name}
585        if ! defined $arg{SSL_hostname} && $class->can_client_sni;
586    $arg{SSL_verifycn_scheme} ||= 'pop3';
587    my $ok = $class->SUPER::start_SSL($pop3,%arg);
588    $@ = $ssl_class->errstr if !$ok;
589    return $ok;
590  }
591}
592
593
594
5951;
596
597__END__
598
599=head1 NAME
600
601Net::POP3 - Post Office Protocol 3 Client class (RFC1939)
602
603=head1 SYNOPSIS
604
605    use Net::POP3;
606
607    # Constructors
608    $pop = Net::POP3->new('pop3host');
609    $pop = Net::POP3->new('pop3host', Timeout => 60);
610    $pop = Net::POP3->new('pop3host', SSL => 1, Timeout => 60);
611
612    if ($pop->login($username, $password) > 0) {
613      my $msgnums = $pop->list; # hashref of msgnum => size
614      foreach my $msgnum (keys %$msgnums) {
615        my $msg = $pop->get($msgnum);
616        print @$msg;
617        $pop->delete($msgnum);
618      }
619    }
620
621    $pop->quit;
622
623=head1 DESCRIPTION
624
625This module implements a client interface to the POP3 protocol, enabling
626a perl5 application to talk to POP3 servers. This documentation assumes
627that you are familiar with the POP3 protocol described in RFC1939.
628With L<IO::Socket::SSL> installed it also provides support for implicit and
629explicit TLS encryption, i.e. POP3S or POP3+STARTTLS.
630
631A new Net::POP3 object must be created with the I<new> method. Once
632this has been done, all POP3 commands are accessed via method calls
633on the object.
634
635The Net::POP3 class is a subclass of Net::Cmd and (depending on avaibility) of
636IO::Socket::IP, IO::Socket::INET6 or IO::Socket::INET.
637
638=head2 Class Methods
639
640=over 4
641
642=item C<new([$host][, %options])>
643
644This is the constructor for a new Net::POP3 object. C<$host> is the
645name of the remote host to which an POP3 connection is required.
646
647C<$host> is optional. If C<$host> is not given then it may instead be
648passed as the C<Host> option described below. If neither is given then
649the C<POP3_Hosts> specified in C<Net::Config> will be used.
650
651C<%options> are passed in a hash like fashion, using key and value pairs.
652Possible options are:
653
654B<Host> - POP3 host to connect to. It may be a single scalar, as defined for
655the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
656an array with hosts to try in turn. The L</host> method will return the value
657which was used to connect to the host.
658
659B<Port> - port to connect to.
660Default - 110 for plain POP3 and 995 for POP3s (direct SSL).
661
662B<SSL> - If the connection should be done from start with SSL, contrary to later
663upgrade with C<starttls>.
664You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will
665usually use the right arguments already.
666
667B<LocalAddr> and B<LocalPort> - These parameters are passed directly
668to IO::Socket to allow binding the socket to a specific local address and port.
669For compatibility with older versions B<ResvPort> can be used instead of
670B<LocalPort>.
671
672B<Domain> - This parameter is passed directly to IO::Socket and makes it
673possible to enforce IPv4 connections even if L<IO::Socket::IP> is used as super
674class. Alternatively B<Family> can be used.
675
676B<Timeout> - Maximum time, in seconds, to wait for a response from the
677POP3 server (default: 120)
678
679B<Debug> - Enable debugging information
680
681=back
682
683=head2 Object Methods
684
685Unless otherwise stated all methods return either a I<true> or I<false>
686value, with I<true> meaning that the operation was a success. When a method
687states that it returns a value, failure will be returned as I<undef> or an
688empty list.
689
690C<Net::POP3> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
691be used to send commands to the remote POP3 server in addition to the methods
692documented here.
693
694=over 4
695
696=item C<host()>
697
698Returns the value used by the constructor, and passed to IO::Socket::INET,
699to connect to the host.
700
701=item C<auth($username, $password)>
702
703Attempt SASL authentication.
704
705=item C<user($user)>
706
707Send the USER command.
708
709=item C<pass($pass)>
710
711Send the PASS command. Returns the number of messages in the mailbox.
712
713=item C<login([$user[, $pass]])>
714
715Send both the USER and PASS commands. If C<$pass> is not given the
716C<Net::POP3> uses C<Net::Netrc> to lookup the password using the host
717and username. If the username is not specified then the current user name
718will be used.
719
720Returns the number of messages in the mailbox. However if there are no
721messages on the server the string C<"0E0"> will be returned. This is
722will give a true value in a boolean context, but zero in a numeric context.
723
724If there was an error authenticating the user then I<undef> will be returned.
725
726=item C<starttls(%sslargs)>
727
728Upgrade existing plain connection to SSL.
729You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will
730usually use the right arguments already.
731
732=item C<apop([$user[, $pass]])>
733
734Authenticate with the server identifying as C<$user> with password C<$pass>.
735Similar to L</login>, but the password is not sent in clear text.
736
737To use this method you must have the Digest::MD5 or the MD5 module installed,
738otherwise this method will return I<undef>.
739
740=item C<banner()>
741
742Return the sever's connection banner
743
744=item C<capa()>
745
746Return a reference to a hash of the capabilities of the server.  APOP
747is added as a pseudo capability.  Note that I've been unable to
748find a list of the standard capability values, and some appear to
749be multi-word and some are not.  We make an attempt at intelligently
750parsing them, but it may not be correct.
751
752=item C<capabilities()>
753
754Just like capa, but only uses a cache from the last time we asked
755the server, so as to avoid asking more than once.
756
757=item C<top($msgnum[, $numlines])>
758
759Get the header and the first C<$numlines> of the body for the message
760C<$msgnum>. Returns a reference to an array which contains the lines of text
761read from the server.
762
763=item C<list([$msgnum])>
764
765If called with an argument the C<list> returns the size of the message
766in octets.
767
768If called without arguments a reference to a hash is returned. The
769keys will be the C<$msgnum>'s of all undeleted messages and the values will
770be their size in octets.
771
772=item C<get($msgnum[, $fh])>
773
774Get the message C<$msgnum> from the remote mailbox. If C<$fh> is not given
775then get returns a reference to an array which contains the lines of
776text read from the server. If C<$fh> is given then the lines returned
777from the server are printed to the filehandle C<$fh>.
778
779=item C<getfh($msgnum)>
780
781As per get(), but returns a tied filehandle.  Reading from this
782filehandle returns the requested message.  The filehandle will return
783EOF at the end of the message and should not be reused.
784
785=item C<last()>
786
787Returns the highest C<$msgnum> of all the messages accessed.
788
789=item C<popstat()>
790
791Returns a list of two elements. These are the number of undeleted
792elements and the size of the mbox in octets.
793
794=item C<ping($user)>
795
796Returns a list of two elements. These are the number of new messages
797and the total number of messages for C<$user>.
798
799=item C<uidl([$msgnum])>
800
801Returns a unique identifier for C<$msgnum> if given. If C<$msgnum> is not
802given C<uidl> returns a reference to a hash where the keys are the
803message numbers and the values are the unique identifiers.
804
805=item C<delete($msgnum)>
806
807Mark message C<$msgnum> to be deleted from the remote mailbox. All messages
808that are marked to be deleted will be removed from the remote mailbox
809when the server connection closed.
810
811=item C<reset()>
812
813Reset the status of the remote POP3 server. This includes resetting the
814status of all messages to not be deleted.
815
816=item C<quit()>
817
818Quit and close the connection to the remote POP3 server. Any messages marked
819as deleted will be deleted from the remote mailbox.
820
821=item C<can_inet6()>
822
823Returns whether we can use IPv6.
824
825=item C<can_ssl()>
826
827Returns whether we can use SSL.
828
829=back
830
831=head2 Notes
832
833If a C<Net::POP3> object goes out of scope before C<quit> method is called
834then the C<reset> method will called before the connection is closed. This
835means that any messages marked to be deleted will not be.
836
837=head1 EXPORTS
838
839I<None>.
840
841=head1 KNOWN BUGS
842
843See L<https://rt.cpan.org/Dist/Display.html?Status=Active&Queue=libnet>.
844
845=head1 SEE ALSO
846
847L<Net::Netrc>,
848L<Net::Cmd>,
849L<IO::Socket::SSL>.
850
851=head1 AUTHOR
852
853Graham Barr E<lt>L<gbarr@pobox.com|mailto:gbarr@pobox.com>E<gt>.
854
855Steve Hay E<lt>L<shay@cpan.org|mailto:shay@cpan.org>E<gt> is now maintaining
856libnet as of version 1.22_02.
857
858=head1 COPYRIGHT
859
860Copyright (C) 1995-2004 Graham Barr.  All rights reserved.
861
862Copyright (C) 2013-2016, 2020 Steve Hay.  All rights reserved.
863
864=head1 LICENCE
865
866This module is free software; you can redistribute it and/or modify it under the
867same terms as Perl itself, i.e. under the terms of either the GNU General Public
868License or the Artistic License, as specified in the F<LICENCE> file.
869
870=head1 VERSION
871
872Version 3.15
873
874=head1 DATE
875
87620 March 2023
877
878=head1 HISTORY
879
880See the F<Changes> file.
881
882=cut
883