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