PK       ! -      PayslipSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class PayslipSend extends Mailable
{
    use Queueable, SerializesModels;

    public $payslip;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($payslip)
    {

        $this->payslip = $payslip;

    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {

        return $this->view('email.payslip_send')->with('payslip', $this->payslip)->subject('Your ' . date('M Y',strtotime($this->payslip->salary_month)). ' payslip is generated.');
    }
}
PK       ! O!      LeaveActionSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class LeaveActionSend extends Mailable
{
    use Queueable, SerializesModels;

    public $leave;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($leave)
    {
        $this->leave = $leave;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.leave_action_send')->with('leave', $this->leave)->subject('Ragarding to approval letter to approve or reject a vacation or leave.');
    }
}
PK       ! \Ϫ_  _    UserCreate.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class UserCreate extends Mailable
{
    use Queueable, SerializesModels;
    public $user;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function     __construct($user)
    {
        $this->user = $user;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {

        if(\Auth::user()->type == 'super admin')
        {
            return $this->markdown('email.user_create')->subject('Login details - '.env('APP_NAME'))->with('user', $this->user);
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->markdown('email.user_create')->subject('Login details - ' . (isset(Utility::settings()['company_name']) && !empty(Utility::settings()['company_name']))?Utility::settings()['company_name']:env('APP_NAME'))->with('user', $this->user);

        }


    }
}
PK       ! CE  E    AppraisalCreatedMail.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\AppraisalReview;

class AppraisalCreatedMail extends Mailable
{
    use Queueable, SerializesModels;

    public $appraisalReview;

    /**
     * Create a new message instance.
     */
    public function __construct(AppraisalReview $appraisalReview)
    {
        $this->appraisalReview = $appraisalReview;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'New Appraisal Review Created for You!',
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'email.appraisal_created',
            with: [
                'employeeName' => optional($this->appraisalReview->employee)->name,
                'reviewLink' => route('appraisal_reviews.edit', $this->appraisalReview),
            ]
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}PK       ! o8      TestMail.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class TestMail extends Mailable
{
    use Queueable, SerializesModels;


    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {

    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('email.test_mail')->subject('Nice Subject - '.env('APP_NAME'));
    }
}
PK       ! ?      TerminationSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class TerminationSend extends Mailable
{
    use Queueable, SerializesModels;

    public $termination;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($termination)
    {
        $this->termination = $termination;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.termination_send')->with('termination', $this->termination)->subject('Ragarding to termination letter.');
    }
}
PK       !  e  e    CommonEmailTemplate.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class CommonEmailTemplate extends Mailable
{
    use Queueable, SerializesModels;
    public $template;
    public $settings;
    public $mailTo;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($template, $settings, $mailTo)
    {
        $this->template = $template;
        $this->settings = $settings;
        $this->mailTo = $mailTo;
    }
    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->from($this->settings['mail_from_address'], $this->settings['mail_from_name'])->markdown('email.common_email_template')->subject($this->template->subject)->with('content', $this->template->content);
    }
}PK       ! 
|U      TransferSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class TransferSend extends Mailable
{
    use Queueable, SerializesModels;

    public $transfer;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($transfer)
    {
        $this->transfer = $transfer;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.transfer_send')->with('transfer', $this->transfer)->subject('Ragarding to  transfer letter to be issued to an employee from one location to another.');
    }
}
PK       ! s      AppraisalReviewReminderMail.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\Employee;

class AppraisalReviewReminderMail extends Mailable
{
    use Queueable, SerializesModels;

    public $employee;

    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Reminder: It\'s Time for Your Appraisal Review!',
        );
    }

    public function content(): Content
    {
        return new Content(
            view: 'email.appraisal_review_reminder',
            with: [
                'employeeName' => $this->employee->name,
                'reviewUrl' => route('appraisal_reviews.index'),
            ]
        );
    }

    public function attachments(): array
    {
        return [];
    }
}PK       ! ;!
      PeerAddedMail.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\AppraisalReview;
use App\Models\Employee;

class PeerAddedMail extends Mailable
{
    use Queueable, SerializesModels;

    public $appraisalReview;
    public $peerEmployee;

    /**
     * Create a new message instance.
     */
    public function __construct(AppraisalReview $appraisalReview, Employee $peerEmployee)
    {
        $this->appraisalReview = $appraisalReview;
        $this->peerEmployee = $peerEmployee;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'You Have Been Added as a Peer Reviewer',
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'email.peer_added',
            with: [
                'peerName' => $this->peerEmployee->name,
                'revieweeName' => optional($this->appraisalReview->employee)->name,
                'reviewLink' => route('appraisal_reviews.edit', $this->appraisalReview),
            ]
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}PK       ! .C      AwardSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class AwardSend extends Mailable
{
    use Queueable, SerializesModels;

    public $award;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($award)
    {
        $this->award = $award;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.award_send')->with('award', $this->award)->subject('Ragarding to  award letter to recognize an employee.');
    }
}
PK       ! S{  {    TicketSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class TicketSend extends Mailable
{
    use Queueable, SerializesModels;

    public $ticket;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($ticket)
    {
        $this->ticket = $ticket;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.ticket_send')->with('ticket', $this->ticket)->subject('Ragarding to employee ticket generated.');
    }
}
PK       ! `      PromotionSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class PromotionSend extends Mailable
{
    use Queueable, SerializesModels;

    public $promotion;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($promotion)
    {
        $this->promotion = $promotion;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.promotion_send')->with('promotion', $this->promotion)->subject('Ragarding to job promotion congratulation letter.');
    }
}
PK       ! \3]  ]    TripSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class TripSend extends Mailable
{
    use Queueable, SerializesModels;

    public $trip;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($trip)
    {
        $this->trip = $trip;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.trip_send')->with('trip', $this->trip)->subject('Ragarding to trip letter.');
    }
}
PK       ! 0L      ComplaintsSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class ComplaintsSend extends Mailable
{
    use Queueable, SerializesModels;

    public $complaints;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($complaints)
    {
        $this->complaints = $complaints;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.complaints_send')->with('complaints', $this->complaints)->subject('Ragarding to job complaints letter.');
    }
}
PK       ! {
  
    GoalReminderMail.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\Employee;

class GoalReminderMail extends Mailable
{
    use Queueable, SerializesModels;

    public $employee;

    /**
     * Create a new message instance.
     */
    public function __construct(Employee $employee)
    {
        $this->employee = $employee;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Reminder: It\'s Time to Create Your Goals!',
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'email.goal_reminder',
            with: [
                'employeeName' => $this->employee->name,
                'goalUrl' => route('simple-goals.index'),
            ]
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}PK       ! @      ResignationSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class ResignationSend extends Mailable
{
    use Queueable, SerializesModels;

    public $resignation;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($resignation)
    {
        $this->resignation = $resignation;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.resignation_send')->with('resignation', $this->resignation)->subject('Ragarding to resignation letter.');
    }
}
PK       ! "HSx  x    WarningSend.phpnu [        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class WarningSend extends Mailable
{
    use Queueable, SerializesModels;

    public $warning;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($warning)
    {
        $this->warning = $warning;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.warning_send')->with('warning', $this->warning)->subject('Ragarding to warning letter.');
    }
}
PK       !       Cap.podnu [        =encoding utf8

=head1 NAME

Mail::Cap - understand mailcap files

=head1 SYNOPSIS

 my $mc   = Mail::Cap->new;

 my $desc = $mc->description('image/gif');
 print "GIF desc: $desc\n";

 my $cmd  = $mc->viewCmd('text/plain; charset=iso-8859-1', 'file.txt');

=head1 DESCRIPTION

Parse mailcap files as specified in "RFC 1524 --A User Agent
Configuration Mechanism For Multimedia Mail Format Information>.  In
the description below C<$type> refers to the MIME type as specified in
the C<Content-Type> header of mail or HTTP messages.  Examples of
types are:

  image/gif
  text/html
  text/plain; charset=iso-8859-1

You could also take a look at the File::MimeInfo distribution, which
are accessing tables which are used by many applications on a system,
and therefore have succeeded the mail-cap specifications on modern
(UNIX) systems.

=head1 METHODS

=head2 Constructors

=over 4

=item Mail::Cap-E<gt>B<new>(%options)

Create and initialize a new Mail::Cap object.  If you give it an
argument it will try to parse the specified file.  Without any
arguments it will search for the mailcap file using the standard
mailcap path, or the MAILCAPS environment variable if it is defined.

 -Option  --Default
  filename  undef
  take      'FIRST'

=over 2

=item filename => FILENAME

Add the specified file to the list to standard locations.  This file
is tried first.

=item take => 'ALL'|'FIRST'

Include all mailcap files you can find.  By default, only the first
file is parsed, however the RFC tells us to include ALL.  To maintain
backwards compatibility, the default only takes the FIRST.

=back

example: 

  $mcap = new Mail::Cap;
  $mcap = new Mail::Cap "/mydir/mailcap";
  $mcap = new Mail::Cap filename => "/mydir/mailcap";
  $mcap = new Mail::Cap take => 'ALL';
  $mcap = Mail::Cap->new(take => 'ALL');

=back

=head2 Run commands

These methods invoke a suitable program presenting or manipulating the
media object in the specified file.  They all return C<1> if a command
was found, and C<0> otherwise.  You might test C<$?> for the outcome
of the command.

=over 4

=item $obj-E<gt>B<compose>($type, $file)

=item $obj-E<gt>B<edit>($type, $file)

=item $obj-E<gt>B<print>($type, $file)

=item $obj-E<gt>B<view>($type, $file)

=back

=head2 Command creator

These methods return a string that is suitable for feeding to system()
in order to invoke a suitable program presenting or manipulating the
media object in the specified file.  It will return C<undef> if no
suitable specification exists.

=over 4

=item $obj-E<gt>B<composeCmd>($type, $file)

=item $obj-E<gt>B<editCmd>($type, $file)

=item $obj-E<gt>B<printCmd>($type, $file)

=item $obj-E<gt>B<viewCmd>($type, $file)

=back

=head2 Look-up definitions

Methods return the corresponding mailcap field for the type.

=over 4

=item $obj-E<gt>B<description>($type)

=item $obj-E<gt>B<field>($type, $field)

Returns the specified field for the type.  Returns undef if no
specification exists.

=item $obj-E<gt>B<nametemplate>($type)

=item $obj-E<gt>B<textualnewlines>($type)

=item $obj-E<gt>B<x11_bitmap>($type)

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! /Z    	  Filter.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Filter;
use vars '$VERSION';
$VERSION = '2.21';


use strict;
use Carp;


sub new(@)
{   my $class = shift;
    bless { filters => [ @_ ] }, $class;
}

#------------

sub add(@)
{   my $self = shift;
    push @{$self->{filters}}, @_;
}

#------------

sub _filter($)
{   my ($self, $mail) = @_;

    foreach my $sub ( @{$self->{filters}} )
    {   my $mail
          = ref $sub eq 'CODE' ? $sub->($self,$mail)
	  : !ref $sub          ? $self->$sub($mail)
	  : carp "Cannot call filter '$sub', ignored";

	ref $mail or last;
    }

    $mail;
}

sub filter
{   my ($self, $obj) = @_;
    if($obj->isa('Mail::Folder'))
    {   $self->{folder} = $obj;
	foreach my $m ($obj->message_list)
	{   my $mail = $obj->get_message($m) or next;
	    $self->{msgnum} = $m;
	    $self->_filter($mail);
	}
	delete $self->{folder};
	delete $self->{msgnum};
    }
    elsif($obj->isa('Mail::Internet'))
    {   return $self->filter($obj);
    }
    else
    {   carp "Cannot process '$obj'";
	return undef;
    }
}


sub folder() {shift->{folder}}


sub msgnum() {shift->{msgnum}}

1;
PK       ! Ni8  8    Field/Generic.podnu [        =encoding utf8

=head1 NAME

Mail::Field::Generic - implementation for inspecific fields

=head1 INHERITANCE

 Mail::Field::Generic
   is a Mail::Field

=head1 SYNOPSIS

 use Mail::Field;
 my $field = Mail::Field->new('Subject', 'some subject text');
 my $field = Mail::Field->new(subject => 'some subject text');

=head1 DESCRIPTION

A generic implementation for header fields without own
implementation. This is fine for fields like C<Subject>, C<X-Mailer>,
etc., where the field holds only a string of no particular
importance/format.

Extends L<"DESCRIPTION" in Mail::Field|Mail::Field/"DESCRIPTION">.
 
=head1 METHODS

Extends L<"METHODS" in Mail::Field|Mail::Field/"METHODS">.
 
=head2 Constructors

Extends L<"Constructors" in Mail::Field|Mail::Field/"Constructors">.
 
=over 4

=item Mail::Field::Generic-E<gt>B<combine>($fields)

Inherited, see L<Mail::Field/"Constructors">

=item Mail::Field::Generic-E<gt>B<extract>( $tag, $head [, $index ] )

Inherited, see L<Mail::Field/"Constructors">

=item Mail::Field::Generic-E<gt>B<new>( $tag [, STRING | %options] )

Inherited, see L<Mail::Field/"Constructors">

=back

=head2 "Fake" constructors

Extends L<""Fake" constructors" in Mail::Field|Mail::Field/""Fake" constructors">.
 
=over 4

=item $obj-E<gt>B<create>(%options)

 -Option--Default
  Text    ''

=over 2

=item Text => STRING

=back

=item $obj-E<gt>B<parse>( [STRING] )

Set the new text, which is empty when no STRING is provided.

=back

=head2 Accessors

Extends L<"Accessors" in Mail::Field|Mail::Field/"Accessors">.
 
=over 4

=item $obj-E<gt>B<set>(%options)

Inherited, see L<Mail::Field/"Accessors">

=item $obj-E<gt>B<stringify>()

Inherited, see L<Mail::Field/"Accessors">

=item $obj-E<gt>B<tag>()

=item Mail::Field::Generic-E<gt>B<tag>()

Inherited, see L<Mail::Field/"Accessors">

=back

=head2 Smart accessors

Extends L<"Smart accessors" in Mail::Field|Mail::Field/"Smart accessors">.
 
=over 4

=item $obj-E<gt>B<text>( [STRING] )

Inherited, see L<Mail::Field/"Smart accessors">

=back

=head1 DETAILS

Extends L<"DETAILS" in Mail::Field|Mail::Field/"DETAILS">.
 
=head1 DIAGNOSTICS

=over 4

=item Error: Undefined subroutine <method> called

Mail::Field objects use autoloading to compile new functionality.
Apparently, the method called is not implemented for the specific
class of the field object.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! F8  8    Field/AddrList.podnu [        =encoding utf8

=head1 NAME

Mail::Field::AddrList - object representation of e-mail address lists

=head1 INHERITANCE

 Mail::Field::AddrList
   is a Mail::Field

=head1 SYNOPSIS

  use Mail::Field::AddrList;

  $to   = Mail::Field->new('To');
  $from = Mail::Field->new('From', 'poe@daimi.aau.dk (Peter Orbaek)');
  
  $from->create('foo@bar.com' => 'Mr. Foo', poe => 'Peter');
  $from->parse('foo@bar.com (Mr Foo), Peter Orbaek <poe>');

  # make a RFC822 header string
  print $from->stringify(),"\n";

  # extract e-mail addresses and names
  @addresses = $from->addresses(); # strings
  @names     = $from->names();     # strings
  @addr      = $from->addr_list(); # Mail::Address objects (v2.00)

  # adjoin a new address to the list
  $from->set_address('foo@bar.com', 'Mr. Foo');

=head1 DESCRIPTION

Defines parsing and formatting of address field, for the following
fields: C<To>, C<From>, C<Cc>, C<Reply-To>, and C<Sender>.

All the normally used features of the address field specification of
RFC2822 are implemented, but some complex (and therefore hardly ever used)
constructs will not be understood.  Use Mail::Message::Field::Full
in MailBox if you need full RFC compliance.

Extends L<"DESCRIPTION" in Mail::Field|Mail::Field/"DESCRIPTION">.
 
=head1 METHODS

Extends L<"METHODS" in Mail::Field|Mail::Field/"METHODS">.
 
=head2 Constructors

Extends L<"Constructors" in Mail::Field|Mail::Field/"Constructors">.
 
=over 4

=item Mail::Field::AddrList-E<gt>B<combine>($fields)

Inherited, see L<Mail::Field/"Constructors">

=item Mail::Field::AddrList-E<gt>B<extract>( $tag, $head [, $index ] )

Inherited, see L<Mail::Field/"Constructors">

=item Mail::Field::AddrList-E<gt>B<new>( $tag [, STRING | %options] )

Inherited, see L<Mail::Field/"Constructors">

=back

=head2 "Fake" constructors

Extends L<""Fake" constructors" in Mail::Field|Mail::Field/""Fake" constructors">.
 
=over 4

=item $obj-E<gt>B<create>(%options)

Inherited, see L<Mail::Field/""Fake" constructors">

=item $obj-E<gt>B<parse>()

Inherited, see L<Mail::Field/""Fake" constructors">

=back

=head2 Accessors

Extends L<"Accessors" in Mail::Field|Mail::Field/"Accessors">.
 
=over 4

=item $obj-E<gt>B<set>(%options)

Inherited, see L<Mail::Field/"Accessors">

=item $obj-E<gt>B<stringify>()

Inherited, see L<Mail::Field/"Accessors">

=item $obj-E<gt>B<tag>()

=item Mail::Field::AddrList-E<gt>B<tag>()

Inherited, see L<Mail::Field/"Accessors">

=back

=head2 Smart accessors

Extends L<"Smart accessors" in Mail::Field|Mail::Field/"Smart accessors">.
 
=over 4

=item $obj-E<gt>B<addr_list>()

Returns the collected L<Mail::Address|Mail::Address> objects.

=item $obj-E<gt>B<addresses>()

Returns a list if email addresses, found in the field content.

=item $obj-E<gt>B<names>()

Returns a list of nicely formatted named, for each of the addresses
found in the content.

=item $obj-E<gt>B<set_address>($email, $name)

Add/replace an $email address to the field.

=item $obj-E<gt>B<text>( [STRING] )

Inherited, see L<Mail::Field/"Smart accessors">

=back

=head1 DETAILS

Extends L<"DETAILS" in Mail::Field|Mail::Field/"DETAILS">.
 
=head1 DIAGNOSTICS

=over 4

=item Error: Undefined subroutine <method> called

Mail::Field objects use autoloading to compile new functionality.
Apparently, the method called is not implemented for the specific
class of the field object.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! 2&  &    Field/Date.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Field::Date;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Field';

use strict;

use Date::Format qw(time2str);
use Date::Parse  qw(str2time);

(bless [])->register('Date');


sub set()
{   my $self = shift;
    my $arg = @_ == 1 ? shift : { @_ };

    foreach my $s (qw(Time TimeStr))
    {   if(exists $arg->{$s})
             { $self->{$s} = $arg->{$s} }
        else { delete $self->{$s} }
    }

    $self;
}

sub parse($)
{   my $self = shift;
    delete $self->{Time};
    $self->{TimeStr} = shift;
    $self;
}


sub time(;$)
{   my $self = shift;

    if(@_)
    {   delete $self->{TimeStr};
        return $self->{Time} = shift;
    }

    $self->{Time} ||= str2time $self->{TimeStr};
}

sub stringify
{   my $self = shift;
    $self->{TimeStr} ||= time2str("%a, %e %b %Y %T %z", $self->time);
}

sub reformat
{   my $self = shift;
    $self->time($self->time);
    $self->stringify;
}

1;
PK       ! 4>bx      Field/Date.podnu [        =encoding utf8

=head1 NAME

Mail::Field::Date - a date header field

=head1 INHERITANCE

 Mail::Field::Date
   is a Mail::Field

=head1 SYNOPSIS

  use HTTP::Date 'time2iso';
  my $field = Mail::Field->new(Date => time2iso());

=head1 DESCRIPTION

Represents one "Date" header field.

Extends L<"DESCRIPTION" in Mail::Field|Mail::Field/"DESCRIPTION">.
 
=head1 METHODS

Extends L<"METHODS" in Mail::Field|Mail::Field/"METHODS">.
 
=head2 Constructors

Extends L<"Constructors" in Mail::Field|Mail::Field/"Constructors">.
 
=over 4

=item Mail::Field::Date-E<gt>B<combine>($fields)

Inherited, see L<Mail::Field/"Constructors">

=item Mail::Field::Date-E<gt>B<extract>( $tag, $head [, $index ] )

Inherited, see L<Mail::Field/"Constructors">

=item Mail::Field::Date-E<gt>B<new>( $tag [, STRING | %options] )

Inherited, see L<Mail::Field/"Constructors">

=back

=head2 "Fake" constructors

Extends L<""Fake" constructors" in Mail::Field|Mail::Field/""Fake" constructors">.
 
=over 4

=item $obj-E<gt>B<create>(%options)

Inherited, see L<Mail::Field/""Fake" constructors">

=item $obj-E<gt>B<parse>()

Inherited, see L<Mail::Field/""Fake" constructors">

=back

=head2 Accessors

Extends L<"Accessors" in Mail::Field|Mail::Field/"Accessors">.
 
=over 4

=item $obj-E<gt>B<set>(%options)

 -Option --Default
  Time     undef
  TimeStr  undef

=over 2

=item Time => SECONDS

=item TimeStr => STRING

A string acceptable to Date::Parse.

=back

=item $obj-E<gt>B<stringify>()

Inherited, see L<Mail::Field/"Accessors">

=item $obj-E<gt>B<tag>()

=item Mail::Field::Date-E<gt>B<tag>()

Inherited, see L<Mail::Field/"Accessors">

=back

=head2 Smart accessors

Extends L<"Smart accessors" in Mail::Field|Mail::Field/"Smart accessors">.
 
=over 4

=item $obj-E<gt>B<text>( [STRING] )

Inherited, see L<Mail::Field/"Smart accessors">

=item $obj-E<gt>B<time>( [$time] )

Query (or change) the $time (as stored in the field) in seconds.

=back

=head1 DETAILS

Extends L<"DETAILS" in Mail::Field|Mail::Field/"DETAILS">.
 
=head1 DIAGNOSTICS

=over 4

=item Error: Undefined subroutine <method> called

Mail::Field objects use autoloading to compile new functionality.
Apparently, the method called is not implemented for the specific
class of the field object.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! T
  
    Field/Generic.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Field::Generic;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Field';

use Carp;


sub create
{   my ($self, %arg) = @_;
    $self->{Text} = delete $arg{Text};

    croak "Unknown options " . join(",", keys %arg)
       if %arg;

    $self;
}


sub parse
{   my $self = shift;
    $self->{Text} = shift || "";
    $self;
}

sub stringify { shift->{Text} }

1;
PK       ! WO  O    Field/AddrList.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

use strict;

package Mail::Field::AddrList;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Field';

use Carp;
use Mail::Address;


my $x = bless [];
$x->register('To');
$x->register('From');
$x->register('Cc');
$x->register('Reply-To');
$x->register('Sender');

sub create(@)
{   my ($self, %arg)  = @_;
    $self->{AddrList} = {};

    while(my ($e, $n) = each %arg)
    {   $self->{AddrList}{$e} = Mail::Address->new($n, $e);
    }

    $self;
}

sub parse($)
{   my ($self, $string) = @_;
    foreach my $a (Mail::Address->parse($string))
    {   my $e = $a->address;
	$self->{AddrList}{$e} = $a;
    }
    $self;
}

sub stringify()
{   my $self = shift;
    join(", ", map { $_->format } values %{$self->{AddrList}});
}


sub addresses { keys %{shift->{AddrList}} }


# someone forgot to implement a method to return the Mail::Address
# objects.  Added in 2.00; a pity that the name addresses() is already
# given :(  That one should have been named emails()
sub addr_list { values %{shift->{AddrList}} }


sub names { map { $_->name } values %{shift->{AddrList}} }


sub set_address($$)
{   my ($self, $email, $name) = @_;
    $self->{AddrList}{$email} = Mail::Address->new($name, $email);
    $self;
}

1;
PK       ! l݁e  e  
  Mailer.podnu [        =encoding utf8

=head1 NAME

Mail::Mailer - send simple emails

=head1 INHERITANCE

 Mail::Mailer
   is an IO::Handle

=head1 SYNOPSIS

  use Mail::Mailer;
  use Mail::Mailer qw(mail);    # specifies default mailer

  $mailer = Mail::Mailer->new;
  $mailer = Mail::Mailer->new($type, @args);

  $mailer->open(\%headers);
  print $mailer $body;
  $mailer->close
      or die "couldn't send whole message: $!\n";

=head1 DESCRIPTION

Sends mail using any of the built-in methods.  As TYPE argument
to L<new()|Mail::Mailer/"Constructors">, you can specify any of

=over 4

=item C<sendmail>

Use the C<sendmail> program to deliver the mail.

=item C<smtp>

Use the C<smtp> protocol via Net::SMTP to deliver the mail. The server
to use can be specified in C<@args> with

 $mailer = Mail::Mailer->new('smtp', Server => $server);

The smtp mailer does not handle C<Cc> and C<Bcc> lines, neither their
C<Resent-*> fellows. The C<Debug> options enables debugging output
from C<Net::SMTP>.

[added 2.21] You may also use the C<< StartTLS => 1 >> options to upgrade the
connection with STARTTLS.  You need C<libnet> version 1.28 (2014) for this
to work.

You may also use the C<< Auth => [ $user, $password ] >> option for SASL
authentication. To make this work, you have to install the L<Authen::SASL>
distribution yourself: it is not automatically installed.

=item C<smtps>

This option is B<deprecated> when you have C<libnet> 1.28 (2014) and above.

Use the smtp over ssl protocol via L<Net::SMTP::SSL> to deliver the mail.
Usage is identical to C<smtp>. You have to install Authen::SASL as
well.

 $mailer = Mail::Mailer->new('smtps', Server => $server);

=item C<qmail>

Use qmail's qmail-inject program to deliver the mail.

=item C<testfile>

Used for debugging, this displays the data to the file named in
C<$Mail::Mailer::testfile::config{outfile}> which defaults to a file
named C<mailer.testfile>.  No mail is ever sent.

=back

C<Mail::Mailer> will search for executables in the above order. The
default mailer will be the first one found.

=head1 METHODS

=head2 Constructors

=over 4

=item Mail::Mailer-E<gt>B<new>($type, %options)

The $type is one of the back-end sender implementations, as described in
the DESCRIPTION chapter of this manual page.  The %options are passed to
that back-end.

=item $obj-E<gt>B<open>(HASH)

The HASH consists of key and value pairs, the key being the name of
the header field (eg, C<To>), and the value being the corresponding
contents of the header field.  The value can either be a scalar
(eg, C<gnat@frii.com>) or a reference to an array of scalars
(C<< eg, ['gnat@frii.com', 'Tim.Bunce@ig.co.uk'] >>).

=back

=head1 DETAILS

=head2 ENVIRONMENT VARIABLES

=over 4

=item PERL_MAILERS

Augments/override the build in choice for binary used to send out
our mail messages.

Format:

    "type1:mailbinary1;mailbinary2;...:type2:mailbinaryX;...:..."

Example: assume you want you use private sendmail binary instead
of mailx, one could set C<PERL_MAILERS> to:

    "mail:/does/not/exists:sendmail:$HOME/test/bin/sendmail"

On systems which may include C<:> in file names, use C<|> as separator
between type-groups.

    "mail:c:/does/not/exists|sendmail:$HOME/test/bin/sendmail"

=back

=head2 BUGS

Mail::Mailer does not help with folding, and does not protect
against various web-script hacker attacks, for instance where
a new-line is inserted in the content of the field.

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! I7  I7  	  Header.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Header;
use vars '$VERSION';
$VERSION = '2.21';


use strict;
use Carp;

my $MAIL_FROM = 'KEEP';
my %HDR_LENGTHS = ();

our $FIELD_NAME = '[^\x00-\x1f\x7f-\xff :]+:';


##
## Private functions
##

sub _error { warn @_; () }

# tidy up internal hash table and list

sub _tidy_header
{   my $self    = shift;
    my $deleted = 0;

    for(my $i = 0 ; $i < @{$self->{mail_hdr_list}}; $i++)
    {   next if defined $self->{mail_hdr_list}[$i];

        splice @{$self->{mail_hdr_list}}, $i, 1;
        $deleted++;
        $i--;
    }

    if($deleted)
    {   local $_;
        my @del;

        while(my ($key,$ref) = each %{$self->{mail_hdr_hash}} )
        {   push @del, $key
	       unless @$ref = grep { ref $_ && defined $$_ } @$ref;
        }

        delete $self->{'mail_hdr_hash'}{$_} for @del;
    }
}

# fold the line to the given length

my %STRUCTURE = map { (lc $_ => undef) }
  qw{ To Cc Bcc From Date Reply-To Sender
      Resent-Date Resent-From Resent-Sender Resent-To Return-Path
      list-help list-post list-unsubscribe Mailing-List
      Received References Message-ID In-Reply-To
      Content-Length Content-Type Content-Disposition
      Delivered-To
      Lines
      MIME-Version
      Precedence
      Status
    };

sub _fold_line
{   my($ln,$maxlen) = @_;

    $maxlen = 20
       if $maxlen < 20;

    my $max = int($maxlen - 5);         # 4 for leading spcs + 1 for [\,\;]
    my $min = int($maxlen * 4 / 5) - 4;

    $_[0] =~ s/[\r\n]+//og;        # Remove new-lines
    $_[0] =~ s/\s*\Z/\n/so;        # End line with an EOLN

    return if $_[0] =~ /^From\s/io;

    if(length($_[0]) > $maxlen)
    {   if($_[0] =~ /^([-\w]+)/ && exists $STRUCTURE{ lc $1 } )
        {   #Split the line up
            # first bias towards splitting at a , or a ; >4/5 along the line
            # next split a whitespace
            # else we are looking at a single word and probably don't want to split
            my $x = "";
            $x .= "$1\n " while $_[0] =~
                s/^\s*
                   ( [^"]{$min,$max} [,;]
                   | [^"]{1,$max}    [,;\s]
                   | [^\s"]*(?:"[^"]*"[ \t]?[^\s"]*)+\s
                   ) //x;

            $x .= $_[0];
            $_[0] = $x;
            $_[0] =~ s/(\A\s+|[\t ]+\Z)//sog;
            $_[0] =~ s/\s+\n/\n/sog;
        }
        else
        {   $_[0] =~ s/(.{$min,$max})(\s)/$1\n$2/g;
            $_[0] =~ s/\s*$/\n/s;
        }
    }

    $_[0] =~ s/\A(\S+)\n\s*(?=\S)/$1 /so; 
}

# Tags are case-insensitive, but there is a (slightly) preferred construction
# being all characters are lowercase except the first of each word. Also
# if the word is an `acronym' then all characters are uppercase. We decide
# a word is an acronym if it does not contain a vowel.
# In general, this change of capitalization is a bad idea, but it is in
# the code for ages, and therefore probably crucial for existing
# applications.

sub _tag_case
{   my $tag = shift;
    $tag =~ s/\:$//;
    join '-'
      , map { /^[b-df-hj-np-tv-z]+$|^(?:MIME|SWE|SOAP|LDAP|ID)$/i
              ? uc($_) : ucfirst(lc($_))
            } split m/\-/, $tag, -1;
}

# format a complete line
#  ensure line starts with the given tag
#  ensure tag is correct case
#  change the 'From ' tag as required
#  fold the line

sub _fmt_line
{   my ($self, $tag, $line, $modify) = @_;
    $modify ||= $self->{mail_hdr_modify};
    my $ctag = undef;

    ($tag) = $line =~ /^($FIELD_NAME|From )/oi
        unless defined $tag;

    if(defined $tag && $tag =~ /^From /io && $self->{mail_hdr_mail_from} ne 'KEEP')
    {   if($self->{mail_hdr_mail_from} eq 'COERCE')
        {   $line =~ s/^From /Mail-From: /o;
            $tag = "Mail-From:";
        }
        elsif($self->{mail_hdr_mail_from} eq 'IGNORE')
        {   return ();
        }
        elsif($self->{mail_hdr_mail_from} eq 'ERROR')
        {    return _error "unadorned 'From ' ignored: <$line>";
        }
    }

    if(defined $tag)
    {   $tag  = _tag_case($ctag = $tag);
        $ctag = $tag if $modify;
        $ctag =~ s/([^ :])$/$1:/o if defined $ctag;
    }

    defined $ctag && $ctag =~ /^($FIELD_NAME|From )/oi
        or croak "Bad RFC822 field name '$tag'\n";

    # Ensure the line starts with tag
    if(defined $ctag && ($modify || $line !~ /^\Q$ctag\E/i))
    {   (my $xtag = $ctag) =~ s/\s*\Z//o;
        $line =~ s/^(\Q$ctag\E)?\s*/$xtag /i;
    }

    my $maxlen = $self->{mail_hdr_lengths}{$tag}
              || $HDR_LENGTHS{$tag}
              || $self->fold_length;

    if ($modify && defined $maxlen)
    {   # folding will fix bad header continuations for us
        _fold_line $line, $maxlen;
    }
    elsif($line =~ /\r?\n\S/)
    {   return _error "Bad header continuation, skipping '$tag': ",
                      "no space after newline in '$line'\n";
    }


    $line =~ s/\n*$/\n/so;
    ($tag, $line);
}

sub _insert
{   my ($self, $tag, $line, $where) = @_;

    if($where < 0)
    {   $where = @{$self->{mail_hdr_list}} + $where + 1;
        $where = 0 if $where < 0;
    }
    elsif($where >= @{$self->{mail_hdr_list}})
    {   $where = @{$self->{mail_hdr_list}};
    }

    my $atend = $where == @{$self->{mail_hdr_list}};
    splice @{$self->{mail_hdr_list}}, $where, 0, $line;

    $self->{mail_hdr_hash}{$tag} ||= [];
    my $ref = \${$self->{mail_hdr_list}}[$where];

    my $def = $self->{mail_hdr_hash}{$tag};
    if($def && $where)
    {   if($atend) { push @$def, $ref }
        else
        {   my $i = 0;
            foreach my $ln (@{$self->{mail_hdr_list}})
            {   my $r = \$ln;
                last if $r == $ref;
                $i++ if $r == $def->[$i];
            }
            splice @$def, $i, 0, $ref;
        }
    }
    else
    {    unshift @$def, $ref;
    }
}

#------------

sub new
{   my $call  = shift;
    my $class = ref($call) || $call;
    my $arg   = @_ % 2 ? shift : undef;
    my %opt   = @_;

    $opt{Modify} = delete $opt{Reformat}
        unless exists $opt{Modify};

    my $self = bless
      { mail_hdr_list     => []
      , mail_hdr_hash     => {}
      , mail_hdr_modify   => (delete $opt{Modify} || 0)
      , mail_hdr_foldlen  => 79
      , mail_hdr_lengths  => {}
      }, $class;

    $self->mail_from( uc($opt{MailFrom} || $MAIL_FROM) );

    $self->fold_length($opt{FoldLength})
        if exists $opt{FoldLength};

    if(!ref $arg)               {}
    elsif(ref($arg) eq 'ARRAY') { $self->extract( [ @$arg ] ) }
    elsif(defined fileno($arg)) { $self->read($arg) }

    $self;
}


sub dup
{   my $self = shift;
    my $dup  = ref($self)->new;

    %$dup    = %$self;
    $dup->empty;      # rebuild tables

    $dup->{mail_hdr_list} = [ @{$self->{mail_hdr_list}} ];

    foreach my $ln ( @{$dup->{mail_hdr_list}} )
    {    my $tag = _tag_case +($ln =~ /^($FIELD_NAME|From )/oi)[0];
         push @{$dup->{mail_hdr_hash}{$tag}}, \$ln;
    }

    $dup;
}

#------------

sub extract
{   my ($self, $lines) = @_;
    $self->empty;

    while(@$lines)
    {   my $line = shift @$lines;
        last if $line =~ /^\r?$/;

        $line    =~ /^($FIELD_NAME|From )/o or next;
        my $tag  = $1;

        $line   .= shift @$lines
            while @$lines && $lines->[0] =~ /^[ \t]+/;

        ($tag, $line) = _fmt_line $self, $tag, $line;

        _insert $self, $tag, $line, -1
            if defined $line;
    }

    $self;
}


sub read
{   my ($self, $fd) = @_;
    $self->empty;

    my ($ln, $tag, $line);
    while(1)
    {   $ln = <$fd>;

        if(defined $ln && defined $line && $ln =~ /^[ \t]+/)
        {   $line .= $ln;  # folded line
            next;
        }

        if(defined $line)
        {   ($tag, $line) = _fmt_line $self, $tag, $line;
            _insert $self, $tag, $line, -1
	        if defined $line;
            ($tag, $line) = ();
        }

        last if !defined $ln || $ln =~ m/^\r?$/;

        $ln =~ /^($FIELD_NAME|From )/o or next;
        ($tag, $line) = ($1, $ln);
    }

    $self;
}


sub empty
{   my $self = shift;
    $self->{mail_hdr_list} = [];
    $self->{mail_hdr_hash} = {};
    $self;
}


sub header
{   my $self = shift;

    $self->extract(@_)
	if @_;

    $self->fold
        if $self->{mail_hdr_modify};

    [ @{$self->{mail_hdr_list}} ];
}


sub header_hashref
{   my ($self, $hashref) = @_;

    while(my ($key, $value) = each %$hashref)
    {   $self->add($key, $_) for ref $value ? @$value : $value;
    }

    $self->fold
        if $self->{mail_hdr_modify};

    defined wantarray  # MO, added minimal optimization
        or return;

    +{ map { ($_ => [$self->get($_)] ) }   # MO: Eh?
           keys %{$self->{mail_hdr_hash}}
     }; 
}

#------------

sub modify
{   my $self = shift;
    my $old  = $self->{mail_hdr_modify};

    $self->{mail_hdr_modify} = 0 + shift
	if @_;

    $old;
}


sub mail_from
{   my $thing  = shift;
    my $choice = uc shift;

    $choice =~ /^(IGNORE|ERROR|COERCE|KEEP)$/ 
	or die "bad Mail-From choice: '$choice'";

    if(ref $thing) { $thing->{mail_hdr_mail_from} = $choice }
    else           { $MAIL_FROM = $choice }

    $thing;
}


sub fold_length
{   my $thing = shift;
    my $old;

    if(@_ == 2)
    {   my $tag = _tag_case shift;
        my $len = shift;

        my $hash = ref $thing ? $thing->{mail_hdr_lengths} : \%HDR_LENGTHS;
        $old     = $hash->{$tag};
        $hash->{$tag} = $len > 20 ? $len : 20;
    }
    else
    {   my $self = $thing;
        my $len  = shift;
        $old = $self->{mail_hdr_foldlen};

        if(defined $len)
        {    $self->{mail_hdr_foldlen} = $len > 20 ? $len : 20;
             $self->fold if $self->{mail_hdr_modify};
        }
    }

    $old;
}

#------------

sub fold
{   my ($self, $maxlen) = @_;

    while(my ($tag, $list) = each %{$self->{mail_hdr_hash}})
    {   my $len = $maxlen
             || $self->{mail_hdr_lengths}{$tag}
             || $HDR_LENGTHS{$tag}
             || $self->fold_length;

        foreach my $ln (@$list)
        {    _fold_line $$ln, $len
                 if defined $ln;
        }
    }

    $self;
}


sub unfold
{   my $self = shift;

    if(@_)
    {   my $tag  = _tag_case shift;
        my $list = $self->{mail_hdr_hash}{$tag}
            or return $self;

        foreach my $ln (@$list)
        {   $$ln =~ s/\r?\n\s+/ /sog
                if defined $ln && defined $$ln;
        }

        return $self;
    }

    while( my ($tag, $list) = each %{$self->{mail_hdr_hash}})
    {   foreach my $ln (@$list)
        {   $$ln =~ s/\r?\n\s+/ /sog
	        if defined $ln && defined $$ln;
        }
    }

    $self;
}


sub add
{   my ($self, $tag, $text, $where) = @_;
    ($tag, my $line) = _fmt_line $self, $tag, $text;

    defined $tag && defined $line
        or return undef;

    defined $where
        or $where = -1;

    _insert $self, $tag, $line, $where;

    $line =~ /^\S+\s(.*)/os;
    $1;
}


sub replace
{   my $self = shift;
    my $idx  = @_ % 2 ? pop @_ : 0;

    my ($tag, $line);
  TAG:
    while(@_)
    {   ($tag,$line) = _fmt_line $self, splice(@_,0,2);

        defined $tag && defined $line
            or return undef;

        my $field = $self->{mail_hdr_hash}{$tag};
        if($field && defined $field->[$idx])
             { ${$field->[$idx]} = $line }
        else { _insert $self, $tag, $line, -1 }
    }

    $line =~ /^\S+\s*(.*)/os;
    $1;
}


sub combine
{   my $self = shift;
    my $tag  = _tag_case shift;
    my $with = shift || ' ';

    $tag =~ /^From /io && $self->{mail_hdr_mail_from} ne 'KEEP'
        and return _error "unadorned 'From ' ignored";

    my $def = $self->{mail_hdr_hash}{$tag}
        or return undef;

    return $def->[0]
        if @$def <= 1;

    my @lines = $self->get($tag);
    chomp @lines;

    my $line = (_fmt_line $self, $tag, join($with,@lines), 1)[1];

    $self->{mail_hdr_hash}{$tag} = [ \$line ];
    $line;
}


sub get
{   my $self = shift;
    my $tag = _tag_case shift;
    my $idx = shift;

    my $def = $self->{mail_hdr_hash}{$tag}
        or return ();

    my $l = length $tag;
    $l   += 1 if $tag !~ / $/o;

    if(defined $idx || !wantarray)
    {    $idx ||= 0;
         defined $def->[$idx] or return undef;
         my $val = ${$def->[$idx]};
         defined $val or return undef;

	 $val = substr $val, $l;
	 $val =~ s/^\s+//;
         return $val;
    }

    map { my $tmp = substr $$_,$l; $tmp =~ s/^\s+//; $tmp } @$def;
}



sub count
{   my $self = shift;
    my $tag  = _tag_case shift;
    my $def  = $self->{mail_hdr_hash}{$tag};
    defined $def ? scalar(@$def) : 0;
}



sub delete
{   my $self = shift;
    my $tag  = _tag_case shift;
    my $idx  = shift;
    my @val;

    if(my $def = $self->{mail_hdr_hash}{$tag})
    {   my $l = length $tag;
        $l   += 2 if $tag !~ / $/;

        if(defined $idx)
        {   if(defined $def->[$idx])
            {   push @val, substr ${$def->[$idx]}, $l;
                undef ${$def->[$idx]};
            }
        }
        else
        {   @val = map {my $x = substr $$_,$l; undef $$_; $x } @$def;
        }

        _tidy_header($self);
    }

    @val;
}



sub print
{   my $self = shift;
    my $fd   = shift || \*STDOUT;

    foreach my $ln (@{$self->{mail_hdr_list}})
    {   defined $ln or next;
        print $fd $ln or return 0;
    }

    1;
}


sub as_string { join '', grep {defined} @{shift->{mail_hdr_list}} }


sub tags { keys %{shift->{mail_hdr_hash}} }


sub cleanup
{   my $self = shift;
    my $deleted = 0;

    foreach my $key (@_ ? @_ : keys %{$self->{mail_hdr_hash}})
    {   my $fields = $self->{mail_hdr_hash}{$key};
        foreach my $field (@$fields)
        {   next if $$field =~ /^\S+\s+\S/s;
            undef $$field;
            $deleted++;
        }
    }

    _tidy_header $self
        if $deleted;

    $self;  
}

1;
PK       ! R      Field.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Field;
use vars '$VERSION';
$VERSION = '2.21';


use strict;

use Carp;
use Mail::Field::Generic;


sub _header_pkg_name
{   my $header = lc shift;
    $header    =~ s/((\b|_)\w)/\U$1/g;

    if(length($header) > 8)
    {   my @header = split /[-_]+/, $header;
        my $chars  = int((7 + @header) / @header) || 1;
        $header    = substr join('', map {substr $_,0,$chars} @header), 0, 8;
    }
    else
    {   $header =~ s/[-_]+//g;
    }

    'Mail::Field::' . $header;
}

sub _require_dir
{   my($class, $dir, $dir_sep) = @_;

    local *DIR;
    opendir DIR, $dir
        or return;

    my @inc;
    foreach my $f (readdir DIR)
    {   $f =~ /^([\w\-]+)/ or next;
        my $p = $1;
        my $n = "$dir$dir_sep$p";

        if(-d $n )
        {   _require_dir("${class}::$f", $n, $dir_sep);
        }
        else
        {   $p =~ s/-/_/go;
            eval "require ${class}::$p";

            # added next warning in 2.14, may be ignored for ancient code
            warn $@ if $@;
        }
    }
    closedir DIR;
}

sub import
{   my $class = shift;

    if(@_)
    {   local $_;
        eval "require " . _header_pkg_name($_) || die $@
            for @_;
        return;
    }

    my ($dir, $dir_sep);
    foreach my $f (grep defined $INC{$_}, keys %INC)
    {   next if $f !~ /^Mail(\W)Field\W/i;
        $dir_sep = $1;
# $dir = ($INC{$f} =~ /(.*Mail\W+Field)/i)[0] . $dir_sep;
        ($dir = $INC{$f}) =~ s/(Mail\W+Field).*/$1$dir_sep/;
        last;
    }

    _require_dir('Mail::Field', $dir, $dir_sep);
}

# register a header class, this creates a new method in Mail::Field
# which will call new on that class
sub register
{   my $thing  = shift;
    my $method = lc shift;
    my $class  = shift || ref($thing) || $thing;

    $method    =~ tr/-/_/;
    $class     = _header_pkg_name $method
	if $class eq "Mail::Field";

    croak "Re-register of $method"
	if Mail::Field->can($method);

    no strict 'refs';
    *{$method} = sub {
	shift;
	$class->can('stringify') or eval "require $class" or die $@;
	$class->_build(@_);
    };
}

# the *real* constructor
# if called with one argument then the `parse' method will be called
# otherwise the `create' method is called

sub _build
{   my $self = bless {}, shift;
    @_==1 ? $self->parse(@_) : $self->create(@_);
}

#-------------

sub new
{   my $class = shift;
    my $field = lc shift;
    $field =~ tr/-/_/;
    $class->$field(@_);
}


sub combine {confess "Combine not implemented" }

our $AUTOLOAD;
sub AUTOLOAD
{   my $method = $AUTOLOAD;
    $method    =~ s/.*:://;

    $method    =~ /^[^A-Z\x00-\x1f\x80-\xff :]+$/
        or croak "Undefined subroutine &$AUTOLOAD called";

    my $class = _header_pkg_name $method;

    unless(eval "require $class")
    {   my $tag = $method;
        $tag    =~ s/_/-/g;
        $tag    = join '-',
            map { /^[b-df-hj-np-tv-z]+$|^MIME$/i ? uc($_) : ucfirst(lc $_) }
                split /\-/, $tag;

        no strict;
        @{"${class}::ISA"} = qw(Mail::Field::Generic);
        *{"${class}::tag"} = sub { $tag };
    }

    Mail::Field->can($method)
        or $class->register($method);

    goto &$AUTOLOAD;
}


# Of course, the functionality should have been in the Mail::Header class
sub extract
{   my ($class, $tag, $head) = (shift, shift, shift);

    my $method = lc $tag;
    $method    =~ tr/-/_/;

    if(@_==0 && wantarray)
    {   my @ret;
        my $text;  # need real copy!
        foreach $text ($head->get($tag))
        {   chomp $text;
            push @ret, $class->$method($text);
        }
        return @ret;
    }

    my $idx  = shift || 0;
    my $text = $head->get($tag,$idx)
        or return undef;

    chomp $text;
    $class->$method($text);
}

#-------------

# before 2.00, this method could be called as class method, however
# not all extensions supported that.
sub create
{   my ($self, %arg) = @_;
    %$self = ();
    $self->set(\%arg);
}


# before 2.00, this method could be called as class method, however
# not all extensions supported that.
sub parse
{   my $class = ref shift;
    confess "parse() not implemented";
}

#-------------

sub stringify { confess "stringify() not implemented" } 


sub tag
{   my $thing = shift;
    my $tag   = ref($thing) || $thing;
    $tag =~ s/.*:://;
    $tag =~ s/_/-/g;

    join '-',
        map { /^[b-df-hj-np-tv-z]+$|^MIME$/i ? uc($_) : ucfirst(lc $_) }
            split /\-/, $tag;
}


sub set(@) { confess "set() not implemented" }

# prevent the calling of AUTOLOAD for DESTROY :-)
sub DESTROY {}

#-------------

sub text
{   my $self = shift;
    @_ ? $self->parse(@_) : $self->stringify;
}

#-------------

1;
PK       ! (AߟL  L    Address.podnu [        =encoding utf8

=head1 NAME

Mail::Address - parse mail addresses

=head1 SYNOPSIS

 use Mail::Address;
 my @addrs = Mail::Address->parse($line);

 foreach $addr (@addrs) {
     print $addr->format,"\n";
 }

=head1 DESCRIPTION

C<Mail::Address> extracts and manipulates email addresses from a message
header.  It cannot be used to extract addresses from some random text.
You can use this module to create RFC822 compliant fields.

Although C<Mail::Address> is a very popular subject for books, and is
used in many applications, it does a very poor job on the more complex
message fields.  It does only handle simple address formats (which
covers about 95% of what can be found). Problems are with

=over 4

=item *

no support for address groups, even not with the semi-colon as
separator between addresses;

=item *

limited support for escapes in phrases and comments.  There are
cases where it can get wrong; and

=item *

you have to take care of most escaping when you create an address yourself:
C<Mail::Address> does not do that for you.

=back

Often requests are made to the maintainers of this code improve this
situation, but this is not a good idea, where it will break zillions
of existing applications.  If you wish for a fully RFC2822 compliant
implementation you may take a look at L<Mail::Message::Field::Full>,
part of MailBox.

B<. Example>

  my $s = Mail::Message::Field::Full->new($from_header);
  # ref $s isa Mail::Message::Field::Addresses;

  my @g = $s->groups;          # all groups, at least one
  # ref $g[0] isa Mail::Message::Field::AddrGroup;
  my $ga = $g[0]->addresses;   # group addresses

  my @a = $s->addresses;       # all addresses
  # ref $a[0] isa Mail::Message::Field::Address;

=head1 METHODS

=head2 Constructors

=over 4

=item Mail::Address-E<gt>B<new>( $phrase, $address, [ $comment ] )

Create a new C<Mail::Address> object which represents an address with the
elements given. In a message these 3 elements would be seen like:

 PHRASE <ADDRESS> (COMMENT)
 ADDRESS (COMMENT)

example: 

 Mail::Address->new("Perl5 Porters", "perl5-porters@africa.nicoh.com");

=item $obj-E<gt>B<parse>($line)

Parse the given line a return a list of extracted C<Mail::Address> objects.
The line would normally be one taken from a To,Cc or Bcc line in a message

example: 

 my @addr = Mail::Address->parse($line);

=back

=head2 Accessors

=over 4

=item $obj-E<gt>B<address>()

Return the address part of the object.

=item $obj-E<gt>B<comment>()

Return the comment part of the object

=item $obj-E<gt>B<format>(@addresses)

Return a string representing the address in a suitable form to be placed
on a C<To>, C<Cc>, or C<Bcc> line of a message.  This method is called on
the first address to be used; other specified addresses will be appended,
separated by commas.

=item $obj-E<gt>B<phrase>()

Return the phrase part of the object.

=back

=head2 Smart accessors

=over 4

=item $obj-E<gt>B<host>()

Return the address excluding the user id and '@'

=item $obj-E<gt>B<name>()

Using the information contained within the object attempt to identify what
the person or groups name is.

B<Note:> This function tries to be smart with the "phrase" of the
email address, which is probably a very bad idea.  Consider to use
L<phrase()|Mail::Address/"Accessors"> itself.

=item $obj-E<gt>B<user>()

Return the address excluding the '@' and the mail domain

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! B(  (    Internet.podnu [        =encoding utf8

=head1 NAME

Mail::Internet - manipulate email messages

=head1 SYNOPSIS

  use Mail::Internet;
  my $msg = Mail::Internet->new(\*STDIN);

=head1 DESCRIPTION

This package implements reading, creating, manipulating, and writing email
messages.  Sometimes, the implementation tries to be too smart, but in
the general case it works as expected.

If you start writing a B<new application>, you should use the L<Mail::Box>
distribution, which has more features and handles messages much better
according to the RFCs.  See L<http://perl.overmeer.net/mailbox/>.
You may also chose L<MIME::Entity>, to get at least some multipart
support in your application.

=head1 METHODS

=head2 Constructors

=over 4

=item $obj-E<gt>B<dup>()

Duplicate the message as a whole.  Both header and body will be
deep-copied: a new L<Mail::Internet|Mail::Internet> object is returned.

=item $obj-E<gt>B<extract>(\@lines)

Extract header and body from an ARRAY of message lines.  Requires an
object already created with L<new()|Mail::Internet/"Constructors">, which contents will get overwritten.

=item $obj-E<gt>B<new>( [$arg], [%options] )

=item Mail::Internet-E<gt>B<new>( [$arg], [%options] )

$arg is optional and may be either a file descriptor (reference to a GLOB)
or a reference to an array. If given the new object will be
initialized with headers and body either from the array of read from 
the file descriptor.

The L<Mail::Header::new()|Mail::Header/"Constructors"> %options C<Modify>, C<MailFrom> and C<FoldLength>
may also be given.

 -Option--Default
  Body    []
  Header  undef

=over 2

=item Body => ARRAY-of-LINES

The value of this option should be a reference to an array which contains
the lines for the body of the message. Each line should be terminated with
C<\n> (LF). If Body is given then C<Mail::Internet> will not attempt to
read the body from C<$arg> (even if it is specified).

=item Header => Mail::Header

The value of this option should be a L<Mail::Header|Mail::Header> object. If given then
C<Mail::Internet> will not attempt to read a mail header from C<$arg>, if
it was specified.

=back

=item $obj-E<gt>B<read>($fh)

Read a message from the $fh into an already existing message
object.  Better use L<new()|Mail::Internet/"Constructors"> with the $fh as first argument.

=back

=head2 Accessors

=over 4

=item $obj-E<gt>B<body>( [$body] )

Returns the body of the message. This is a reference to an array.
Each entry in the array represents a single line in the message.

If I<$body> is given, it can be a reference to an array or an array, then
the body will be replaced. If a reference is passed, it is used directly
and not copied, so any subsequent changes to the array will change the
contents of the body.

=item $obj-E<gt>B<head>()

Returns the C<Mail::Header> object which holds the headers for the current
message

=back

=head2 Processing the message as a whole

=over 4

=item $obj-E<gt>B<as_mbox_string>( [$already_escaped] )

Returns the message as a string in mbox format.  C<$already_escaped>, if
given and true, indicates that L<escape_from()|Mail::Internet/"High-level functionality"> has already been called on
this object.

=item $obj-E<gt>B<as_string>()

Returns the message as a single string.

=item $obj-E<gt>B<print>( [$fh] )

Print the header, body or whole message to file descriptor I<$fh>.
I<$fd> should be a reference to a GLOB. If I<$fh> is not given the
output will be sent to STDOUT.

example: 

    $mail->print( \*STDOUT );  # Print message to STDOUT

=item $obj-E<gt>B<print_body>( [$fh] )

Print only the body to the $fh (default STDOUT).

=item $obj-E<gt>B<print_header>( [$fh] )

Print only the header to the $fh (default STDOUT).

=back

=head2 Processing the header

Most of these methods are simply wrappers around methods provided
by L<Mail::Header|Mail::Header>.

=over 4

=item $obj-E<gt>B<add>(PAIRS)

The PAIRS are field-name and field-content.  For each PAIR,
L<Mail::Header::add()|Mail::Header/"Processing"> is called.  All fields are added after
existing fields.  The last addition is returned.

=item $obj-E<gt>B<combine>( $tag, [$with] )

See L<Mail::Header::combine()|Mail::Header/"Processing">.

=item $obj-E<gt>B<delete>( $tag, [$tags] )

Delete all fields with the name $tag.  L<Mail::Header::delete()|Mail::Header/"Processing"> is doing the
work.

=item $obj-E<gt>B<fold>( [$length] )

See L<Mail::Header::fold()|Mail::Header/"Processing">.

=item $obj-E<gt>B<fold_length>( [$tag], [$length] )

See L<Mail::Header::fold_length()|Mail::Header/"Accessors">.

=item $obj-E<gt>B<get>( $tag, [$tags] )

In LIST context, all fields with the name $tag are returned.  In SCALAR
context, only the first field which matches the earliest $tag is returned.
L<Mail::Header::get()|Mail::Header/"Processing"> is called to collect the data.

=item $obj-E<gt>B<header>(\@lines)

See L<Mail::Header::header()|Mail::Header/""Fake" constructors">.

=item $obj-E<gt>B<replace>(PAIRS)

The PAIRS are field-name and field-content.  For each PAIR,
L<Mail::Header::replace()|Mail::Header/"Processing"> is called with index 0. If a $field is already
in the header, it will be removed first.  Do not specified the same
field-name twice.

=back

=head2 Processing the body

=over 4

=item $obj-E<gt>B<remove_sig>( [$nlines] )

Attempts to remove a user's signature from the body of a message. It does this 
by looking for a line equal to C<'-- '> within the last C<$nlines> of the
message. If found then that line and all lines after it will be removed. If
C<$nlines> is not given a default value of 10 will be used. This would be of
most use in auto-reply scripts.

=item $obj-E<gt>B<sign>(%options)

Add your signature to the body.  L<remove_sig()|Mail::Internet/"Processing the body"> will strip existing
signatures first.

 -Option   --Default
  File       undef
  Signature  []

=over 2

=item File => FILEHANDLE

Take from the FILEHANDLE all lines starting from the first C<< -- >>.

=item Signature => STRING|ARRAY-of-LINES

=back

=item $obj-E<gt>B<tidy_body>()

Removes all leading and trailing lines from the body that only contain
white spaces.

=back

=head2 High-level functionality

=over 4

=item $obj-E<gt>B<escape_from>()

It can cause problems with some applications if a message contains a line
starting with C<`From '>, in particular when attempting to split a folder.
This method inserts a leading C<`>'> on any line that matches the regular
expression C</^>*From/>

=item $obj-E<gt>B<nntppost>( [%options] )

Post an article via NNTP.  Requires Net::NNTP to be installed.

 -Option--Default
  Debug   <false>
  Host    <required>
  Port    119

=over 2

=item Debug => BOOLEAN

Debug value to pass to Net::NNTP, see L<Net::NNTP>

=item Host => HOSTNAME|Net::NNTP object

Name of NNTP server to connect to, or a Net::NNTP object to use.

=item Port => INTEGER

Port number to connect to on remote host

=back

=item $obj-E<gt>B<reply>(%options)

Create a new object with header initialised for a reply to the current 
object. And the body will be a copy of the current message indented.

The C<.mailhdr> file in your home directory (if exists) will be read
first, to provide defaults.

 -Option  --Default
  Exclude   []
  Indent    '>'
  Keep      []
  ReplyAll  false

=over 2

=item Exclude => ARRAY-of-FIELDS

Remove the listed FIELDS from the produced message.

=item Indent => STRING

Use as indentation string.  The string may contain C<%%> to get a single C<%>,
C<%f> to get the first from name, C<%F> is the first character of C<%f>,
C<%l> is the last name, C<%L> its first character, C<%n> the whole from
string, and C<%I> the first character of each of the names in the from string.

=item Keep => ARRAY-of-FIELDS

Copy the listed FIELDS from the original message.

=item ReplyAll => BOOLEAN

Automatically include all To and Cc addresses of the original mail,
excluding those mentioned in the Bcc list.

=back

=item $obj-E<gt>B<send>( [$type, [$args...]] )

Send a Mail::Internet message using L<Mail::Mailer|Mail::Mailer>.  $type and $args are
passed on to L<Mail::Mailer::new()|Mail::Mailer/"Constructors">.

=item $obj-E<gt>B<smtpsend>( [%options] )

Send a Mail::Internet message using direct SMTP to the given
ADDRESSES, each can be either a string or a reference to a list of email
addresses. If none of C<To>, <Cc> or C<Bcc> are given then the addresses
are extracted from the message being sent.

The return value will be a list of email addresses that the message was sent
to. If the message was not sent the list will be empty.

Requires Net::SMTP and Net::Domain to be installed.

 -Option  --Default
  Bcc       undef
  Cc        undef
  Debug     <false>
  Hello     localhost.localdomain
  Host      $ENV{SMTPHOSTS}
  MailFrom  Mail::Util::mailaddress()
  Port      25
  To        undef

=over 2

=item Bcc => ADDRESSES

=item Cc => ADDRESSES

=item Debug => BOOLEAN

Debug value to pass to Net::SMTP, see <Net::SMTP>

=item Hello => STRING

Send a HELO (or EHLO) command to the server with the given name.

=item Host => HOSTNAME

Name of the SMTP server to connect to, or a Net::SMTP object to use

If C<Host> is not given then the SMTP host is found by attempting
connections first to hosts specified in C<$ENV{SMTPHOSTS}>, a colon
separated list, then C<mailhost> and C<localhost>.

=item MailFrom => ADDRESS

The e-mail address which is used as sender.  By default,
L<Mail::Util::mailaddress()|Mail::Util/"FUNCTIONS"> provides the address of the sender.

=item Port => INTEGER

Port number to connect to on remote host

=item To => ADDRESSES

=back

=item $obj-E<gt>B<unescape_from>(())

Remove the escaping added by L<escape_from()|Mail::Internet/"High-level functionality">.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! pFh  h    Send.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Send;
use vars '$VERSION';
$VERSION = '2.21';


use strict;

use Mail::Mailer ();

sub Version { our $VERSION }

#------------------

sub new(@)
{   my ($class, %attr) = @_;
    my $self = bless {}, $class;

    while(my($key, $value) = each %attr)
    {	$key = lc $key;
        $self->$key($value);
    }

    $self;
}

#---------------

sub set($@)
{   my ($self, $hdr, @values) = @_;
    $self->{$hdr} = [ @values ] if @values;
    @{$self->{$hdr} || []};	# return new (or original) values
}


sub add($@)
{   my ($self, $hdr, @values) = @_;
    push @{$self->{$hdr}}, @values;
}


sub delete($)
{   my($self, $hdr) = @_;
    delete $self->{$hdr};
}


sub to		{ my $self=shift; $self->set('To', @_); }
sub cc		{ my $self=shift; $self->set('Cc', @_); }
sub bcc		{ my $self=shift; $self->set('Bcc', @_); }
sub subject	{ my $self=shift; $self->set('Subject', join (' ', @_)); }

#---------------

sub open(@)
{   my $self = shift;
    Mail::Mailer->new(@_)->open($self);
}

1;
PK       ! Cy      Cap.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Cap;
use vars '$VERSION';
$VERSION = '2.21';


use strict;

sub Version { our $VERSION }


our $useCache = 1;  # don't evaluate tests every time

my @path;
if($^O eq "MacOS")
{   @path = split /\,/, $ENV{MAILCAPS} || "$ENV{HOME}mailcap";
}
else
{   @path = split /\:/
      , ( $ENV{MAILCAPS} || (defined $ENV{HOME} ? "$ENV{HOME}/.mailcap:" : '')
        . '/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap'
        );   # this path is specified under RFC1524 appendix A 
}

#--------

sub new
{   my $class = shift;
    
    unshift @_, 'filename' if @_ % 2;
    my %args  = @_;

    my $take_all = $args{take} && uc $args{take} eq 'ALL';

    my $self  = bless {_count => 0}, $class;

    $self->_process_file($args{filename})
        if defined $args{filename} && -r $args{filename};

    if(!defined $args{filename} || $take_all)
    {   foreach my $fname (@path)
        {   -r $fname or next;

            $self->_process_file($fname);
            last unless $take_all;
        }
    }

    unless($self->{_count})
    {   # Set up default mailcap
        $self->{'audio/*'} = [{'view' => "showaudio %s"}];
        $self->{'image/*'} = [{'view' => "xv %s"}];
        $self->{'message/rfc822'} = [{'view' => "xterm -e metamail %s"}];
    }

    $self;
}

sub _process_file
{   my $self = shift;
    my $file = shift or return;

    local *MAILCAP;
    open MAILCAP, $file
        or return;

    $self->{_file} = $file;

    local $_;
    while(<MAILCAP>)
    {   next if /^\s*#/; # comment
        next if /^\s*$/; # blank line
        $_ .= <MAILCAP>  # continuation line
           while s/(^|[^\\])((?:\\\\)*)\\\s*$/$1$2/;
        chomp;
        s/\0//g;              # ensure no NULs in the line
        s/(^|[^\\]);/$1\0/g;  # make field separator NUL
        my ($type, $view, @parts) = split /\s*\0\s*/;

        $type    .= "/*" if $type !~ m[/];
        $view     =~ s/\\;/;/g;
        $view     =~ s/\\\\/\\/g;
        my %field = (view => $view);

        foreach (@parts)
        {   my($key, $val) = split /\s*\=\s*/, $_, 2;
            if(defined $val)
            {   $val =~ s/\\;/;/g;
                $val =~ s/\\\\/\\/g;
                $field{$key} = $val;
            }
            else
            {   $field{$key} = 1;
            }
        }

        if(my $test = $field{test})
        {   unless ($test =~ /\%/)
            {   # No parameters in test, can perform it right away
                system $test;
                next if $?;
            }
        }

        # record this entry
        unless(exists $self->{$type})
        {   $self->{$type} = [];
            $self->{_count}++; 
        }
        push @{$self->{$type}}, \%field;
    }

    close MAILCAP;
}

#------------------

sub view    { my $self = shift; $self->_run($self->viewCmd(@_))    }
sub compose { my $self = shift; $self->_run($self->composeCmd(@_)) }
sub edit    { my $self = shift; $self->_run($self->editCmd(@_))    }
sub print   { my $self = shift; $self->_run($self->printCmd(@_))   }

sub _run($)
{   my ($self, $cmd) = @_;
    defined $cmd or return 0;

    system $cmd;
    1;
}

#------------------

sub viewCmd    { shift->_createCommand(view    => @_) }
sub composeCmd { shift->_createCommand(compose => @_) }
sub editCmd    { shift->_createCommand(edit    => @_) }
sub printCmd   { shift->_createCommand(print   => @_) }

sub _createCommand($$$)
{   my ($self, $method, $type, $file) = @_;
    my $entry = $self->getEntry($type, $file);

    $entry && exists $entry->{$method}
        or return undef;

    $self->expandPercentMacros($entry->{$method}, $type, $file);
}

sub makeName($$)
{   my ($self, $type, $basename) = @_;
    my $template = $self->nametemplate($type)
        or return $basename;

    $template =~ s/%s/$basename/g;
    $template;
}

#------------------

sub field($$)
{   my($self, $type, $field) = @_;
    my $entry = $self->getEntry($type);
    $entry->{$field};
}


sub description     { shift->field(shift, 'description');     }
sub textualnewlines { shift->field(shift, 'textualnewlines'); }
sub x11_bitmap      { shift->field(shift, 'x11-bitmap');      }
sub nametemplate    { shift->field(shift, 'nametemplate');    }

sub getEntry
{   my($self, $origtype, $file) = @_;

    return $self->{_cache}{$origtype}
        if $useCache && exists $self->{_cache}{$origtype};

    my ($fulltype, @params) = split /\s*;\s*/, $origtype;
    my ($type, $subtype)    = split m[/], $fulltype, 2;
    $subtype ||= '';

    my $entry;
    foreach (@{$self->{"$type/$subtype"}}, @{$self->{"$type/*"}})
    {   if(exists $_->{'test'})
        {   # must run test to see if it applies
            my $test = $self->expandPercentMacros($_->{'test'},
        					  $origtype, $file);
            system $test;
            next if $?;
        }
        $entry = { %$_ };  # make copy
        last;
    }
    $self->{_cache}{$origtype} = $entry if $useCache;
    $entry;
}

sub expandPercentMacros
{   my ($self, $text, $type, $file) = @_;
    defined $type or return $text;
    defined $file or $file = "";

    my ($fulltype, @params) = split /\s*;\s*/, $type;
    ($type, my $subtype)    = split m[/], $fulltype, 2;

    my %params;
    foreach (@params)
    {   my($key, $val) = split /\s*=\s*/, $_, 2;
        $params{$key} = $val;
    }
    $text =~ s/\\%/\0/g;        # hide all escaped %'s
    $text =~ s/%t/$fulltype/g;  # expand %t
    $text =~ s/%s/$file/g;      # expand %s
    {   # expand %{field}
        local $^W = 0;  # avoid warnings when expanding %params
        $text =~ s/%\{\s*(.*?)\s*\}/$params{$1}/g;
    }
    $text =~ s/\0/%/g;
    $text;
}

# This following procedures can be useful for debugging purposes

sub dumpEntry
{   my($hash, $prefix) = @_;
    defined $prefix or $prefix = "";
    print "$prefix$_ = $hash->{$_}\n"
        for sort keys %$hash;
}

sub dump
{   my $self = shift;
    foreach (keys %$self)
    {   next if /^_/;
        print "$_\n";
        foreach (@{$self->{$_}})
        {   dumpEntry($_, "\t");
            print "\n";
        }
    }

    if(exists $self->{_cache})
    {   print "Cached types\n";
        print "\t$_\n"
            for keys %{$self->{_cache}};
    }
}

1;
PK       ! V(F  F  
  Filter.podnu [        =encoding utf8

=head1 NAME

Mail::Filter - filter mail through multiple subroutines

=head1 SYNOPSIS

 use Mail::Filter;
    
 my $filter = Mail::Filter->new( \&filter1, \&filter2 );
    
 my $mail   = Mail::Internet->new( [<>] );
 my $mail   = $filter->filter($mail);

 my $folder = Mail::Folder->new( .... );
 my $filter->filter($folder);

=head1 DESCRIPTION

C<Mail::Filter> provides an interface to filtering Email through multiple
subroutines.

C<Mail::Filter> filters mail by calling each filter subroutine in turn. Each
filter subroutine is called with two arguments, the first is the filter
object and the second is the mail or folder object being filtered.

The result from each filter sub is passed to the next filter as the mail
object. If a filter subroutine returns undef, then C<Mail::Filter> will abort
and return immediately.

The function returns the result from the last subroutine to operate on the 
mail object.  

=head1 METHODS

=head2 Constructors

=over 4

=item Mail::Filter-E<gt>B<new>(@filters)

Create a new C<Mail::Filter> object with the given filter subroutines. Each
filter may be either a code reference or the name of a method to call
on the <Mail::Filter> object.

=back

=head2 Accessors

=over 4

=item $obj-E<gt>B<add>(@filters)

Add the given @filters to the end of the filter list.

=back

=head2 Processing

=over 4

=item $obj-E<gt>B<filter>($mail|$folder)

If the first argument is a L<Mail::Internet|Mail::Internet> object, then this object will
be passed through the filter list. If the first argument is a Mail::Folder
object, then each message in turn will be passed through the filter list.

=item $obj-E<gt>B<folder>()

While the L<filter()|Mail::Filter/"Processing"> method is called with a Mail::Folder object, these
filter subroutines can call this method to obtain the folder object that is
being processed.

=item $obj-E<gt>B<msgnum>()

If the L<filter()|Mail::Filter/"Processing"> method is called with a Mail::Folder object, then the
filter subroutines may call this method to obtain the message number
of the message that is being processed.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! W    	  Mailer.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Mailer;
use vars '$VERSION';
$VERSION = '2.21';

use base 'IO::Handle';

use strict;
use POSIX qw/_exit/;

use Carp;
use Config;

#--------------


sub is_exe($);

sub Version { our $VERSION }

our @Mailers =
  ( sendmail => '/usr/sbin/sendmail;/usr/lib/sendmail;/usr/ucblib/sendmail'
  , smtp     => undef
  , smtps    => undef
  , qmail    => '/usr/sbin/qmail-inject;/var/qmail/bin/qmail-inject'
  , testfile => undef
  );

push @Mailers, map { split /\:/, $_, 2 }
                   split /$Config{path_sep}/, $ENV{PERL_MAILERS}
    if $ENV{PERL_MAILERS};

our %Mailers = @Mailers;
our $MailerType;
our $MailerBinary;

# does this really need to be done? or should a default mailer be specified?

$Mailers{sendmail} = 'sendmail'
    if $^O eq 'os2' && ! is_exe $Mailers{sendmail};

if($^O =~ m/MacOS|VMS|MSWin|os2|NetWare/i )
{   $MailerType   = 'smtp';
    $MailerBinary = $Mailers{$MailerType};
}
else
{   for(my $i = 0 ; $i < @Mailers ; $i += 2)
    {   $MailerType = $Mailers[$i];
        if(my $binary = is_exe $Mailers{$MailerType})
        {   $MailerBinary = $binary;
            last;
        }
    }
}

sub import
{   shift;  # class
    @_ or return;

    my $type = shift;
    my $exe  = shift || $Mailers{$type};

    is_exe $exe
        or carp "Cannot locate '$exe'";

    $MailerType = $type;
    $Mailers{$MailerType} = $exe;
}

sub to_array($)
{   my ($self, $thing) = @_;
    ref $thing ? @$thing : $thing;
}

sub is_exe($)
{   my $exe = shift || '';

    foreach my $cmd (split /\;/, $exe)
    {   $cmd =~ s/^\s+//;

        # remove any options
        my $name = ($cmd =~ /^(\S+)/)[0];

        # check for absolute or relative path
        return $cmd
            if -x $name && ! -d $name && $name =~ m![\\/]!;

        if(defined $ENV{PATH})
        {   foreach my $dir (split /$Config{path_sep}/, $ENV{PATH})
            {   return "$dir/$cmd"
        	    if -x "$dir/$name" && ! -d "$dir/$name";
            }
        }
    }
    0;
}


sub new($@)
{   my ($class, $type, @args) = @_;

    unless($type)
    {   $MailerType or croak "No MailerType specified";

        warn "No real MTA found, using '$MailerType'"
             if $MailerType eq 'testfile';

        $type = $MailerType;
    }

    my $exe = $Mailers{$type};

    if(defined $exe)
    {   $exe   = is_exe $exe
            if defined $type;

        $exe ||= $MailerBinary
            or croak "No mailer type specified (and no default available), thus can not find executable program.";
    }

    $class = "Mail::Mailer::$type";
    eval "require $class" or die $@;

    my $glob = $class->SUPER::new;   # object is a GLOB!
    %{*$glob} = (Exe => $exe, Args => [ @args ]);
    $glob;
}


sub open($)
{   my ($self, $hdrs) = @_;
    my $exe    = *$self->{Exe};   # no exe, then direct smtp
    my $args   = *$self->{Args};

    my @to     = $self->who_to($hdrs);
    my $sender = $self->who_sender($hdrs);
    
    $self->close;	# just in case;

    if(defined $exe)
    {   # Fork and start a mailer
        my $child = open $self, '|-';
        defined $child or die "Failed to send: $!";

        if($child==0)
        {   # Child process will handle sending, but this is not real exec()
            # this is a setup!!!
            unless($self->exec($exe, $args, \@to, $sender))
            {   warn $!;     # setup failed
                _exit(1);    # no DESTROY(), keep it for parent
            }
        }
    }
    else
    {   # Sending is handled by a subclass
        $self->exec(undef, $args, \@to)
            or die $!;    
    }

    $self->set_headers($hdrs);
    $self;
}

sub _cleanup_hdrs($)
{   foreach my $h (values %{(shift)})
    {   foreach (ref $h ? @$h : $h)
        {   s/\n\s*/ /g;
            s/\s+$//;
        }
    }
}

sub exec($$$$)
{   my($self, $exe, $args, $to, $sender) = @_;

    # Fork and exec the mailer (no shell involved to avoid risks)
    my @exe = split /\s+/, $exe;
    exec @exe, @$args, @$to;
}

sub can_cc { 1 }	# overridden in subclass for mailer that can't

sub who_to($)
{   my($self, $hdrs) = @_;
    my @to = $self->to_array($hdrs->{To});
    unless($self->can_cc)  # Can't cc/bcc so add them to @to
    {   push @to, $self->to_array($hdrs->{Cc} ) if $hdrs->{Cc};
        push @to, $self->to_array($hdrs->{Bcc}) if $hdrs->{Bcc};
    }
    @to;
}

sub who_sender($)
{   my ($self, $hdrs) = @_;
    ($self->to_array($hdrs->{Sender} || $hdrs->{From}))[0];
}

sub epilogue {
    # This could send a .signature, also see ::smtp subclass
}

sub close(@)
{   my $self = shift;
    fileno $self or return;

    $self->epilogue;
    CORE::close $self;
}

sub DESTROY { shift->close }

#--------------

1;
PK       ! V[  [    Util.podnu [        =encoding utf8

=head1 NAME

Mail::Util - mail utility functions

=head1 INHERITANCE

 Mail::Util
   is a Exporter

=head1 SYNOPSIS

  use Mail::Util qw( ... );

=head1 DESCRIPTION

This package provides several mail related utility functions. Any function
required must by explicitly listed on the use line to be exported into
the calling package.

=head1 FUNCTIONS

=over 4

=item B<mailaddress>( [$address] )

Return a guess at the current users mail address. The user can force
the return value by setting the MAILADDRESS environment variable.
[2.10] You may set the $address via the parameter.

WARNING:
When not supplied via the environment variable, <mailaddress> looks at
various configuration files and other environmental data. Although this
seems to be smart behavior, this is not predictable enough (IMHO) to
be used.  Please set the MAILADDRESS explicitly, and do not trust on
the "automatic detection", even when that produces a correct address
(on the moment)

example: 

 # in your main script
 $ENV{MAILADDRESS} = 'me@example.com';

 # everywhere else
 use Mail::Util 'mailaddress';
 print mailaddress;

 # since v2.10
 mailaddress "me@example.com";

=item B<maildomain>()

Attempt to determine the current user mail domain string via the following
methods

=over 4

=item * Look for the MAILDOMAIN environment variable, which can be set from outside the program.  This is by far the best way to configure the domain.

=item * Look for a sendmail.cf file and extract DH parameter

=item * Look for a smail config file and usr the first host defined in hostname(s)

=item * Try an SMTP connect (if Net::SMTP exists) first to mailhost then localhost

=item * Use value from Net::Domain::domainname (if Net::Domain exists)

=back

WARNING:
On modern machines, there is only one good way to provide information to
this method: the first; always explicitly configure the MAILDOMAIN.

example: 

 # in your main script
 $ENV{MAILDOMAIN} = 'example.com';

 # everywhere else
 use Mail::Util 'maildomain';
 print maildomain;

=item B<read_mbox>($file)

Read $file, a binmail mailbox file, and return a list of  references.
Each reference is a reference to an array containing one message.

WARNING:
This method does not quote lines which accidentally also start with the
message separator C<From>, so this implementation can be considered
broken.  See Mail::Box::Mbox

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! ZAͱ      Send.podnu [        =encoding utf8

=head1 NAME

Mail::Send - Simple electronic mail interface

=head1 SYNOPSIS

  require Mail::Send;

  $msg = Mail::Send->new;
  $msg = Mail::Send->new(Subject => 'example', To => 'timbo');

  $msg->to('user@host');
  $msg->to('user@host', 'user2@example.com');
  $msg->subject('example subject');
  $msg->cc('user@host');
  $msg->bcc('someone@else');

  $msg->set($header, @values);
  $msg->add($header, @values);
  $msg->delete($header);

  # Launch mailer and set headers. The filehandle returned
  # by open() is an instance of the Mail::Mailer class.
  # Arguments to the open() method are passed to the Mail::Mailer
  # constructor.

  $fh = $msg->open;   # some default mailer
  $fh = $msg->open('sendmail'); # explicit
  print $fh "Body of message";
  $fh->close          # complete the message and send it
      or die "couldn't send whole message: $!\n";

=head1 DESCRIPTION

L<Mail::Send|Mail::Send> creates e-mail messages without using the L<Mail::Header|Mail::Header>
knowledge, which means that all escaping and folding must be done by
you!  Also: do not forget to escape leading dots.  Simplicity has its price.

When you have time, take a look at Mail::Transport which is part of
the MailBox suite.

=head1 METHODS

=head2 Constructors

=over 4

=item Mail::Send-E<gt>B<new>(PAIRS)

A list of header fields (provided as key-value PAIRS) can be used to
initialize the object, limited to the few provided as method: C<to>,
C<subject>, C<cc>, and C<bcc>.  For other header fields, use L<add()|Mail::Send/"Header fields">.

=back

=head2 Header fields

=over 4

=item $obj-E<gt>B<add>($fieldname, @values)

Add values to the list of defined values for the $fieldname.

=item $obj-E<gt>B<bcc>(@values)

=item $obj-E<gt>B<cc>(@values)

=item $obj-E<gt>B<delete>($fieldname)

=item $obj-E<gt>B<set>($fieldname, @values)

The @values will replace the old values for the $fieldname.  Returned is
the LIST of values after modification.

=item $obj-E<gt>B<subject>(@values)

=item $obj-E<gt>B<to>(@values)

=back

=head2 Sending

=over 4

=item $obj-E<gt>B<open>(%options)

The %options are used to initiate a mailer object via
L<Mail::Mailer::new()|Mail::Mailer/"Constructors">.  Then L<Mail::Mailer::open()|Mail::Mailer/"Constructors"> is called
with the knowledge collected in this C<Mail::Send> object.

Be warned: this module implements raw smtp, which means that you have
to escape lines which start with a dot, by adding one in front.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! G    	  Field.podnu [        =encoding utf8

=head1 NAME

Mail::Field - base-class for manipulation of mail header fields

=head1 INHERITANCE

 Mail::Field is extended by
   Mail::Field::AddrList
   Mail::Field::Date
   Mail::Field::Generic

=head1 SYNOPSIS

 use Mail::Field;
    
 my $field = Mail::Field->new('Subject', 'some subject text');
 my $field = Mail::Field->new(Subject => 'some subject text');
 print $field->tag,": ",$field->stringify,"\n";

 my $field = Mail::Field->subject('some subject text');

=head1 DESCRIPTION

C<Mail::Field> creates and manipulates fields in MIME headers, collected
within a L<Mail::Header|Mail::Header> object.  Different field types have their
own sub-class (extension), defining additional useful accessors to the
field content.

People are invited to merge their implementation to special fields into
MailTools, to maintain a consistent set of packages and documentation.

=head1 METHODS

=head2 Constructors

Mail::Field (and it's sub-classes) define several methods which return
new objects. These can all be categorized as constructor.

=over 4

=item Mail::Field-E<gt>B<combine>($fields)

Take a LIST of C<Mail::Field> objects (which should all be of the same
sub-class) and create a new object in that same class.

=item Mail::Field-E<gt>B<extract>( $tag, $head [, $index ] )

Takes as arguments the tag name, a C<Mail::Head> object
and optionally an index.

If the index argument is given then C<extract> will retrieve the given tag
from the C<Mail::Head> object and create a new C<Mail::Field> based object.
I<undef> will be returned in the field does not exist.

If the index argument is not given the result depends on the context
in which C<extract> is called. If called in a scalar context the result
will be as if C<extract> was called with an index value of zero. If called
in an array context then all tags will be retrieved and a list of
C<Mail::Field> objects will be returned.

=item Mail::Field-E<gt>B<new>( $tag [, STRING | %options] )

Create an object in the class which defines the field specified by
the $tag argument.

=back

=head2 "Fake" constructors

=over 4

=item $obj-E<gt>B<create>(%options)

This constructor is used internally with preprocessed field information.
When called on an existing object, its original content will get
replaced.

=item $obj-E<gt>B<parse>()

Parse a field line.

=back

=head2 Accessors

=over 4

=item $obj-E<gt>B<set>(%options)

Change the settings (the content, but then smart) of this field.

=item $obj-E<gt>B<stringify>()

Returns the field as a string.

=item $obj-E<gt>B<tag>()

=item Mail::Field-E<gt>B<tag>()

Return the tag (in the correct case) for this item.  Well, actually any
casing is OK, because the field tags are treated case-insensitive; however
people have some preferences.

=back

=head2 Smart accessors

=over 4

=item $obj-E<gt>B<text>( [STRING] )

Without arguments, the field is returned as L<stringify()|Mail::Field/"Accessors"> does.  Otherwise,
the STRING is parsed with L<parse()|Mail::Field/""Fake" constructors"> to replace the object's content.

It is more clear to call either L<stringify()|Mail::Field/"Accessors"> or L<parse()|Mail::Field/""Fake" constructors"> directly, because
this method does not add additional processing.

=back

=head1 DETAILS

=head2 SUB-CLASS PACKAGE NAMES

All sub-classes should be called Mail::Field::I<name> where I<name> is
derived from the tag using these rules.

=over 4

=item *

Consider a tag as being made up of elements separated by '-'

=item *

Convert all characters to lowercase except the first in each element, which
should be uppercase.

=item *

I<name> is then created from these elements by using the first
N characters from each element.

=item *

N is calculated by using the formula :-

    int((7 + #elements) / #elements)

=item *

I<name> is then limited to a maximum of 8 characters, keeping the first 8
characters.

=back

For an example of this take a look at the definition of the 
C<_header_pkg_name()> subroutine in C<Mail::Field>

=head1 DIAGNOSTICS

=over 4

=item Error: Undefined subroutine <method> called

Mail::Field objects use autoloading to compile new functionality.
Apparently, the method called is not implemented for the specific
class of the field object.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! @*1  1    Internet.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Internet;
use vars '$VERSION';
$VERSION = '2.21';

use strict;
# use warnings?  probably breaking too much code

use Carp;
use Mail::Header;
use Mail::Util    qw/mailaddress/;
use Mail::Address;


sub new(@)
{   my $call  = shift;
    my $arg   = @_ % 2 ? shift : undef;
    my %opt   = @_;

    my $class = ref($call) || $call;
    my $self  = bless {}, $class;

    $self->{mail_inet_head} = $opt{Header} if exists $opt{Header};
    $self->{mail_inet_body} = $opt{Body}   if exists $opt{Body};

    my $head = $self->head;
    $head->fold_length(delete $opt{FoldLength} || 79);
    $head->mail_from($opt{MailFrom}) if exists $opt{MailFrom};
    $head->modify(exists $opt{Modify} ? $opt{Modify} : 1);

    if(!defined $arg) { }
    elsif(ref($arg) eq 'ARRAY')
    {   $self->header($arg) unless exists $opt{Header};
        $self->body($arg)   unless exists $opt{Body};
    }
    elsif(defined fileno($arg))
    {   $self->read_header($arg) unless exists $opt{Header};
        $self->read_body($arg)   unless exists $opt{Body};
    }
    else
    {   croak "couldn't understand $arg to Mail::Internet constructor";
    }

    $self;
}


sub read(@)
{   my $self = shift;
    $self->read_header(@_);
    $self->read_body(@_);
}

sub read_body($)
{   my ($self, $fd) = @_;
    $self->body( [ <$fd> ] );
}

sub read_header(@)
{   my $head = shift->head;
    $head->read(@_);
    $head->header;
}


sub extract($)
{   my ($self, $lines) = @_;
    $self->head->extract($lines);
    $self->body($lines);
}


sub dup()
{   my $self = shift;
    my $dup  = ref($self)->new;

    my $body = $self->{mail_inet_body} || [];
    my $head = $self->{mail_inet_head};;

    $dup->{mail_inet_body} = [ @$body ];
    $dup->{mail_inet_head} = $head->dup if $head;
    $dup;
}

#---------------

sub body(;$@)
{   my $self = shift;

    return $self->{mail_inet_body} ||= []
        unless @_;

    $self->{mail_inet_body} = ref $_[0] eq 'ARRAY' ? $_[0] : [ @_ ];
}


sub head         { shift->{mail_inet_head} ||= Mail::Header->new }

#---------------

sub print($)
{   my $self = shift;
    my $fd   = shift || \*STDOUT;

    $self->print_header($fd)
       and print $fd "\n"
       and $self->print_body($fd);
}


sub print_header($) { shift->head->print(@_) }

sub print_body($)
{   my $self = shift;
    my $fd   = shift || \*STDOUT;

    foreach my $ln (@{$self->body})
    {    print $fd $ln or return 0;
    }

    1;
}


sub as_string()
{   my $self = shift;
    $self->head->as_string . "\n" . join '', @{$self->body};
}


sub as_mbox_string($)
{   my $self    = shift->dup;
    my $escaped = shift;

    $self->head->delete('Content-Length');
    $self->escape_from unless $escaped;
    $self->as_string . "\n";
}

#---------------

sub header       { shift->head->header(@_) }
sub fold         { shift->head->fold(@_) }
sub fold_length  { shift->head->fold_length(@_) }
sub combine      { shift->head->combine(@_) }


sub add(@)
{   my $head = shift->head;
    my $ret;
    while(@_)
    {   my ($tag, $line) = splice @_, 0, 2;
        $ret = $head->add($tag, $line, -1)
            or return undef;
    }

    $ret;
}


sub replace(@)
{   my $head = shift->head;
    my $ret;

    while(@_)
    {   my ($tag, $line) = splice @_, 0, 2;
        $ret = $head->replace($tag, $line, 0)
             or return undef;
    }

    $ret;
}


sub get(@)
{   my $head = shift->head;

    return map { $head->get($_) } @_
        if wantarray;

    foreach my $tag (@_)
    {   my $r = $head->get($tag);
        return $r if defined $r;
    }

    undef;
}


sub delete(@)
{   my $head = shift->head;
    map { $head->delete($_) } @_;
}

# Undocumented; unused???
sub empty()
{   my $self = shift;
    %$self = ();
    1;
}

#---------------

sub remove_sig($)
{   my $body   = shift->body;
    my $nlines = shift || 10;
    my $start  = @$body;

    my $i    = 0;
    while($i++ < $nlines && $start--)
    {   next if $body->[$start] !~ /^--[ ]?[\r\n]/;

        splice @$body, $start, $i;
        last;
    }
}


sub sign(@)
{   my ($self, %arg) = @_;
    my ($sig, @sig);

    if($sig = delete $arg{File})
    {   local *SIG;

        if(open(SIG, $sig))
        {   local $_;
            while(<SIG>) { last unless /^(--)?\s*$/ }
            @sig = ($_, <SIG>, "\n");
            close SIG;
        }
    }
    elsif($sig = delete $arg{Signature})
    {    @sig = ref($sig) ? @$sig : split(/\n/, $sig);
    }

    if(@sig)
    {   $self->remove_sig;
        s/[\r\n]*$/\n/ for @sig;
        push @{$self->body}, "-- \n", @sig;
    }

    $self;
}


sub tidy_body()
{   my $body = shift->body;

    shift @$body while @$body && $body->[0]  =~ /^\s*$/;
    pop @$body   while @$body && $body->[-1] =~ /^\s*$/;
    $body;
}

#---------------

sub reply(@)
{   my ($self, %arg) = @_;
    my $class = ref $self;
    my @reply;

    local *MAILHDR;
    if(open(MAILHDR, "$ENV{HOME}/.mailhdr")) 
    {    # User has defined a mail header template
         @reply = <MAILHDR>;
         close MAILHDR;
    }

    my $reply = $class->new(\@reply);

    # The Subject line
    my $subject = $self->get('Subject') || "";
    $subject = "Re: " . $subject
        if $subject =~ /\S+/ && $subject !~ /Re:/i;

    $reply->replace(Subject => $subject);

    # Locate who we are sending to
    my $to = $self->get('Reply-To')
          || $self->get('From')
          || $self->get('Return-Path')
          || "";

    my $sender = (Mail::Address->parse($to))[0];

    my $name = $sender->name;
    unless(defined $name)
    {    my $fr = $self->get('From');
         $fr    = (Mail::Address->parse($fr))[0] if defined $fr;
         $name  = $fr->name if defined $fr;
    }

    my $indent = $arg{Indent} || ">";
    if($indent =~ /\%/) 
    {   my %hash = ( '%' => '%');
        my @name = $name ? grep( {length $_} split /[\n\s]+/, $name) : '';

        $hash{f} = $name[0];
        $hash{F} = $#name ? substr($hash{f},0,1) : $hash{f};

        $hash{l} = $#name ? $name[$#name] : "";
        $hash{L} = substr($hash{l},0,1) || "";

        $hash{n} = $name || "";
        $hash{I} = join "", map {substr($_,0,1)} @name;

        $indent  =~ s/\%(.)/defined $hash{$1} ? $hash{$1} : $1/eg;
    }

    my $id     = $sender->address;
    $reply->replace(To => $id);

    # Find addresses not to include
    my $mailaddresses = $ENV{MAILADDRESSES} || "";

    my %nocc = (lc($id) => 1);
    $nocc{lc $_->address} = 1
        for Mail::Address->parse($reply->get('Bcc'), $mailaddresses);

    if($arg{ReplyAll})   # Who shall we copy this to
    {   my %cc;
        foreach my $addr (Mail::Address->parse($self->get('To'), $self->get('Cc'))) 
        {   my $lc   = lc $addr->address;
            $cc{$lc} = $addr->format
                 unless $nocc{$lc};
        }
        my $cc = join ', ', values %cc;
        $reply->replace(Cc => $cc);
    }

    # References
    my $refs    = $self->get('References') || "";
    my $mid     = $self->get('Message-Id');

    $refs      .= " " . $mid if defined $mid;
    $reply->replace(References => $refs);

    # In-Reply-To
    my $date    = $self->get('Date');
    my $inreply = "";

    if(defined $mid)
    {    $inreply  = $mid;
         my @comment;
         push @comment, "from $name" if defined $name;
         push @comment, "on $date"   if defined $date;
         local $"  = ' ';
         $inreply .= " (@comment)"   if @comment;
    }
    elsif(defined $name)
    {    $inreply  = $name    . "'s message";
         $inreply .= "of "    . $date if defined $date;
    }
    $reply->replace('In-Reply-To' => $inreply);

    # Quote the body
    my $body  = $reply->body;
    @$body = @{$self->body};    # copy body
    $reply->remove_sig;
    $reply->tidy_body;
    s/\A/$indent/ for @$body;

    # Add references
    unshift @{$body}, (defined $name ? $name . " " : "") . "<$id> writes:\n";

    if(defined $arg{Keep} && ref $arg{Keep} eq 'ARRAY')      # Include lines
    {   foreach my $keep (@{$arg{Keep}}) 
        {    my $ln = $self->get($keep);
             $reply->replace($keep => $ln) if defined $ln;
        }
    }

    if(defined $arg{Exclude} && ref $arg{Exclude} eq 'ARRAY') # Exclude lines
    {    $reply->delete(@{$arg{Exclude}});
    }

    $reply->head->cleanup;      # remove empty header lines
    $reply;
}


sub smtpsend($@)
{   my ($self, %opt) = @_;

    require Net::SMTP;
    require Net::Domain;

    my $host     = $opt{Host};
    my $envelope = $opt{MailFrom} || mailaddress();
    my $quit     = 1;

    my ($smtp, @hello);

    push @hello, Hello => $opt{Hello}
        if defined $opt{Hello};

    push @hello, Port => $opt{Port}
	if exists $opt{Port};

    push @hello, Debug => $opt{Debug}
	if exists $opt{Debug};

    if(!defined $host)
    {   local $SIG{__DIE__};
	my @hosts = qw(mailhost localhost);
	unshift @hosts, split /\:/, $ENV{SMTPHOSTS}
            if defined $ENV{SMTPHOSTS};

	foreach $host (@hosts)
        {   $smtp = eval { Net::SMTP->new($host, @hello) };
	    last if defined $smtp;
	}
    }
    elsif(UNIVERSAL::isa($host,'Net::SMTP')
       || UNIVERSAL::isa($host,'Net::SMTP::SSL'))
    {   $smtp = $host;
	$quit = 0;
    }
    else
    {   local $SIG{__DIE__};
	$smtp = eval { Net::SMTP->new($host, @hello) };
    }

    defined $smtp or return ();

    my $head = $self->cleaned_header_dup;

    # Who is it to

    my @rcpt = map { ref $_ ? @$_ : $_ } grep { defined } @opt{'To','Cc','Bcc'};
    @rcpt    = map { $head->get($_) } qw(To Cc Bcc)
	unless @rcpt;

    my @addr = map {$_->address} Mail::Address->parse(@rcpt);
    @addr or return ();

    $head->delete('Bcc');

    # Send it

    my $ok = $smtp->mail($envelope)
          && $smtp->to(@addr)
          && $smtp->data(join("", @{$head->header}, "\n", @{$self->body}));

    $quit && $smtp->quit;
    $ok ? @addr : ();
}


sub send($@)
{   my ($self, $type, @args) = @_;

    require Mail::Mailer;

    my $head  = $self->cleaned_header_dup;
    my $mailer = Mail::Mailer->new($type, @args);

    $mailer->open($head->header_hashref);
    $self->print_body($mailer);
    $mailer->close;
}


sub nntppost
{   my ($self, %opt) = @_;

    require Net::NNTP;

    my $groups = $self->get('Newsgroups') || "";
    my @groups = split /[\s,]+/, $groups;
    @groups or return ();

    my $head   = $self->cleaned_header_dup;

    # Remove these incase the NNTP host decides to mail as well as me
    $head->delete(qw(To Cc Bcc)); 

    my $news;
    my $quit   = 1;

    my $host   = $opt{Host};
    if(ref($host) && UNIVERSAL::isa($host,'Net::NNTP'))
    {   $news = $host;
	$quit = 0;
    }
    else
    {   my @opt = $opt{Host};

	push @opt, Port => $opt{Port}
	    if exists $opt{Port};

	push @opt, Debug => $opt{Debug}
	    if exists $opt{Debug};

	$news = Net::NNTP->new(@opt)
	    or return ();
    }

    $news->post(@{$head->header}, "\n", @{$self->body});
    my $rc = $news->code;

    $news->quit if $quit;

    $rc == 240 ? @groups : ();
}


sub escape_from
{   my $body = shift->body;
    scalar grep { s/\A(>*From) />$1 /o } @$body;
}



sub unescape_from
{   my $body = shift->body;
    scalar grep { s/\A>(>*From) /$1 /o } @$body;
}

# Don't tell people it exists
sub cleaned_header_dup()
{   my $head = shift->head->dup;

    $head->delete('From '); # Just in case :-)

    # An original message should not have any Received lines
    $head->delete('Received');

    $head->replace('X-Mailer', "Perl5 Mail::Internet v".$Mail::Internet::VERSION)
        unless $head->count('X-Mailer');

    my $name = eval {local $SIG{__DIE__}; (getpwuid($>))[6]} || $ENV{NAME} ||"";

    while($name =~ s/\([^\(\)]*\)//) { 1; }

    # Strip extra fields: adduser-generated usernames have multiple comma
    # seperated fields, only the first of which should be used to prevent
    # accidental exposure of system-local information like phone numbers/
    # room numbers.
    $name = (split /,/, $name)[0] if $name ne "";

    if($name =~ /[^\w\s]/)
    {   $name =~ s/"/\"/g;
	$name = '"' . $name . '"';
    }

    my $from = sprintf "%s <%s>", $name, mailaddress();
    $from =~ s/\s{2,}/ /g;

    foreach my $tag (qw(From Sender))
    {   $head->get($tag) or $head->add($tag, $from);
    }

    $head;
}

1;
PK       ! j      Mailer/sendmail.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Mailer::sendmail;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Mailer::rfc822';

use strict;

sub exec($$$$)
{   my($self, $exe, $args, $to, $sender) = @_;
    # Fork and exec the mailer (no shell involved to avoid risks)

    # We should always use a -t on sendmail so that Cc: and Bcc: work
    #  Rumor: some sendmails may ignore or break with -t (AIX?)
    # Chopped out the @$to arguments, because -t means
    # they are sent in the body, and postfix complains if they
    # are also given on command line.

    exec( $exe, '-t', @$args );
}

1;
PK       ! k	  	    Mailer/smtps.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

# Based on smtp.pm, adapted by Maciej Żenczykowski

package Mail::Mailer::smtps;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Mailer::rfc822';

use strict;

use Net::SMTP::SSL;
use Mail::Util qw(mailaddress);
use Carp;

sub can_cc { 0 }

sub exec {
    my ($self, $exe, $args, $to) = @_;
    my %opt   = @$args;
    my $host  = $opt{Server} || undef;
    $opt{Debug} ||= 0;
    $opt{Port}  ||= 465;

    my $smtp = Net::SMTP::SSL->new($host, %opt)
	or return undef;

    if($opt{Auth})
    {   $smtp->auth(@{$opt{Auth}})
           or return undef;
    }

    ${*$self}{sock} = $smtp;

    $smtp->mail($opt{From} || mailaddress);
    $smtp->to($_) for @$to;
    $smtp->data;

    untie *$self if tied *$self;
    tie *$self, 'Mail::Mailer::smtps::pipe', $self;
    $self;
}

sub set_headers($)
{   my ($self, $hdrs) = @_;
    $self->SUPER::set_headers
     ( { From => "<" . mailaddress() . ">"
       , %$hdrs
       , 'X-Mailer' => "Mail::Mailer[v$Mail::Mailer::VERSION] "
           . " Net::SMTP[v$Net::SMTP::VERSION]"
           . " Net::SMTP::SSL[v$Net::SMTP::SSL::VERSION]"
       }
     );
}

sub epilogue()
{   my $self = shift;
    my $sock = ${*$self}{sock};

    my $ok = $sock->dataend;
    $sock->quit;

    delete ${*$self}{sock};
    untie *$self;
    $ok;
}

sub close(@)
{   my ($self, @to) = @_;
    my $sock = ${*$self}{sock};

    $sock && fileno $sock
        or return 1;

    my $ok = $self->epilogue;

    # Epilogue should destroy the SMTP filehandle,
    # but just to be on the safe side.
    $sock && fileno $sock
        or return $ok;

    close $sock
        or croak 'Cannot destroy socket filehandle';

    $ok;
}

package Mail::Mailer::smtps::pipe;
use vars '$VERSION';
$VERSION = '2.21';


sub TIEHANDLE
{   my ($class, $self) = @_;
    my $sock = ${*$self}{sock};
    bless \$sock, $class;
}

sub PRINT
{   my $self = shift;
    my $sock = $$self;
    $sock->datasend( @_ );
}

1;
PK       ! ~)  )    Mailer/rfc822.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Mailer::rfc822;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Mailer';

use strict;

sub set_headers
{   my ($self, $hdrs) = @_;

    local $\ = "";

    foreach (keys %$hdrs)
    {   next unless m/^[A-Z]/;

        foreach my $h ($self->to_array($hdrs->{$_}))
        {   $h =~ s/\n+\Z//;
            print $self "$_: $h\n";
        }
    }

    print $self "\n";	# terminate headers
}

1;
PK       ! ڰ&	  &	    Mailer/smtp.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Mailer::smtp;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Mailer::rfc822';

use strict;

use Net::SMTP;
use Mail::Util qw(mailaddress);
use Carp;

sub can_cc { 0 }

sub exec {
    my ($self, $exe, $args, $to) = @_;
    my %opt   = @$args;
    my $host  = $opt{Server} || undef;
    $opt{Debug} ||= 0;

    my $smtp = Net::SMTP->new($host, %opt)
        or return undef;

    if($opt{StartTLS})
    {   $Net::SMTP::VERSION >= 1.28
            or die "StartTLS requires Net::SMTP 1.28";

        $smtp->starttls
            or return undef;
    }

    if($opt{Auth})
    {   $smtp->auth(@{$opt{Auth}})
           or return undef;
    }

    ${*$self}{sock} = $smtp;

    $smtp->mail($opt{From} || mailaddress());
    $smtp->to($_) for @$to;
    $smtp->data;

    untie *$self if tied *$self;
    tie *$self, 'Mail::Mailer::smtp::pipe', $self;
    $self;
}

sub set_headers($)
{   my ($self, $hdrs) = @_;
    $self->SUPER::set_headers
     ( { From => "<" . mailaddress() . ">"
       , %$hdrs
       , 'X-Mailer' => "Mail::Mailer[v$Mail::Mailer::VERSION] Net::SMTP[v$Net::SMTP::VERSION]"
       }
     );
}

sub epilogue()
{   my $self = shift;
    my $sock = ${*$self}{sock};

    my $ok = $sock->dataend;
    $sock->quit;

    delete ${*$self}{sock};
    untie *$self;
    $ok;
}

sub close(@)
{   my ($self, @to) = @_;
    my $sock = ${*$self}{sock};

    $sock && fileno $sock
        or return 1;

    my $ok = $self->epilogue;

    # Epilogue should destroy the SMTP filehandle,
    # but just to be on the safe side.
    $sock && fileno $sock
        or return $ok;

    close $sock
        or croak 'Cannot destroy socket filehandle';

    $ok;
}

package Mail::Mailer::smtp::pipe;
use vars '$VERSION';
$VERSION = '2.21';


sub TIEHANDLE
{   my ($class, $self) = @_;
    my $sock = ${*$self}{sock};
    bless \$sock, $class;
}

sub PRINT
{   my $self = shift;
    my $sock = $$self;
    $sock->datasend( @_ );
}

1;
PK       ! ]      Mailer/qmail.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Mailer::qmail;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Mailer::rfc822';

use strict;

sub exec($$$$)
{   my($self, $exe, $args, $to, $sender) = @_;
    my $address = defined $sender && $sender =~ m/\<(.*?)\>/ ? $1 : $sender;

    exec($exe, (defined $address ? "-f$address" : ()));
    die "ERROR: cannot run $exe: $!";
}

1;
PK       ! Sqlh  h    Mailer/testfile.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Mailer::testfile;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Mail::Mailer::rfc822';

use strict;

use Mail::Util qw/mailaddress/;

my $num = 0;
sub can_cc() { 0 }

sub exec($$$)
{   my ($self, $exe, $args, $to) = @_;

    my $outfn = $Mail::Mailer::testfile::config{outfile} || 'mailer.testfile';
    open F, '>>', $outfn
        or die "Cannot append message to testfile $outfn: $!";

    print F "\n===\ntest ", ++$num, " ", (scalar localtime),
            "\nfrom: " . mailaddress(),
            "\nto: " . join(' ',@{$to}), "\n\n";
    close F;

    untie *$self if tied *$self;
    tie *$self, 'Mail::Mailer::testfile::pipe', $self;
    $self;
}

sub close { 1 }

package Mail::Mailer::testfile::pipe;
use vars '$VERSION';
$VERSION = '2.21';


sub TIEHANDLE
{   my ($class, $self) = @_;
    bless \$self, $class;
}

sub PRINT
{   my $self = shift;
    open F, '>>', $Mail::Mailer::testfile::config{outfile} || 'mailer.testfile';
    print F @_;
    close F;
}

1;
PK       ! [;<    
  Header.podnu [        =encoding utf8

=head1 NAME

Mail::Header - manipulate MIME headers

=head1 SYNOPSIS

 use Mail::Header;
    
 my $head = Mail::Header->new;
 my $head = Mail::Header->new( \*STDIN );
 my $head = Mail::Header->new( [<>], Modify => 0);

=head1 DESCRIPTION

Read, write, create, and manipulate MIME headers, the leading part
of each modern e-mail message, but also used in other protocols
like HTTP.  The fields are kept in L<Mail::Field|Mail::Field> objects.

Be aware that the header fields each have a name part, which shall
be treated case-insensitive, and a content part, which may be folded
over multiple lines.  

Mail::Header does not always follow the RFCs strict enough, does not
help you with character encodings.  It does not use weak references
where it could (because those did not exist when the module was written)
which costs some performance and make the implementation a little more
complicated.  The Mail::Message::Head implementation is much newer
and therefore better.

=head1 METHODS

=head2 Constructors

=over 4

=item $obj-E<gt>B<dup>()

Create a duplicate of the current object.

=item $obj-E<gt>B<new>( [$source], [%options] )

=item Mail::Header-E<gt>B<new>( [$source], [%options] )

The $source may be either a file descriptor (reference to a GLOB)
or a reference to an array. If given the new object will be
initialized with headers either from the array of read from 
the file descriptor.

%options is a list of options given in the form of key-value
pairs, just like a hash table. Valid options are

 -Option    --Default
  FoldLength  79
  MailFrom    'KEEP'
  Modify      false

=over 2

=item FoldLength => INTEGER

The default length of line to be used when folding header lines.
See L<fold_length()|Mail::Header/"Accessors">.

=item MailFrom => 'IGNORE'|'COERCE'|'KEEP'|'ERROR'

See method L<mail_from()|Mail::Header/"Accessors">.

=item Modify => BOOLEAN

If this value is I<true> then the headers will be re-formatted,
otherwise the format of the header lines will remain unchanged.

=back

=back

=head2 "Fake" constructors

Be warned that the next constructors all require an already created
header object, of which the original content will be destroyed.

=over 4

=item $obj-E<gt>B<empty>()

Empty an existing C<Mail::Header> object of all lines.

=item $obj-E<gt>B<extract>(ARRAY)

Extract a header from the given array into an existing Mail::Header
object. C<extract> B<will modify> this array.
Returns the object that the method was called on.

=item $obj-E<gt>B<header>( [ARRAY] )

C<header> does multiple operations. First it will extract a header from
the ARRAY, if given. It will then reformat the header (if reformatting
is permitted), and finally return a reference to an array which
contains the header in a printable form.

=item $obj-E<gt>B<header_hashref>( [HASH] )

As L<header()|Mail::Header/""Fake" constructors">, but it will eventually set headers from a hash
reference, and it will return the headers as a hash reference.

example: 

 $fields->{From} = 'Tobias Brox <tobix@cpan.org>';
 $fields->{To}   = ['you@somewhere', 'me@localhost'];
 $head->header_hashref($fields);

=item $obj-E<gt>B<read>($fh)

Read a header from the given file descriptor into an existing Mail::Header
object.

=back

=head2 Accessors

=over 4

=item $obj-E<gt>B<fold_length>( [$tag], [$length] )

Set the default fold length for all tags or just one. With no arguments
the default fold length is returned. With two arguments it sets the fold
length for the given tag and returns the previous value. If only C<$length>
is given it sets the default fold length for the current object.

In the two argument form C<fold_length> may be called as a static method,
setting default fold lengths for tags that will be used by B<all>
C<Mail::Header> objects. See the C<fold> method for
a description on how C<Mail::Header> uses these values.

=item $obj-E<gt>B<mail_from>('IGNORE'|'COERCE'|'KEEP'|'ERROR')

This specifies what to do when a C<`From '> line is encountered.
Valid values are C<IGNORE> - ignore and discard the header,
C<ERROR> - invoke an error (call die), C<COERCE> - rename them as Mail-From
and C<KEEP> - keep them.

=item $obj-E<gt>B<modify>( [$value] )

If C<$value> is I<false> then C<Mail::Header> will not do any automatic
reformatting of the headers, other than to ensure that the line
starts with the tags given.

=back

=head2 Processing

=over 4

=item $obj-E<gt>B<add>( $tag, $line [, $index] )

Add a new line to the header. If $tag is C<undef> the tag will be
extracted from the beginning of the given line. If $index is given,
the new line will be inserted into the header at the given point, otherwise
the new line will be appended to the end of the header.

=item $obj-E<gt>B<as_string>()

Returns the header as a single string.

=item $obj-E<gt>B<cleanup>()

Remove any header line that, other than the tag, only contains whitespace

=item $obj-E<gt>B<combine>( $tag [, $with] )

Combine all instances of $tag into one. The lines will be
joined together $with, or a single space if not given. The new
item will be positioned in the header where the first instance was, all
other instances of $tag will be removed.

=item $obj-E<gt>B<count>($tag)

Returns the number of times the given atg appears in the header

=item $obj-E<gt>B<delete>( $tag [, $index ] )

Delete a tag from the header. If an $index id is given, then the Nth instance
of the tag will be removed. If no $index is given, then all instances
of tag will be removed.

=item $obj-E<gt>B<fold>( [$length] )

Fold the header. If $length is not given, then C<Mail::Header> uses the
following rules to determine what length to fold a line.

=item $obj-E<gt>B<get>( $tag [, $index] )

Get the text from a line. If an $index is given, then the text of the Nth
instance will be returned. If it is not given the return value depends on the
context in which C<get> was called. In an array context a list of all the
text from all the instances of the $tag will be returned. In a scalar context
the text for the first instance will be returned.

The lines are unfolded, but still terminated with a new-line (see C<chomp>)

=item $obj-E<gt>B<print>( [$fh] )

Print the header to the given file descriptor, or C<STDOUT> if no
file descriptor is given.

=item $obj-E<gt>B<replace>( $tag, $line [, $index ] )

Replace a line in the header.  If $tag is C<undef> the tag will be
extracted from the beginning of the given line. If $index is given
the new line will replace the Nth instance of that tag, otherwise the
first instance of the tag is replaced. If the tag does not appear in the
header then a new line will be appended to the header.

=item $obj-E<gt>B<tags>()

Returns an array of all the tags that exist in the header. Each tag will
only appear in the list once. The order of the tags is not specified.

=item $obj-E<gt>B<unfold>( [$tag] )

Unfold all instances of the given tag so that they do not spread across
multiple lines. If C<$tag> is not given then all lines are unfolded.

The unfolding process is wrong but (for compatibility reasons) will
not be repaired: only one blank at the start of the line should be
removed, not all of them.

=back

=head1 SEE ALSO

This module is part of the MailTools distribution,
F<http://perl.overmeer.net/mailtools/>.

=head1 AUTHORS

The MailTools bundle was developed by Graham Barr.  Later, Mark
Overmeer took over maintenance without commitment to further development.

Mail::Cap by Gisle Aas E<lt>aas@oslonett.noE<gt>.
Mail::Field::AddrList by Peter Orbaek E<lt>poe@cit.dkE<gt>.
Mail::Mailer and Mail::Send by Tim Bunce E<lt>Tim.Bunce@ig.co.ukE<gt>.
For other contributors see ChangeLog.

=head1 LICENSE

Copyrights 1995-2000 Graham Barr E<lt>gbarr@pobox.comE<gt> and
2001-2017 Mark Overmeer E<lt>perl@overmeer.netE<gt>.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See F<http://www.perl.com/perl/misc/Artistic.html>

PK       ! p  p    Util.pmnu [        # Copyrights 1995-2019 by [Mark Overmeer <markov@cpan.org>].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of the bundle MailTools.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md for Copyright.
# Licensed under the same terms as Perl itself.

package Mail::Util;
use vars '$VERSION';
$VERSION = '2.21';

use base 'Exporter';

use strict;
use Carp;

our @EXPORT_OK = qw(read_mbox maildomain mailaddress);

sub Version { our $VERSION }

my ($domain, $mailaddress);
my @sendmailcf = qw(/etc /etc/sendmail /etc/ucblib
    /etc/mail /usr/lib /var/adm/sendmail);


sub read_mbox($)
{   my $file  = shift;

    local *FH;
    open FH,'<', $file
	or croak "cannot open '$file': $!\n";

    local $_;
    my @mbox;
    my $mail  = [];
    my $blank = 1;

    while(<FH>)
    {   if($blank && /^From .*\d{4}/)
        {   push @mbox, $mail if @$mail;
            $mail  = [ $_ ];
            $blank = 0;
        }
        else
        {   $blank = m/^$/ ? 1 : 0;
            push @$mail, $_;
        }
    }

    push @mbox, $mail if @$mail;
    close FH;

    wantarray ? @mbox : \@mbox;
}


sub maildomain()
{   return $domain
	if defined $domain;

    $domain = $ENV{MAILDOMAIN}
        and return $domain;

    # Try sendmail configuration file

    my $config = (grep -r, map {"$_/sendmail.cf"} @sendmailcf)[0];

    local *CF;
    local $_;
    if(defined $config && open CF, '<', $config)
    {   my %var;
	while(<CF>)
        {   if(my ($v, $arg) = /^D([a-zA-Z])([\w.\$\-]+)/)
            {   $arg =~ s/\$([a-zA-Z])/exists $var{$1} ? $var{$1} : '$'.$1/eg;
		$var{$v} = $arg;
	    }
	}
	close CF;
	$domain = $var{j} if defined $var{j};
	$domain = $var{M} if defined $var{M};

        $domain = $1
            if $domain && $domain =~ m/([A-Za-z0-9](?:[\.\-A-Za-z0-9]+))/;

	return $domain
	    if defined $domain && $domain !~ /\$/;
    }

    # Try smail config file if exists

    if(open CF, '<', "/usr/lib/smail/config")
    {   while(<CF>)
        {   if( /\A\s*hostnames?\s*=\s*(\S+)/ )
            {   $domain = (split /\:/,$1)[0];
		last;
	    }
	}
	close CF;

	return $domain
	    if defined $domain;
    }

    # Try a SMTP connection to 'mailhost'

    if(eval {require Net::SMTP})
    {   foreach my $host (qw(mailhost localhost))
        {   # hosts are local, so short timeout
            my $smtp = eval { Net::SMTP->new($host, Timeout => 5) };
	    if(defined $smtp)
            {   $domain = $smtp->domain;
		$smtp->quit;
		last;
	    }
	}
    }

    # Use internet(DNS) domain name, if it can be found
    $domain = Net::Domain::domainname()
        if !defined $domain && eval {require Net::Domain};

    $domain ||= "localhost";
}


sub mailaddress(;$)
{   $mailaddress = shift if @_;

    return $mailaddress
        if defined $mailaddress;

    # Get user name from environment
    $mailaddress = $ENV{MAILADDRESS};

    unless($mailaddress || $^O ne 'MacOS')
    {   require Mac::InternetConfig;

        no strict;
	Mac::InternetConfig->import;
	$mailaddress = $InternetConfig{kICEmail()};
    }

    $mailaddress ||= $ENV{USER} || $ENV{LOGNAME} || eval {getpwuid $>}
                 ||  "postmaster";

    # Add domain if it does not exist
    $mailaddress .= '@' . maildomain
	if $mailaddress !~ /\@/;

    $mailaddress =~ s/(^.*<|>.*$)//g;
    $mailaddress;
}

1;
PK       ! 6q      
  Address.pmnu [        package Git::LoadCPAN::Mail::Address;
use 5.008;
use strict;
use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
use Git::LoadCPAN (
	module => 'Mail::Address',
	import => 0,
);

1;
PK       ! wP[H  H  
  Mailer.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Closure;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Mail\Mailable as MailableContract;
use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Contracts\Mail\MailQueue as MailQueueContract;
use Illuminate\Contracts\Queue\Factory as QueueContract;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\Factory;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Mail\Events\MessageSent;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\Email;

class Mailer implements MailerContract, MailQueueContract
{
    use Macroable;

    /**
     * The name that is configured for the mailer.
     *
     * @var string
     */
    protected $name;

    /**
     * The view factory instance.
     *
     * @var \Illuminate\Contracts\View\Factory
     */
    protected $views;

    /**
     * The Symfony Transport instance.
     *
     * @var \Symfony\Component\Mailer\Transport\TransportInterface
     */
    protected $transport;

    /**
     * The event dispatcher instance.
     *
     * @var \Illuminate\Contracts\Events\Dispatcher|null
     */
    protected $events;

    /**
     * The global from address and name.
     *
     * @var array
     */
    protected $from;

    /**
     * The global reply-to address and name.
     *
     * @var array
     */
    protected $replyTo;

    /**
     * The global return path address.
     *
     * @var array
     */
    protected $returnPath;

    /**
     * The global to address and name.
     *
     * @var array
     */
    protected $to;

    /**
     * The queue factory implementation.
     *
     * @var \Illuminate\Contracts\Queue\Factory
     */
    protected $queue;

    /**
     * Create a new Mailer instance.
     *
     * @param  string  $name
     * @param  \Illuminate\Contracts\View\Factory  $views
     * @param  \Symfony\Component\Mailer\Transport\TransportInterface  $transport
     * @param  \Illuminate\Contracts\Events\Dispatcher|null  $events
     * @return void
     */
    public function __construct(string $name, Factory $views, TransportInterface $transport, Dispatcher $events = null)
    {
        $this->name = $name;
        $this->views = $views;
        $this->events = $events;
        $this->transport = $transport;
    }

    /**
     * Set the global from address and name.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return void
     */
    public function alwaysFrom($address, $name = null)
    {
        $this->from = compact('address', 'name');
    }

    /**
     * Set the global reply-to address and name.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return void
     */
    public function alwaysReplyTo($address, $name = null)
    {
        $this->replyTo = compact('address', 'name');
    }

    /**
     * Set the global return path address.
     *
     * @param  string  $address
     * @return void
     */
    public function alwaysReturnPath($address)
    {
        $this->returnPath = compact('address');
    }

    /**
     * Set the global to address and name.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return void
     */
    public function alwaysTo($address, $name = null)
    {
        $this->to = compact('address', 'name');
    }

    /**
     * Begin the process of mailing a mailable class instance.
     *
     * @param  mixed  $users
     * @param  string|null  $name
     * @return \Illuminate\Mail\PendingMail
     */
    public function to($users, $name = null)
    {
        if (! is_null($name) && is_string($users)) {
            $users = new Address($users, $name);
        }

        return (new PendingMail($this))->to($users);
    }

    /**
     * Begin the process of mailing a mailable class instance.
     *
     * @param  mixed  $users
     * @param  string|null  $name
     * @return \Illuminate\Mail\PendingMail
     */
    public function cc($users, $name = null)
    {
        if (! is_null($name) && is_string($users)) {
            $users = new Address($users, $name);
        }

        return (new PendingMail($this))->cc($users);
    }

    /**
     * Begin the process of mailing a mailable class instance.
     *
     * @param  mixed  $users
     * @param  string|null  $name
     * @return \Illuminate\Mail\PendingMail
     */
    public function bcc($users, $name = null)
    {
        if (! is_null($name) && is_string($users)) {
            $users = new Address($users, $name);
        }

        return (new PendingMail($this))->bcc($users);
    }

    /**
     * Send a new message with only an HTML part.
     *
     * @param  string  $html
     * @param  mixed  $callback
     * @return \Illuminate\Mail\SentMessage|null
     */
    public function html($html, $callback)
    {
        return $this->send(['html' => new HtmlString($html)], [], $callback);
    }

    /**
     * Send a new message with only a raw text part.
     *
     * @param  string  $text
     * @param  mixed  $callback
     * @return \Illuminate\Mail\SentMessage|null
     */
    public function raw($text, $callback)
    {
        return $this->send(['raw' => $text], [], $callback);
    }

    /**
     * Send a new message with only a plain part.
     *
     * @param  string  $view
     * @param  array  $data
     * @param  mixed  $callback
     * @return \Illuminate\Mail\SentMessage|null
     */
    public function plain($view, array $data, $callback)
    {
        return $this->send(['text' => $view], $data, $callback);
    }

    /**
     * Render the given message as a view.
     *
     * @param  string|array  $view
     * @param  array  $data
     * @return string
     */
    public function render($view, array $data = [])
    {
        // First we need to parse the view, which could either be a string or an array
        // containing both an HTML and plain text versions of the view which should
        // be used when sending an e-mail. We will extract both of them out here.
        [$view, $plain, $raw] = $this->parseView($view);

        $data['message'] = $this->createMessage();

        return $this->replaceEmbeddedAttachments(
            $this->renderView($view ?: $plain, $data),
            $data['message']->getSymfonyMessage()->getAttachments()
        );
    }

    /**
     * Replace the embedded image attachments with raw, inline image data for browser rendering.
     *
     * @param  string  $renderedView
     * @param  array  $attachments
     * @return string
     */
    protected function replaceEmbeddedAttachments(string $renderedView, array $attachments)
    {
        if (preg_match_all('/<img.+?src=[\'"]cid:([^\'"]+)[\'"].*?>/i', $renderedView, $matches)) {
            foreach (array_unique($matches[1]) as $image) {
                foreach ($attachments as $attachment) {
                    if ($attachment->getFilename() === $image) {
                        $renderedView = str_replace(
                            'cid:'.$image,
                            'data:'.$attachment->getContentType().';base64,'.$attachment->bodyToString(),
                            $renderedView
                        );

                        break;
                    }
                }
            }
        }

        return $renderedView;
    }

    /**
     * Send a new message using a view.
     *
     * @param  \Illuminate\Contracts\Mail\Mailable|string|array  $view
     * @param  array  $data
     * @param  \Closure|string|null  $callback
     * @return \Illuminate\Mail\SentMessage|null
     */
    public function send($view, array $data = [], $callback = null)
    {
        if ($view instanceof MailableContract) {
            return $this->sendMailable($view);
        }

        $data['mailer'] = $this->name;

        // First we need to parse the view, which could either be a string or an array
        // containing both an HTML and plain text versions of the view which should
        // be used when sending an e-mail. We will extract both of them out here.
        [$view, $plain, $raw] = $this->parseView($view);

        $data['message'] = $message = $this->createMessage();

        // Once we have retrieved the view content for the e-mail we will set the body
        // of this message using the HTML type, which will provide a simple wrapper
        // to creating view based emails that are able to receive arrays of data.
        if (! is_null($callback)) {
            $callback($message);
        }

        $this->addContent($message, $view, $plain, $raw, $data);

        // If a global "to" address has been set, we will set that address on the mail
        // message. This is primarily useful during local development in which each
        // message should be delivered into a single mail address for inspection.
        if (isset($this->to['address'])) {
            $this->setGlobalToAndRemoveCcAndBcc($message);
        }

        // Next we will determine if the message should be sent. We give the developer
        // one final chance to stop this message and then we will send it to all of
        // its recipients. We will then fire the sent event for the sent message.
        $symfonyMessage = $message->getSymfonyMessage();

        if ($this->shouldSendMessage($symfonyMessage, $data)) {
            $symfonySentMessage = $this->sendSymfonyMessage($symfonyMessage);

            if ($symfonySentMessage) {
                $sentMessage = new SentMessage($symfonySentMessage);

                $this->dispatchSentEvent($sentMessage, $data);

                return $sentMessage;
            }
        }
    }

    /**
     * Send the given mailable.
     *
     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
     * @return \Illuminate\Mail\SentMessage|null
     */
    protected function sendMailable(MailableContract $mailable)
    {
        return $mailable instanceof ShouldQueue
                        ? $mailable->mailer($this->name)->queue($this->queue)
                        : $mailable->mailer($this->name)->send($this);
    }

    /**
     * Parse the given view name or array.
     *
     * @param  \Closure|array|string  $view
     * @return array
     *
     * @throws \InvalidArgumentException
     */
    protected function parseView($view)
    {
        if (is_string($view) || $view instanceof Closure) {
            return [$view, null, null];
        }

        // If the given view is an array with numeric keys, we will just assume that
        // both a "pretty" and "plain" view were provided, so we will return this
        // array as is, since it should contain both views with numerical keys.
        if (is_array($view) && isset($view[0])) {
            return [$view[0], $view[1], null];
        }

        // If this view is an array but doesn't contain numeric keys, we will assume
        // the views are being explicitly specified and will extract them via the
        // named keys instead, allowing the developers to use one or the other.
        if (is_array($view)) {
            return [
                $view['html'] ?? null,
                $view['text'] ?? null,
                $view['raw'] ?? null,
            ];
        }

        throw new InvalidArgumentException('Invalid view.');
    }

    /**
     * Add the content to a given message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @param  string  $view
     * @param  string  $plain
     * @param  string  $raw
     * @param  array  $data
     * @return void
     */
    protected function addContent($message, $view, $plain, $raw, $data)
    {
        if (isset($view)) {
            $message->html($this->renderView($view, $data) ?: ' ');
        }

        if (isset($plain)) {
            $message->text($this->renderView($plain, $data) ?: ' ');
        }

        if (isset($raw)) {
            $message->text($raw);
        }
    }

    /**
     * Render the given view.
     *
     * @param  \Closure|string  $view
     * @param  array  $data
     * @return string
     */
    protected function renderView($view, $data)
    {
        $view = value($view, $data);

        return $view instanceof Htmlable
                        ? $view->toHtml()
                        : $this->views->make($view, $data)->render();
    }

    /**
     * Set the global "to" address on the given message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return void
     */
    protected function setGlobalToAndRemoveCcAndBcc($message)
    {
        $message->forgetTo();

        $message->to($this->to['address'], $this->to['name'], true);

        $message->forgetCc();
        $message->forgetBcc();
    }

    /**
     * Queue a new e-mail message for sending.
     *
     * @param  \Illuminate\Contracts\Mail\Mailable|string|array  $view
     * @param  string|null  $queue
     * @return mixed
     *
     * @throws \InvalidArgumentException
     */
    public function queue($view, $queue = null)
    {
        if (! $view instanceof MailableContract) {
            throw new InvalidArgumentException('Only mailables may be queued.');
        }

        if (is_string($queue)) {
            $view->onQueue($queue);
        }

        return $view->mailer($this->name)->queue($this->queue);
    }

    /**
     * Queue a new e-mail message for sending on the given queue.
     *
     * @param  string  $queue
     * @param  \Illuminate\Contracts\Mail\Mailable  $view
     * @return mixed
     */
    public function onQueue($queue, $view)
    {
        return $this->queue($view, $queue);
    }

    /**
     * Queue a new e-mail message for sending on the given queue.
     *
     * This method didn't match rest of framework's "onQueue" phrasing. Added "onQueue".
     *
     * @param  string  $queue
     * @param  \Illuminate\Contracts\Mail\Mailable  $view
     * @return mixed
     */
    public function queueOn($queue, $view)
    {
        return $this->onQueue($queue, $view);
    }

    /**
     * Queue a new e-mail message for sending after (n) seconds.
     *
     * @param  \DateTimeInterface|\DateInterval|int  $delay
     * @param  \Illuminate\Contracts\Mail\Mailable  $view
     * @param  string|null  $queue
     * @return mixed
     *
     * @throws \InvalidArgumentException
     */
    public function later($delay, $view, $queue = null)
    {
        if (! $view instanceof MailableContract) {
            throw new InvalidArgumentException('Only mailables may be queued.');
        }

        return $view->mailer($this->name)->later(
            $delay, is_null($queue) ? $this->queue : $queue
        );
    }

    /**
     * Queue a new e-mail message for sending after (n) seconds on the given queue.
     *
     * @param  string  $queue
     * @param  \DateTimeInterface|\DateInterval|int  $delay
     * @param  \Illuminate\Contracts\Mail\Mailable  $view
     * @return mixed
     */
    public function laterOn($queue, $delay, $view)
    {
        return $this->later($delay, $view, $queue);
    }

    /**
     * Create a new message instance.
     *
     * @return \Illuminate\Mail\Message
     */
    protected function createMessage()
    {
        $message = new Message(new Email());

        // If a global from address has been specified we will set it on every message
        // instance so the developer does not have to repeat themselves every time
        // they create a new message. We'll just go ahead and push this address.
        if (! empty($this->from['address'])) {
            $message->from($this->from['address'], $this->from['name']);
        }

        // When a global reply address was specified we will set this on every message
        // instance so the developer does not have to repeat themselves every time
        // they create a new message. We will just go ahead and push this address.
        if (! empty($this->replyTo['address'])) {
            $message->replyTo($this->replyTo['address'], $this->replyTo['name']);
        }

        if (! empty($this->returnPath['address'])) {
            $message->returnPath($this->returnPath['address']);
        }

        return $message;
    }

    /**
     * Send a Symfony Email instance.
     *
     * @param  \Symfony\Component\Mime\Email  $message
     * @return \Symfony\Component\Mailer\SentMessage|null
     */
    protected function sendSymfonyMessage(Email $message)
    {
        try {
            return $this->transport->send($message, Envelope::create($message));
        } finally {
            //
        }
    }

    /**
     * Determines if the email can be sent.
     *
     * @param  \Symfony\Component\Mime\Email  $message
     * @param  array  $data
     * @return bool
     */
    protected function shouldSendMessage($message, $data = [])
    {
        if (! $this->events) {
            return true;
        }

        return $this->events->until(
            new MessageSending($message, $data)
        ) !== false;
    }

    /**
     * Dispatch the message sent event.
     *
     * @param  \Illuminate\Mail\SentMessage  $message
     * @param  array  $data
     * @return void
     */
    protected function dispatchSentEvent($message, $data = [])
    {
        if ($this->events) {
            $this->events->dispatch(
                new MessageSent($message, $data)
            );
        }
    }

    /**
     * Get the Symfony Transport instance.
     *
     * @return \Symfony\Component\Mailer\Transport\TransportInterface
     */
    public function getSymfonyTransport()
    {
        return $this->transport;
    }

    /**
     * Get the view factory instance.
     *
     * @return \Illuminate\Contracts\View\Factory
     */
    public function getViewFactory()
    {
        return $this->views;
    }

    /**
     * Set the Symfony Transport instance.
     *
     * @param  \Symfony\Component\Mailer\Transport\TransportInterface  $transport
     * @return void
     */
    public function setSymfonyTransport(TransportInterface $transport)
    {
        $this->transport = $transport;
    }

    /**
     * Set the queue manager instance.
     *
     * @param  \Illuminate\Contracts\Queue\Factory  $queue
     * @return $this
     */
    public function setQueue(QueueContract $queue)
    {
        $this->queue = $queue;

        return $this;
    }
}
PK       ! s      StockAlertSend.phpnu ̗        <?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class StockAlertSend extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    protected $data;

    public function __construct($data)
    {
        $this->data = $data;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->subject('Low Stock Alert')->view('emails.stock.stock_alert', $this->data);
    }
}
PK       ! ň      InvoiceSend.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class InvoiceSend extends Mailable
{
    use Queueable, SerializesModels;

    public $invoice;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($invoice)
    {
        $this->invoice = $invoice;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {

        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.invoice_send')->with('invoice', $this->invoice)->subject('Ragarding to product/service invoice generator.');
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.invoice_send')->with('invoice', $this->invoice)->subject('Ragarding to product/service invoice generator.');
        }
    }
}
PK       ! |9  9    Markdown.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\MarkdownConverter;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;

class Markdown
{
    /**
     * The view factory implementation.
     *
     * @var \Illuminate\Contracts\View\Factory
     */
    protected $view;

    /**
     * The current theme being used when generating emails.
     *
     * @var string
     */
    protected $theme = 'default';

    /**
     * The registered component paths.
     *
     * @var array
     */
    protected $componentPaths = [];

    /**
     * Create a new Markdown renderer instance.
     *
     * @param  \Illuminate\Contracts\View\Factory  $view
     * @param  array  $options
     * @return void
     */
    public function __construct(ViewFactory $view, array $options = [])
    {
        $this->view = $view;
        $this->theme = $options['theme'] ?? 'default';
        $this->loadComponentsFrom($options['paths'] ?? []);
    }

    /**
     * Render the Markdown template into HTML.
     *
     * @param  string  $view
     * @param  array  $data
     * @param  \TijsVerkoyen\CssToInlineStyles\CssToInlineStyles|null  $inliner
     * @return \Illuminate\Support\HtmlString
     */
    public function render($view, array $data = [], $inliner = null)
    {
        $this->view->flushFinderCache();

        $contents = $this->view->replaceNamespace(
            'mail', $this->htmlComponentPaths()
        )->make($view, $data)->render();

        if ($this->view->exists($customTheme = Str::start($this->theme, 'mail.'))) {
            $theme = $customTheme;
        } else {
            $theme = str_contains($this->theme, '::')
                ? $this->theme
                : 'mail::themes.'.$this->theme;
        }

        return new HtmlString(($inliner ?: new CssToInlineStyles)->convert(
            $contents, $this->view->make($theme, $data)->render()
        ));
    }

    /**
     * Render the Markdown template into text.
     *
     * @param  string  $view
     * @param  array  $data
     * @return \Illuminate\Support\HtmlString
     */
    public function renderText($view, array $data = [])
    {
        $this->view->flushFinderCache();

        $contents = $this->view->replaceNamespace(
            'mail', $this->textComponentPaths()
        )->make($view, $data)->render();

        return new HtmlString(
            html_entity_decode(preg_replace("/[\r\n]{2,}/", "\n\n", $contents), ENT_QUOTES, 'UTF-8')
        );
    }

    /**
     * Parse the given Markdown text into HTML.
     *
     * @param  string  $text
     * @return \Illuminate\Support\HtmlString
     */
    public static function parse($text)
    {
        $environment = new Environment([
            'allow_unsafe_links' => false,
        ]);

        $environment->addExtension(new CommonMarkCoreExtension);
        $environment->addExtension(new TableExtension);

        $converter = new MarkdownConverter($environment);

        return new HtmlString($converter->convert($text)->getContent());
    }

    /**
     * Get the HTML component paths.
     *
     * @return array
     */
    public function htmlComponentPaths()
    {
        return array_map(function ($path) {
            return $path.'/html';
        }, $this->componentPaths());
    }

    /**
     * Get the text component paths.
     *
     * @return array
     */
    public function textComponentPaths()
    {
        return array_map(function ($path) {
            return $path.'/text';
        }, $this->componentPaths());
    }

    /**
     * Get the component paths.
     *
     * @return array
     */
    protected function componentPaths()
    {
        return array_unique(array_merge($this->componentPaths, [
            __DIR__.'/resources/views',
        ]));
    }

    /**
     * Register new mail component paths.
     *
     * @param  array  $paths
     * @return void
     */
    public function loadComponentsFrom(array $paths = [])
    {
        $this->componentPaths = $paths;
    }

    /**
     * Set the default theme to be used.
     *
     * @param  string  $theme
     * @return $this
     */
    public function theme($theme)
    {
        $this->theme = $theme;

        return $this;
    }

    /**
     * Get the theme currently being used by the renderer.
     *
     * @return string
     */
    public function getTheme()
    {
        return $this->theme;
    }
}
PK       ! J  J    Transport/SesV2Transport.phpnu ȯ        <?php

namespace Illuminate\Mail\Transport;

use Aws\Exception\AwsException;
use Aws\SesV2\SesV2Client;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\Header\MetadataHeader;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
use Symfony\Component\Mime\Message;

class SesV2Transport extends AbstractTransport
{
    /**
     * The Amazon SES V2 instance.
     *
     * @var \Aws\SesV2\SesV2Client
     */
    protected $ses;

    /**
     * The Amazon SES transmission options.
     *
     * @var array
     */
    protected $options = [];

    /**
     * Create a new SES V2 transport instance.
     *
     * @param  \Aws\SesV2\SesV2Client  $ses
     * @param  array  $options
     * @return void
     */
    public function __construct(SesV2Client $ses, $options = [])
    {
        $this->ses = $ses;
        $this->options = $options;

        parent::__construct();
    }

    /**
     * {@inheritDoc}
     */
    protected function doSend(SentMessage $message): void
    {
        $options = $this->options;

        if ($message->getOriginalMessage() instanceof Message) {
            foreach ($message->getOriginalMessage()->getHeaders()->all() as $header) {
                if ($header instanceof MetadataHeader) {
                    $options['EmailTags'][] = ['Name' => $header->getKey(), 'Value' => $header->getValue()];
                }
            }
        }

        try {
            $result = $this->ses->sendEmail(
                array_merge(
                    $options, [
                        'Source' => $message->getEnvelope()->getSender()->toString(),
                        'Destination' => [
                            'ToAddresses' => collect($message->getEnvelope()->getRecipients())
                                    ->map
                                    ->toString()
                                    ->values()
                                    ->all(),
                        ],
                        'Content' => [
                            'Raw' => [
                                'Data' => $message->toString(),
                            ],
                        ],
                    ]
                )
            );
        } catch (AwsException $e) {
            $reason = $e->getAwsErrorMessage() ?? $e->getMessage();

            throw new TransportException(
                sprintf('Request to AWS SES V2 API failed. Reason: %s.', $reason),
                is_int($e->getCode()) ? $e->getCode() : 0,
                $e
            );
        }

        $messageId = $result->get('MessageId');

        $message->getOriginalMessage()->getHeaders()->addHeader('X-Message-ID', $messageId);
        $message->getOriginalMessage()->getHeaders()->addHeader('X-SES-Message-ID', $messageId);
    }

    /**
     * Get the Amazon SES V2 client for the SesV2Transport instance.
     *
     * @return \Aws\SesV2\SesV2Client
     */
    public function ses()
    {
        return $this->ses;
    }

    /**
     * Get the transmission options being used by the transport.
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Set the transmission options being used by the transport.
     *
     * @param  array  $options
     * @return array
     */
    public function setOptions(array $options)
    {
        return $this->options = $options;
    }

    /**
     * Get the string representation of the transport.
     *
     * @return string
     */
    public function __toString(): string
    {
        return 'ses-v2';
    }
}
PK       ! ^p      Transport/SesTransport.phpnu ȯ        <?php

namespace Illuminate\Mail\Transport;

use Aws\Exception\AwsException;
use Aws\Ses\SesClient;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\Header\MetadataHeader;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
use Symfony\Component\Mime\Message;

class SesTransport extends AbstractTransport
{
    /**
     * The Amazon SES instance.
     *
     * @var \Aws\Ses\SesClient
     */
    protected $ses;

    /**
     * The Amazon SES transmission options.
     *
     * @var array
     */
    protected $options = [];

    /**
     * Create a new SES transport instance.
     *
     * @param  \Aws\Ses\SesClient  $ses
     * @param  array  $options
     * @return void
     */
    public function __construct(SesClient $ses, $options = [])
    {
        $this->ses = $ses;
        $this->options = $options;

        parent::__construct();
    }

    /**
     * {@inheritDoc}
     */
    protected function doSend(SentMessage $message): void
    {
        $options = $this->options;

        if ($message->getOriginalMessage() instanceof Message) {
            foreach ($message->getOriginalMessage()->getHeaders()->all() as $header) {
                if ($header instanceof MetadataHeader) {
                    $options['Tags'][] = ['Name' => $header->getKey(), 'Value' => $header->getValue()];
                }
            }
        }

        try {
            $result = $this->ses->sendRawEmail(
                array_merge(
                    $options, [
                        'Source' => $message->getEnvelope()->getSender()->toString(),
                        'Destinations' => collect($message->getEnvelope()->getRecipients())
                                ->map
                                ->toString()
                                ->values()
                                ->all(),
                        'RawMessage' => [
                            'Data' => $message->toString(),
                        ],
                    ]
                )
            );
        } catch (AwsException $e) {
            $reason = $e->getAwsErrorMessage() ?? $e->getMessage();

            throw new TransportException(
                sprintf('Request to AWS SES API failed. Reason: %s.', $reason),
                is_int($e->getCode()) ? $e->getCode() : 0,
                $e
            );
        }

        $messageId = $result->get('MessageId');

        $message->getOriginalMessage()->getHeaders()->addHeader('X-Message-ID', $messageId);
        $message->getOriginalMessage()->getHeaders()->addHeader('X-SES-Message-ID', $messageId);
    }

    /**
     * Get the Amazon SES client for the SesTransport instance.
     *
     * @return \Aws\Ses\SesClient
     */
    public function ses()
    {
        return $this->ses;
    }

    /**
     * Get the transmission options being used by the transport.
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Set the transmission options being used by the transport.
     *
     * @param  array  $options
     * @return array
     */
    public function setOptions(array $options)
    {
        return $this->options = $options;
    }

    /**
     * Get the string representation of the transport.
     *
     * @return string
     */
    public function __toString(): string
    {
        return 'ses';
    }
}
PK       ! 8	  	    Transport/LogTransport.phpnu ȯ        <?php

namespace Illuminate\Mail\Transport;

use Illuminate\Support\Str;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\RawMessage;

class LogTransport implements TransportInterface
{
    /**
     * The Logger instance.
     *
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;

    /**
     * Create a new log transport instance.
     *
     * @param  \Psr\Log\LoggerInterface  $logger
     * @return void
     */
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
    {
        $string = Str::of($message->toString());

        if ($string->contains('Content-Type: multipart/')) {
            $boundary = $string
                ->after('boundary=')
                ->before("\r\n")
                ->prepend('--')
                ->append("\r\n");

            $string = $string
                ->explode($boundary)
                ->map($this->decodeQuotedPrintableContent(...))
                ->implode($boundary);
        } elseif ($string->contains('Content-Transfer-Encoding: quoted-printable')) {
            $string = $this->decodeQuotedPrintableContent($string);
        }

        $this->logger->debug((string) $string);

        return new SentMessage($message, $envelope ?? Envelope::create($message));
    }

    /**
     * Decode the given quoted printable content.
     *
     * @param  string  $part
     * @return string
     */
    protected function decodeQuotedPrintableContent(string $part)
    {
        if (! str_contains($part, 'Content-Transfer-Encoding: quoted-printable')) {
            return $part;
        }

        [$headers, $content] = explode("\r\n\r\n", $part, 2);

        return implode("\r\n\r\n", [
            $headers,
            quoted_printable_decode($content),
        ]);
    }

    /**
     * Get the logger for the LogTransport instance.
     *
     * @return \Psr\Log\LoggerInterface
     */
    public function logger()
    {
        return $this->logger;
    }

    /**
     * Get the string representation of the transport.
     *
     * @return string
     */
    public function __toString(): string
    {
        return 'log';
    }
}
PK       ! zȋ      Transport/ResendTransport.phpnu Iw        <?php

namespace Illuminate\Mail\Transport;

use Exception;
use Resend\Contracts\Client;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\MessageConverter;

/*
MIT License

Copyright (c) 2023 Jayan Ratna

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class ResendTransport extends AbstractTransport
{
    /**
     * Create a new Resend transport instance.
     */
    public function __construct(protected Client $resend)
    {
        parent::__construct();
    }

    /**
     * {@inheritDoc}
     */
    protected function doSend(SentMessage $message): void
    {
        $email = MessageConverter::toEmail($message->getOriginalMessage());

        $envelope = $message->getEnvelope();

        $headers = [];

        $headersToBypass = ['from', 'to', 'cc', 'bcc', 'reply-to', 'sender', 'subject', 'content-type'];

        foreach ($email->getHeaders()->all() as $name => $header) {
            if (in_array($name, $headersToBypass, true)) {
                continue;
            }

            $headers[$header->getName()] = $header->getBodyAsString();
        }

        $attachments = [];

        if ($email->getAttachments()) {
            foreach ($email->getAttachments() as $attachment) {
                $attachmentHeaders = $attachment->getPreparedHeaders();
                $contentType = $attachmentHeaders->get('Content-Type')->getBody();
                $disposition = $attachmentHeaders->getHeaderBody('Content-Disposition');
                $filename = $attachmentHeaders->getHeaderParameter('Content-Disposition', 'filename');

                if ($contentType == 'text/calendar') {
                    $content = $attachment->getBody();
                } else {
                    $content = str_replace("\r\n", '', $attachment->bodyToString());
                }

                $item = [
                    'content_type' => $contentType,
                    'content' => $content,
                    'filename' => $filename,
                ];

                if ($disposition === 'inline') {
                    $item['content_id'] = $attachment->hasContentId() ? $attachment->getContentId() : $filename;
                }

                $attachments[] = $item;
            }
        }

        try {
            $result = $this->resend->emails->send([
                'from' => $envelope->getSender()->toString(),
                'to' => $this->stringifyAddresses($this->getRecipients($email, $envelope)),
                'cc' => $this->stringifyAddresses($email->getCc()),
                'bcc' => $this->stringifyAddresses($email->getBcc()),
                'reply_to' => $this->stringifyAddresses($email->getReplyTo()),
                'headers' => $headers,
                'subject' => $email->getSubject(),
                'html' => $email->getHtmlBody(),
                'text' => $email->getTextBody(),
                'attachments' => $attachments,
            ]);

            throw_if(isset($result['statusCode']) && $result['statusCode'] != Response::HTTP_OK, Exception::class, $result['message']);
        } catch (Exception $exception) {
            throw new TransportException(
                sprintf('Request to Resend API failed. Reason: %s.', $exception->getMessage()),
                is_int($exception->getCode()) ? $exception->getCode() : 0,
                $exception
            );
        }

        $messageId = $result->id;

        $email->getHeaders()->addHeader('X-Resend-Email-ID', $messageId);
    }

    /**
     * Get the recipients without CC or BCC.
     */
    protected function getRecipients(Email $email, Envelope $envelope): array
    {
        return array_filter($envelope->getRecipients(), function (Address $address) use ($email) {
            return in_array($address, array_merge($email->getCc(), $email->getBcc()), true) === false;
        });
    }

    /**
     * Get the string representation of the transport.
     */
    public function __toString(): string
    {
        return 'resend';
    }
}
PK       ! F.      Transport/ArrayTransport.phpnu ȯ        <?php

namespace Illuminate\Mail\Transport;

use Illuminate\Support\Collection;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\RawMessage;

class ArrayTransport implements TransportInterface
{
    /**
     * The collection of Symfony Messages.
     *
     * @var \Illuminate\Support\Collection
     */
    protected $messages;

    /**
     * Create a new array transport instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->messages = new Collection;
    }

    /**
     * {@inheritdoc}
     */
    public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
    {
        return $this->messages[] = new SentMessage($message, $envelope ?? Envelope::create($message));
    }

    /**
     * Retrieve the collection of messages.
     *
     * @return \Illuminate\Support\Collection
     */
    public function messages()
    {
        return $this->messages;
    }

    /**
     * Clear all of the messages from the local collection.
     *
     * @return \Illuminate\Support\Collection
     */
    public function flush()
    {
        return $this->messages = new Collection;
    }

    /**
     * Get the string representation of the transport.
     *
     * @return string
     */
    public function __toString(): string
    {
        return 'array';
    }
}
PK       ! r      Mailable.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Container\Container;
use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory;
use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Contracts\Mail\Factory as MailFactory;
use Illuminate\Contracts\Mail\Mailable as MailableContract;
use Illuminate\Contracts\Queue\Factory as Queue;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Contracts\Translation\HasLocalePreference;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\ForwardsCalls;
use Illuminate\Support\Traits\Localizable;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Testing\Constraints\SeeInOrder;
use PHPUnit\Framework\Assert as PHPUnit;
use ReflectionClass;
use ReflectionProperty;
use Symfony\Component\Mailer\Header\MetadataHeader;
use Symfony\Component\Mailer\Header\TagHeader;
use Symfony\Component\Mime\Address;

class Mailable implements MailableContract, Renderable
{
    use Conditionable, ForwardsCalls, Localizable, Macroable {
        __call as macroCall;
    }

    /**
     * The locale of the message.
     *
     * @var string
     */
    public $locale;

    /**
     * The person the message is from.
     *
     * @var array
     */
    public $from = [];

    /**
     * The "to" recipients of the message.
     *
     * @var array
     */
    public $to = [];

    /**
     * The "cc" recipients of the message.
     *
     * @var array
     */
    public $cc = [];

    /**
     * The "bcc" recipients of the message.
     *
     * @var array
     */
    public $bcc = [];

    /**
     * The "reply to" recipients of the message.
     *
     * @var array
     */
    public $replyTo = [];

    /**
     * The subject of the message.
     *
     * @var string
     */
    public $subject;

    /**
     * The Markdown template for the message (if applicable).
     *
     * @var string
     */
    public $markdown;

    /**
     * The HTML to use for the message.
     *
     * @var string
     */
    protected $html;

    /**
     * The view to use for the message.
     *
     * @var string
     */
    public $view;

    /**
     * The plain text view to use for the message.
     *
     * @var string
     */
    public $textView;

    /**
     * The view data for the message.
     *
     * @var array
     */
    public $viewData = [];

    /**
     * The attachments for the message.
     *
     * @var array
     */
    public $attachments = [];

    /**
     * The raw attachments for the message.
     *
     * @var array
     */
    public $rawAttachments = [];

    /**
     * The attachments from a storage disk.
     *
     * @var array
     */
    public $diskAttachments = [];

    /**
     * The tags for the message.
     *
     * @var array
     */
    protected $tags = [];

    /**
     * The metadata for the message.
     *
     * @var array
     */
    protected $metadata = [];

    /**
     * The callbacks for the message.
     *
     * @var array
     */
    public $callbacks = [];

    /**
     * The name of the theme that should be used when formatting the message.
     *
     * @var string|null
     */
    public $theme;

    /**
     * The name of the mailer that should send the message.
     *
     * @var string
     */
    public $mailer;

    /**
     * The rendered mailable views for testing / assertions.
     *
     * @var array
     */
    protected $assertionableRenderStrings;

    /**
     * The callback that should be invoked while building the view data.
     *
     * @var callable
     */
    public static $viewDataCallback;

    /**
     * Send the message using the given mailer.
     *
     * @param  \Illuminate\Contracts\Mail\Factory|\Illuminate\Contracts\Mail\Mailer  $mailer
     * @return \Illuminate\Mail\SentMessage|null
     */
    public function send($mailer)
    {
        return $this->withLocale($this->locale, function () use ($mailer) {
            $this->prepareMailableForDelivery();

            $mailer = $mailer instanceof MailFactory
                            ? $mailer->mailer($this->mailer)
                            : $mailer;

            return $mailer->send($this->buildView(), $this->buildViewData(), function ($message) {
                $this->buildFrom($message)
                     ->buildRecipients($message)
                     ->buildSubject($message)
                     ->buildTags($message)
                     ->buildMetadata($message)
                     ->runCallbacks($message)
                     ->buildAttachments($message);
            });
        });
    }

    /**
     * Queue the message for sending.
     *
     * @param  \Illuminate\Contracts\Queue\Factory  $queue
     * @return mixed
     */
    public function queue(Queue $queue)
    {
        if (isset($this->delay)) {
            return $this->later($this->delay, $queue);
        }

        $connection = property_exists($this, 'connection') ? $this->connection : null;

        $queueName = property_exists($this, 'queue') ? $this->queue : null;

        return $queue->connection($connection)->pushOn(
            $queueName ?: null, $this->newQueuedJob()
        );
    }

    /**
     * Deliver the queued message after (n) seconds.
     *
     * @param  \DateTimeInterface|\DateInterval|int  $delay
     * @param  \Illuminate\Contracts\Queue\Factory  $queue
     * @return mixed
     */
    public function later($delay, Queue $queue)
    {
        $connection = property_exists($this, 'connection') ? $this->connection : null;

        $queueName = property_exists($this, 'queue') ? $this->queue : null;

        return $queue->connection($connection)->laterOn(
            $queueName ?: null, $delay, $this->newQueuedJob()
        );
    }

    /**
     * Make the queued mailable job instance.
     *
     * @return mixed
     */
    protected function newQueuedJob()
    {
        return Container::getInstance()->make(SendQueuedMailable::class, ['mailable' => $this])
                    ->through(array_merge(
                        method_exists($this, 'middleware') ? $this->middleware() : [],
                        $this->middleware ?? []
                    ));
    }

    /**
     * Render the mailable into a view.
     *
     * @return string
     *
     * @throws \ReflectionException
     */
    public function render()
    {
        return $this->withLocale($this->locale, function () {
            $this->prepareMailableForDelivery();

            return Container::getInstance()->make('mailer')->render(
                $this->buildView(), $this->buildViewData()
            );
        });
    }

    /**
     * Build the view for the message.
     *
     * @return array|string
     *
     * @throws \ReflectionException
     */
    protected function buildView()
    {
        if (isset($this->html)) {
            return array_filter([
                'html' => new HtmlString($this->html),
                'text' => $this->textView ?? null,
            ]);
        }

        if (isset($this->markdown)) {
            return $this->buildMarkdownView();
        }

        if (isset($this->view, $this->textView)) {
            return [$this->view, $this->textView];
        } elseif (isset($this->textView)) {
            return ['text' => $this->textView];
        }

        return $this->view;
    }

    /**
     * Build the Markdown view for the message.
     *
     * @return array
     *
     * @throws \ReflectionException
     */
    protected function buildMarkdownView()
    {
        $data = $this->buildViewData();

        return [
            'html' => $this->buildMarkdownHtml($data),
            'text' => $this->buildMarkdownText($data),
        ];
    }

    /**
     * Build the view data for the message.
     *
     * @return array
     *
     * @throws \ReflectionException
     */
    public function buildViewData()
    {
        $data = $this->viewData;

        if (static::$viewDataCallback) {
            $data = array_merge($data, call_user_func(static::$viewDataCallback, $this));
        }

        foreach ((new ReflectionClass($this))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
            if ($property->isInitialized($this) && $property->getDeclaringClass()->getName() !== self::class) {
                $data[$property->getName()] = $property->getValue($this);
            }
        }

        return $data;
    }

    /**
     * Build the HTML view for a Markdown message.
     *
     * @param  array  $viewData
     * @return \Closure
     */
    protected function buildMarkdownHtml($viewData)
    {
        return fn ($data) => $this->markdownRenderer()->render(
            $this->markdown,
            array_merge($data, $viewData),
        );
    }

    /**
     * Build the text view for a Markdown message.
     *
     * @param  array  $viewData
     * @return \Closure
     */
    protected function buildMarkdownText($viewData)
    {
        return function ($data) use ($viewData) {
            if (isset($data['message'])) {
                $data = array_merge($data, [
                    'message' => new TextMessage($data['message']),
                ]);
            }

            return $this->textView ?? $this->markdownRenderer()->renderText(
                $this->markdown,
                array_merge($data, $viewData)
            );
        };
    }

    /**
     * Resolves a Markdown instance with the mail's theme.
     *
     * @return \Illuminate\Mail\Markdown
     */
    protected function markdownRenderer()
    {
        return tap(Container::getInstance()->make(Markdown::class), function ($markdown) {
            $markdown->theme($this->theme ?: Container::getInstance()->get(ConfigRepository::class)->get(
                'mail.markdown.theme', 'default')
            );
        });
    }

    /**
     * Add the sender to the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return $this
     */
    protected function buildFrom($message)
    {
        if (! empty($this->from)) {
            $message->from($this->from[0]['address'], $this->from[0]['name']);
        }

        return $this;
    }

    /**
     * Add all of the recipients to the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return $this
     */
    protected function buildRecipients($message)
    {
        foreach (['to', 'cc', 'bcc', 'replyTo'] as $type) {
            foreach ($this->{$type} as $recipient) {
                $message->{$type}($recipient['address'], $recipient['name']);
            }
        }

        return $this;
    }

    /**
     * Set the subject for the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return $this
     */
    protected function buildSubject($message)
    {
        if ($this->subject) {
            $message->subject($this->subject);
        } else {
            $message->subject(Str::title(Str::snake(class_basename($this), ' ')));
        }

        return $this;
    }

    /**
     * Add all of the attachments to the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return $this
     */
    protected function buildAttachments($message)
    {
        foreach ($this->attachments as $attachment) {
            $message->attach($attachment['file'], $attachment['options']);
        }

        foreach ($this->rawAttachments as $attachment) {
            $message->attachData(
                $attachment['data'], $attachment['name'], $attachment['options']
            );
        }

        $this->buildDiskAttachments($message);

        return $this;
    }

    /**
     * Add all of the disk attachments to the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return void
     */
    protected function buildDiskAttachments($message)
    {
        foreach ($this->diskAttachments as $attachment) {
            $storage = Container::getInstance()->make(
                FilesystemFactory::class
            )->disk($attachment['disk']);

            $message->attachData(
                $storage->get($attachment['path']),
                $attachment['name'] ?? basename($attachment['path']),
                array_merge(['mime' => $storage->mimeType($attachment['path'])], $attachment['options'])
            );
        }
    }

    /**
     * Add all defined tags to the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return $this
     */
    protected function buildTags($message)
    {
        if ($this->tags) {
            foreach ($this->tags as $tag) {
                $message->getHeaders()->add(new TagHeader($tag));
            }
        }

        return $this;
    }

    /**
     * Add all defined metadata to the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return $this
     */
    protected function buildMetadata($message)
    {
        if ($this->metadata) {
            foreach ($this->metadata as $key => $value) {
                $message->getHeaders()->add(new MetadataHeader($key, $value));
            }
        }

        return $this;
    }

    /**
     * Run the callbacks for the message.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return $this
     */
    protected function runCallbacks($message)
    {
        foreach ($this->callbacks as $callback) {
            $callback($message->getSymfonyMessage());
        }

        return $this;
    }

    /**
     * Set the locale of the message.
     *
     * @param  string  $locale
     * @return $this
     */
    public function locale($locale)
    {
        $this->locale = $locale;

        return $this;
    }

    /**
     * Set the priority of this message.
     *
     * The value is an integer where 1 is the highest priority and 5 is the lowest.
     *
     * @param  int  $level
     * @return $this
     */
    public function priority($level = 3)
    {
        $this->callbacks[] = function ($message) use ($level) {
            $message->priority($level);
        };

        return $this;
    }

    /**
     * Set the sender of the message.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function from($address, $name = null)
    {
        return $this->setAddress($address, $name, 'from');
    }

    /**
     * Determine if the given recipient is set on the mailable.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasFrom($address, $name = null)
    {
        return $this->hasRecipient($address, $name, 'from');
    }

    /**
     * Set the recipients of the message.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function to($address, $name = null)
    {
        if (! $this->locale && $address instanceof HasLocalePreference) {
            $this->locale($address->preferredLocale());
        }

        return $this->setAddress($address, $name, 'to');
    }

    /**
     * Determine if the given recipient is set on the mailable.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasTo($address, $name = null)
    {
        return $this->hasRecipient($address, $name, 'to');
    }

    /**
     * Set the recipients of the message.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function cc($address, $name = null)
    {
        return $this->setAddress($address, $name, 'cc');
    }

    /**
     * Determine if the given recipient is set on the mailable.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasCc($address, $name = null)
    {
        return $this->hasRecipient($address, $name, 'cc');
    }

    /**
     * Set the recipients of the message.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function bcc($address, $name = null)
    {
        return $this->setAddress($address, $name, 'bcc');
    }

    /**
     * Determine if the given recipient is set on the mailable.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasBcc($address, $name = null)
    {
        return $this->hasRecipient($address, $name, 'bcc');
    }

    /**
     * Set the "reply to" address of the message.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function replyTo($address, $name = null)
    {
        return $this->setAddress($address, $name, 'replyTo');
    }

    /**
     * Determine if the given replyTo is set on the mailable.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasReplyTo($address, $name = null)
    {
        return $this->hasRecipient($address, $name, 'replyTo');
    }

    /**
     * Set the recipients of the message.
     *
     * All recipients are stored internally as [['name' => ?, 'address' => ?]]
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @param  string  $property
     * @return $this
     */
    protected function setAddress($address, $name = null, $property = 'to')
    {
        if (empty($address)) {
            return $this;
        }

        foreach ($this->addressesToArray($address, $name) as $recipient) {
            $recipient = $this->normalizeRecipient($recipient);

            $this->{$property}[] = [
                'name' => $recipient->name ?? null,
                'address' => $recipient->email,
            ];
        }

        $this->{$property} = collect($this->{$property})
            ->reverse()
            ->unique('address')
            ->reverse()
            ->values()
            ->all();

        return $this;
    }

    /**
     * Convert the given recipient arguments to an array.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return array
     */
    protected function addressesToArray($address, $name)
    {
        if (! is_array($address) && ! $address instanceof Collection) {
            $address = is_string($name) ? [['name' => $name, 'email' => $address]] : [$address];
        }

        return $address;
    }

    /**
     * Convert the given recipient into an object.
     *
     * @param  mixed  $recipient
     * @return object
     */
    protected function normalizeRecipient($recipient)
    {
        if (is_array($recipient)) {
            if (array_values($recipient) === $recipient) {
                return (object) array_map(function ($email) {
                    return compact('email');
                }, $recipient);
            }

            return (object) $recipient;
        } elseif (is_string($recipient)) {
            return (object) ['email' => $recipient];
        } elseif ($recipient instanceof Address) {
            return (object) ['email' => $recipient->getAddress(), 'name' => $recipient->getName()];
        } elseif ($recipient instanceof Mailables\Address) {
            return (object) ['email' => $recipient->address, 'name' => $recipient->name];
        }

        return $recipient;
    }

    /**
     * Determine if the given recipient is set on the mailable.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @param  string  $property
     * @return bool
     */
    protected function hasRecipient($address, $name = null, $property = 'to')
    {
        if (empty($address)) {
            return false;
        }

        $expected = $this->normalizeRecipient(
            $this->addressesToArray($address, $name)[0]
        );

        $expected = [
            'name' => $expected->name ?? null,
            'address' => $expected->email,
        ];

        if ($this->hasEnvelopeRecipient($expected['address'], $expected['name'], $property)) {
            return true;
        }

        return collect($this->{$property})->contains(function ($actual) use ($expected) {
            if (! isset($expected['name'])) {
                return $actual['address'] == $expected['address'];
            }

            return $actual == $expected;
        });
    }

    /**
     * Determine if the mailable "envelope" method defines a recipient.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @param  string  $property
     * @return bool
     */
    private function hasEnvelopeRecipient($address, $name, $property)
    {
        return method_exists($this, 'envelope') && match ($property) {
            'from' => $this->envelope()->isFrom($address, $name),
            'to' => $this->envelope()->hasTo($address, $name),
            'cc' => $this->envelope()->hasCc($address, $name),
            'bcc' => $this->envelope()->hasBcc($address, $name),
            'replyTo' => $this->envelope()->hasReplyTo($address, $name),
        };
    }

    /**
     * Set the subject of the message.
     *
     * @param  string  $subject
     * @return $this
     */
    public function subject($subject)
    {
        $this->subject = $subject;

        return $this;
    }

    /**
     * Determine if the mailable has the given subject.
     *
     * @param  string  $subject
     * @return bool
     */
    public function hasSubject($subject)
    {
        return $this->subject === $subject ||
               (method_exists($this, 'envelope') && $this->envelope()->hasSubject($subject));
    }

    /**
     * Set the Markdown template for the message.
     *
     * @param  string  $view
     * @param  array  $data
     * @return $this
     */
    public function markdown($view, array $data = [])
    {
        $this->markdown = $view;
        $this->viewData = array_merge($this->viewData, $data);

        return $this;
    }

    /**
     * Set the view and view data for the message.
     *
     * @param  string  $view
     * @param  array  $data
     * @return $this
     */
    public function view($view, array $data = [])
    {
        $this->view = $view;
        $this->viewData = array_merge($this->viewData, $data);

        return $this;
    }

    /**
     * Set the rendered HTML content for the message.
     *
     * @param  string  $html
     * @return $this
     */
    public function html($html)
    {
        $this->html = $html;

        return $this;
    }

    /**
     * Set the plain text view for the message.
     *
     * @param  string  $textView
     * @param  array  $data
     * @return $this
     */
    public function text($textView, array $data = [])
    {
        $this->textView = $textView;
        $this->viewData = array_merge($this->viewData, $data);

        return $this;
    }

    /**
     * Set the view data for the message.
     *
     * @param  string|array  $key
     * @param  mixed  $value
     * @return $this
     */
    public function with($key, $value = null)
    {
        if (is_array($key)) {
            $this->viewData = array_merge($this->viewData, $key);
        } else {
            $this->viewData[$key] = $value;
        }

        return $this;
    }

    /**
     * Attach a file to the message.
     *
     * @param  string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment  $file
     * @param  array  $options
     * @return $this
     */
    public function attach($file, array $options = [])
    {
        if ($file instanceof Attachable) {
            $file = $file->toMailAttachment();
        }

        if ($file instanceof Attachment) {
            return $file->attachTo($this, $options);
        }

        $this->attachments = collect($this->attachments)
                    ->push(compact('file', 'options'))
                    ->unique('file')
                    ->all();

        return $this;
    }

    /**
     * Attach multiple files to the message.
     *
     * @param  array  $files
     * @return $this
     */
    public function attachMany($files)
    {
        foreach ($files as $file => $options) {
            if (is_int($file)) {
                $this->attach($options);
            } else {
                $this->attach($file, $options);
            }
        }

        return $this;
    }

    /**
     * Determine if the mailable has the given attachment.
     *
     * @param  string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment  $file
     * @param  array  $options
     * @return bool
     */
    public function hasAttachment($file, array $options = [])
    {
        if ($file instanceof Attachable) {
            $file = $file->toMailAttachment();
        }

        if ($file instanceof Attachment && $this->hasEnvelopeAttachment($file, $options)) {
            return true;
        }

        if ($file instanceof Attachment) {
            $parts = $file->attachWith(
                fn ($path) => [$path, [
                    'as' => $options['as'] ?? $file->as,
                    'mime' => $options['mime'] ?? $file->mime,
                ]],
                fn ($data) => $this->hasAttachedData($data(), $options['as'] ?? $file->as, ['mime' => $options['mime'] ?? $file->mime])
            );

            if ($parts === true) {
                return true;
            }

            [$file, $options] = $parts === false
                ? [null, []]
                : $parts;
        }

        return collect($this->attachments)->contains(
            fn ($attachment) => $attachment['file'] === $file && array_filter($attachment['options']) === array_filter($options)
        );
    }

    /**
     * Determine if the mailable has the given envelope attachment.
     *
     * @param  \Illuminate\Mail\Attachment  $attachment
     * @param  array  $options
     * @return bool
     */
    private function hasEnvelopeAttachment($attachment, $options = [])
    {
        if (! method_exists($this, 'envelope')) {
            return false;
        }

        $attachments = $this->attachments();

        return Collection::make(is_object($attachments) ? [$attachments] : $attachments)
                ->map(fn ($attached) => $attached instanceof Attachable ? $attached->toMailAttachment() : $attached)
                ->contains(fn ($attached) => $attached->isEquivalent($attachment, $options));
    }

    /**
     * Attach a file to the message from storage.
     *
     * @param  string  $path
     * @param  string|null  $name
     * @param  array  $options
     * @return $this
     */
    public function attachFromStorage($path, $name = null, array $options = [])
    {
        return $this->attachFromStorageDisk(null, $path, $name, $options);
    }

    /**
     * Attach a file to the message from storage.
     *
     * @param  string  $disk
     * @param  string  $path
     * @param  string|null  $name
     * @param  array  $options
     * @return $this
     */
    public function attachFromStorageDisk($disk, $path, $name = null, array $options = [])
    {
        $this->diskAttachments = collect($this->diskAttachments)->push([
            'disk' => $disk,
            'path' => $path,
            'name' => $name ?? basename($path),
            'options' => $options,
        ])->unique(function ($file) {
            return $file['name'].$file['disk'].$file['path'];
        })->all();

        return $this;
    }

    /**
     * Determine if the mailable has the given attachment from storage.
     *
     * @param  string  $path
     * @param  string|null  $name
     * @param  array  $options
     * @return bool
     */
    public function hasAttachmentFromStorage($path, $name = null, array $options = [])
    {
        return $this->hasAttachmentFromStorageDisk(null, $path, $name, $options);
    }

    /**
     * Determine if the mailable has the given attachment from a specific storage disk.
     *
     * @param  string  $disk
     * @param  string  $path
     * @param  string|null  $name
     * @param  array  $options
     * @return bool
     */
    public function hasAttachmentFromStorageDisk($disk, $path, $name = null, array $options = [])
    {
        return collect($this->diskAttachments)->contains(
            fn ($attachment) => $attachment['disk'] === $disk
                && $attachment['path'] === $path
                && $attachment['name'] === ($name ?? basename($path))
                && $attachment['options'] === $options
        );
    }

    /**
     * Attach in-memory data as an attachment.
     *
     * @param  string  $data
     * @param  string  $name
     * @param  array  $options
     * @return $this
     */
    public function attachData($data, $name, array $options = [])
    {
        $this->rawAttachments = collect($this->rawAttachments)
                ->push(compact('data', 'name', 'options'))
                ->unique(function ($file) {
                    return $file['name'].$file['data'];
                })->all();

        return $this;
    }

    /**
     * Determine if the mailable has the given data as an attachment.
     *
     * @param  string  $data
     * @param  string  $name
     * @param  array  $options
     * @return bool
     */
    public function hasAttachedData($data, $name, array $options = [])
    {
        return collect($this->rawAttachments)->contains(
            fn ($attachment) => $attachment['data'] === $data
                && $attachment['name'] === $name
                && array_filter($attachment['options']) === array_filter($options)
        );
    }

    /**
     * Add a tag header to the message when supported by the underlying transport.
     *
     * @param  string  $value
     * @return $this
     */
    public function tag($value)
    {
        array_push($this->tags, $value);

        return $this;
    }

    /**
     * Determine if the mailable has the given tag.
     *
     * @param  string  $value
     * @return bool
     */
    public function hasTag($value)
    {
        return in_array($value, $this->tags) ||
               (method_exists($this, 'envelope') && in_array($value, $this->envelope()->tags));
    }

    /**
     * Add a metadata header to the message when supported by the underlying transport.
     *
     * @param  string  $key
     * @param  string  $value
     * @return $this
     */
    public function metadata($key, $value)
    {
        $this->metadata[$key] = $value;

        return $this;
    }

    /**
     * Determine if the mailable has the given metadata.
     *
     * @param  string  $key
     * @param  string  $value
     * @return bool
     */
    public function hasMetadata($key, $value)
    {
        return (isset($this->metadata[$key]) && $this->metadata[$key] === $value) ||
               (method_exists($this, 'envelope') && $this->envelope()->hasMetadata($key, $value));
    }

    /**
     * Assert that the mailable is from the given address.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function assertFrom($address, $name = null)
    {
        $this->renderForAssertions();

        $recipient = $this->formatAssertionRecipient($address, $name);

        PHPUnit::assertTrue(
            $this->hasFrom($address, $name),
            "Email was not from expected address [{$recipient}]."
        );

        return $this;
    }

    /**
     * Assert that the mailable has the given recipient.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function assertTo($address, $name = null)
    {
        $this->renderForAssertions();

        $recipient = $this->formatAssertionRecipient($address, $name);

        PHPUnit::assertTrue(
            $this->hasTo($address, $name),
            "Did not see expected recipient [{$recipient}] in email 'to' recipients."
        );

        return $this;
    }

    /**
     * Assert that the mailable has the given recipient.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function assertHasTo($address, $name = null)
    {
        return $this->assertTo($address, $name);
    }

    /**
     * Assert that the mailable has the given recipient.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function assertHasCc($address, $name = null)
    {
        $this->renderForAssertions();

        $recipient = $this->formatAssertionRecipient($address, $name);

        PHPUnit::assertTrue(
            $this->hasCc($address, $name),
            "Did not see expected recipient [{$recipient}] in email 'cc' recipients."
        );

        return $this;
    }

    /**
     * Assert that the mailable has the given recipient.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function assertHasBcc($address, $name = null)
    {
        $this->renderForAssertions();

        $recipient = $this->formatAssertionRecipient($address, $name);

        PHPUnit::assertTrue(
            $this->hasBcc($address, $name),
            "Did not see expected recipient [{$recipient}] in email 'bcc' recipients."
        );

        return $this;
    }

    /**
     * Assert that the mailable has the given "reply to" address.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function assertHasReplyTo($address, $name = null)
    {
        $this->renderForAssertions();

        $replyTo = $this->formatAssertionRecipient($address, $name);

        PHPUnit::assertTrue(
            $this->hasReplyTo($address, $name),
            "Did not see expected address [{$replyTo}] as email 'reply to' recipient."
        );

        return $this;
    }

    /**
     * Format the mailable recipient for display in an assertion message.
     *
     * @param  object|array|string  $address
     * @param  string|null  $name
     * @return string
     */
    private function formatAssertionRecipient($address, $name = null)
    {
        if (! is_string($address)) {
            $address = json_encode($address);
        }

        if (filled($name)) {
            $address .= ' ('.$name.')';
        }

        return $address;
    }

    /**
     * Assert that the mailable has the given subject.
     *
     * @param  string  $subject
     * @return $this
     */
    public function assertHasSubject($subject)
    {
        $this->renderForAssertions();

        PHPUnit::assertTrue(
            $this->hasSubject($subject),
            "Did not see expected text [{$subject}] in email subject."
        );

        return $this;
    }

    /**
     * Assert that the given text is present in the HTML email body.
     *
     * @param  string  $string
     * @param  bool  $escape
     * @return $this
     */
    public function assertSeeInHtml($string, $escape = true)
    {
        $string = $escape ? e($string) : $string;

        [$html, $text] = $this->renderForAssertions();

        PHPUnit::assertStringContainsString(
            $string,
            $html,
            "Did not see expected text [{$string}] within email body."
        );

        return $this;
    }

    /**
     * Assert that the given text is not present in the HTML email body.
     *
     * @param  string  $string
     * @param  bool  $escape
     * @return $this
     */
    public function assertDontSeeInHtml($string, $escape = true)
    {
        $string = $escape ? e($string) : $string;

        [$html, $text] = $this->renderForAssertions();

        PHPUnit::assertStringNotContainsString(
            $string,
            $html,
            "Saw unexpected text [{$string}] within email body."
        );

        return $this;
    }

    /**
     * Assert that the given text strings are present in order in the HTML email body.
     *
     * @param  array  $strings
     * @param  bool  $escape
     * @return $this
     */
    public function assertSeeInOrderInHtml($strings, $escape = true)
    {
        $strings = $escape ? array_map('e', $strings) : $strings;

        [$html, $text] = $this->renderForAssertions();

        PHPUnit::assertThat($strings, new SeeInOrder($html));

        return $this;
    }

    /**
     * Assert that the given text is present in the plain-text email body.
     *
     * @param  string  $string
     * @return $this
     */
    public function assertSeeInText($string)
    {
        [$html, $text] = $this->renderForAssertions();

        PHPUnit::assertStringContainsString(
            $string,
            $text,
            "Did not see expected text [{$string}] within text email body."
        );

        return $this;
    }

    /**
     * Assert that the given text is not present in the plain-text email body.
     *
     * @param  string  $string
     * @return $this
     */
    public function assertDontSeeInText($string)
    {
        [$html, $text] = $this->renderForAssertions();

        PHPUnit::assertStringNotContainsString(
            $string,
            $text,
            "Saw unexpected text [{$string}] within text email body."
        );

        return $this;
    }

    /**
     * Assert that the given text strings are present in order in the plain-text email body.
     *
     * @param  array  $strings
     * @return $this
     */
    public function assertSeeInOrderInText($strings)
    {
        [$html, $text] = $this->renderForAssertions();

        PHPUnit::assertThat($strings, new SeeInOrder($text));

        return $this;
    }

    /**
     * Assert the mailable has the given attachment.
     *
     * @param  string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment  $file
     * @param  array  $options
     * @return $this
     */
    public function assertHasAttachment($file, array $options = [])
    {
        $this->renderForAssertions();

        PHPUnit::assertTrue(
            $this->hasAttachment($file, $options),
            'Did not find the expected attachment.'
        );

        return $this;
    }

    /**
     * Assert the mailable has the given data as an attachment.
     *
     * @param  string  $data
     * @param  string  $name
     * @param  array  $options
     * @return $this
     */
    public function assertHasAttachedData($data, $name, array $options = [])
    {
        $this->renderForAssertions();

        PHPUnit::assertTrue(
            $this->hasAttachedData($data, $name, $options),
            'Did not find the expected attachment.'
        );

        return $this;
    }

    /**
     * Assert the mailable has the given attachment from storage.
     *
     * @param  string  $path
     * @param  string|null  $name
     * @param  array  $options
     * @return $this
     */
    public function assertHasAttachmentFromStorage($path, $name = null, array $options = [])
    {
        $this->renderForAssertions();

        PHPUnit::assertTrue(
            $this->hasAttachmentFromStorage($path, $name, $options),
            'Did not find the expected attachment.'
        );

        return $this;
    }

    /**
     * Assert the mailable has the given attachment from a specific storage disk.
     *
     * @param  string  $disk
     * @param  string  $path
     * @param  string|null  $name
     * @param  array  $options
     * @return $this
     */
    public function assertHasAttachmentFromStorageDisk($disk, $path, $name = null, array $options = [])
    {
        $this->renderForAssertions();

        PHPUnit::assertTrue(
            $this->hasAttachmentFromStorageDisk($disk, $path, $name, $options),
            'Did not find the expected attachment.'
        );

        return $this;
    }

    /**
     * Assert that the mailable has the given tag.
     *
     * @param  string  $tag
     * @return $this
     */
    public function assertHasTag($tag)
    {
        $this->renderForAssertions();

        PHPUnit::assertTrue(
            $this->hasTag($tag),
            "Did not see expected tag [{$tag}] in email tags."
        );

        return $this;
    }

    /**
     * Assert that the mailable has the given metadata.
     *
     * @param  string  $key
     * @param  string  $value
     * @return $this
     */
    public function assertHasMetadata($key, $value)
    {
        $this->renderForAssertions();

        PHPUnit::assertTrue(
            $this->hasMetadata($key, $value),
            "Did not see expected key [{$key}] and value [{$value}] in email metadata."
        );

        return $this;
    }

    /**
     * Render the HTML and plain-text version of the mailable into views for assertions.
     *
     * @return array
     *
     * @throws \ReflectionException
     */
    protected function renderForAssertions()
    {
        if ($this->assertionableRenderStrings) {
            return $this->assertionableRenderStrings;
        }

        return $this->assertionableRenderStrings = $this->withLocale($this->locale, function () {
            $this->prepareMailableForDelivery();

            $html = Container::getInstance()->make('mailer')->render(
                $view = $this->buildView(), $this->buildViewData()
            );

            if (is_array($view) && isset($view[1])) {
                $text = $view[1];
            }

            $text ??= $view['text'] ?? '';

            if (! empty($text) && ! $text instanceof Htmlable) {
                $text = Container::getInstance()->make('mailer')->render(
                    $text, $this->buildViewData()
                );
            }

            return [(string) $html, (string) $text];
        });
    }

    /**
     * Prepare the mailable instance for delivery.
     *
     * @return void
     */
    protected function prepareMailableForDelivery()
    {
        if (method_exists($this, 'build')) {
            Container::getInstance()->call([$this, 'build']);
        }

        $this->ensureHeadersAreHydrated();
        $this->ensureEnvelopeIsHydrated();
        $this->ensureContentIsHydrated();
        $this->ensureAttachmentsAreHydrated();
    }

    /**
     * Ensure the mailable's headers are hydrated from the "headers" method.
     *
     * @return void
     */
    private function ensureHeadersAreHydrated()
    {
        if (! method_exists($this, 'headers')) {
            return;
        }

        $headers = $this->headers();

        $this->withSymfonyMessage(function ($message) use ($headers) {
            if ($headers->messageId) {
                $message->getHeaders()->addIdHeader('Message-Id', $headers->messageId);
            }

            if (count($headers->references) > 0) {
                $message->getHeaders()->addTextHeader('References', $headers->referencesString());
            }

            foreach ($headers->text as $key => $value) {
                $message->getHeaders()->addTextHeader($key, $value);
            }
        });
    }

    /**
     * Ensure the mailable's "envelope" data is hydrated from the "envelope" method.
     *
     * @return void
     */
    private function ensureEnvelopeIsHydrated()
    {
        if (! method_exists($this, 'envelope')) {
            return;
        }

        $envelope = $this->envelope();

        if (isset($envelope->from)) {
            $this->from($envelope->from->address, $envelope->from->name);
        }

        foreach (['to', 'cc', 'bcc', 'replyTo'] as $type) {
            foreach ($envelope->{$type} as $address) {
                $this->{$type}($address->address, $address->name);
            }
        }

        if ($envelope->subject) {
            $this->subject($envelope->subject);
        }

        foreach ($envelope->tags as $tag) {
            $this->tag($tag);
        }

        foreach ($envelope->metadata as $key => $value) {
            $this->metadata($key, $value);
        }

        foreach ($envelope->using as $callback) {
            $this->withSymfonyMessage($callback);
        }
    }

    /**
     * Ensure the mailable's content is hydrated from the "content" method.
     *
     * @return void
     */
    private function ensureContentIsHydrated()
    {
        if (! method_exists($this, 'content')) {
            return;
        }

        $content = $this->content();

        if ($content->view) {
            $this->view($content->view);
        }

        if ($content->html) {
            $this->view($content->html);
        }

        if ($content->text) {
            $this->text($content->text);
        }

        if ($content->markdown) {
            $this->markdown($content->markdown);
        }

        if ($content->htmlString) {
            $this->html($content->htmlString);
        }

        foreach ($content->with as $key => $value) {
            $this->with($key, $value);
        }
    }

    /**
     * Ensure the mailable's attachments are hydrated from the "attachments" method.
     *
     * @return void
     */
    private function ensureAttachmentsAreHydrated()
    {
        if (! method_exists($this, 'attachments')) {
            return;
        }

        $attachments = $this->attachments();

        Collection::make(is_object($attachments) ? [$attachments] : $attachments)
            ->each(function ($attachment) {
                $this->attach($attachment);
            });
    }

    /**
     * Set the name of the mailer that should send the message.
     *
     * @param  string  $mailer
     * @return $this
     */
    public function mailer($mailer)
    {
        $this->mailer = $mailer;

        return $this;
    }

    /**
     * Register a callback to be called with the Symfony message instance.
     *
     * @param  callable  $callback
     * @return $this
     */
    public function withSymfonyMessage($callback)
    {
        $this->callbacks[] = $callback;

        return $this;
    }

    /**
     * Register a callback to be called while building the view data.
     *
     * @param  callable  $callback
     * @return void
     */
    public static function buildViewDataUsing(callable $callback)
    {
        static::$viewDataCallback = $callback;
    }

    /**
     * Dynamically bind parameters to the message.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return $this
     *
     * @throws \BadMethodCallException
     */
    public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }

        if (str_starts_with($method, 'with')) {
            return $this->with(Str::camel(substr($method, 4)), $parameters[0]);
        }

        static::throwBadMethodCallException($method);
    }
}
PK       ! 
B3      Mailables/Headers.phpnu ȯ        <?php

namespace Illuminate\Mail\Mailables;

use Illuminate\Support\Str;
use Illuminate\Support\Traits\Conditionable;

class Headers
{
    use Conditionable;

    /**
     * The message's message ID.
     *
     * @var string|null
     */
    public $messageId;

    /**
     * The message IDs that are referenced by the message.
     *
     * @var array
     */
    public $references;

    /**
     * The message's text headers.
     *
     * @var array
     */
    public $text;

    /**
     * Create a new instance of headers for a message.
     *
     * @param  string|null  $messageId
     * @param  array  $references
     * @param  array  $text
     * @return void
     *
     * @named-arguments-supported
     */
    public function __construct(string $messageId = null, array $references = [], array $text = [])
    {
        $this->messageId = $messageId;
        $this->references = $references;
        $this->text = $text;
    }

    /**
     * Set the message ID.
     *
     * @param  string  $messageId
     * @return $this
     */
    public function messageId(string $messageId)
    {
        $this->messageId = $messageId;

        return $this;
    }

    /**
     * Set the message IDs referenced by this message.
     *
     * @param  array  $references
     * @return $this
     */
    public function references(array $references)
    {
        $this->references = array_merge($this->references, $references);

        return $this;
    }

    /**
     * Set the headers for this message.
     *
     * @param  array  $references
     * @return $this
     */
    public function text(array $text)
    {
        $this->text = array_merge($this->text, $text);

        return $this;
    }

    /**
     * Get the references header as a string.
     *
     * @return string
     */
    public function referencesString(): string
    {
        return collect($this->references)->map(function ($messageId) {
            return Str::finish(Str::start($messageId, '<'), '>');
        })->implode(' ');
    }
}
PK       ! G_)  )    Mailables/Address.phpnu ȯ        <?php

namespace Illuminate\Mail\Mailables;

class Address
{
    /**
     * The recipient's email address.
     *
     * @var string
     */
    public $address;

    /**
     * The recipient's name.
     *
     * @var string|null
     */
    public $name;

    /**
     * Create a new address instance.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return void
     */
    public function __construct(string $address, string $name = null)
    {
        $this->address = $address;
        $this->name = $name;
    }
}
PK       !         Mailables/Attachment.phpnu ȯ        <?php

namespace Illuminate\Mail\Mailables;

use Illuminate\Mail\Attachment as BaseAttachment;

class Attachment extends BaseAttachment
{
    // Here for namespace consistency...
}
PK       ! %?%  ?%    Mailables/Envelope.phpnu ȯ        <?php

namespace Illuminate\Mail\Mailables;

use Closure;
use Illuminate\Support\Arr;
use Illuminate\Support\Traits\Conditionable;

class Envelope
{
    use Conditionable;

    /**
     * The address sending the message.
     *
     * @var \Illuminate\Mail\Mailables\Address|string|null
     */
    public $from;

    /**
     * The recipients of the message.
     *
     * @var array
     */
    public $to;

    /**
     * The recipients receiving a copy of the message.
     *
     * @var array
     */
    public $cc;

    /**
     * The recipients receiving a blind copy of the message.
     *
     * @var array
     */
    public $bcc;

    /**
     * The recipients that should be replied to.
     *
     * @var array
     */
    public $replyTo;

    /**
     * The subject of the message.
     *
     * @var string|null
     */
    public $subject;

    /**
     * The message's tags.
     *
     * @var array
     */
    public $tags = [];

    /**
     * The message's meta data.
     *
     * @var array
     */
    public $metadata = [];

    /**
     * The message's Symfony Message customization callbacks.
     *
     * @var array
     */
    public $using = [];

    /**
     * Create a new message envelope instance.
     *
     * @param  \Illuminate\Mail\Mailables\Address|string|null  $from
     * @param  array<int, \Illuminate\Mail\Mailables\Address|string>  $to
     * @param  array<int, \Illuminate\Mail\Mailables\Address|string>  $cc
     * @param  array<int, \Illuminate\Mail\Mailables\Address|string>  $bcc
     * @param  array<int, \Illuminate\Mail\Mailables\Address|string>  $replyTo
     * @param  string|null  $subject
     * @param  array  $tags
     * @param  array  $metadata
     * @param  \Closure|array  $using
     * @return void
     *
     * @named-arguments-supported
     */
    public function __construct(Address|string $from = null, $to = [], $cc = [], $bcc = [], $replyTo = [], string $subject = null, array $tags = [], array $metadata = [], Closure|array $using = [])
    {
        $this->from = is_string($from) ? new Address($from) : $from;
        $this->to = $this->normalizeAddresses($to);
        $this->cc = $this->normalizeAddresses($cc);
        $this->bcc = $this->normalizeAddresses($bcc);
        $this->replyTo = $this->normalizeAddresses($replyTo);
        $this->subject = $subject;
        $this->tags = $tags;
        $this->metadata = $metadata;
        $this->using = Arr::wrap($using);
    }

    /**
     * Normalize the given array of addresses.
     *
     * @param  array<int, \Illuminate\Mail\Mailables\Address|string>  $addresses
     * @return array<int, \Illuminate\Mail\Mailables\Address>
     */
    protected function normalizeAddresses($addresses)
    {
        return collect($addresses)->map(function ($address) {
            return is_string($address) ? new Address($address) : $address;
        })->all();
    }

    /**
     * Specify who the message will be "from".
     *
     * @param  \Illuminate\Mail\Mailables\Address|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function from(Address|string $address, $name = null)
    {
        $this->from = is_string($address) ? new Address($address, $name) : $address;

        return $this;
    }

    /**
     * Add a "to" recipient to the message envelope.
     *
     * @param  \Illuminate\Mail\Mailables\Address|array<int, \Illuminate\Mail\Mailables\Address|string>|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function to(Address|array|string $address, $name = null)
    {
        $this->to = array_merge($this->to, $this->normalizeAddresses(
            is_string($name) ? [new Address($address, $name)] : Arr::wrap($address),
        ));

        return $this;
    }

    /**
     * Add a "cc" recipient to the message envelope.
     *
     * @param  \Illuminate\Mail\Mailables\Address|array<int, \Illuminate\Mail\Mailables\Address|string>|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function cc(Address|array|string $address, $name = null)
    {
        $this->cc = array_merge($this->cc, $this->normalizeAddresses(
            is_string($name) ? [new Address($address, $name)] : Arr::wrap($address),
        ));

        return $this;
    }

    /**
     * Add a "bcc" recipient to the message envelope.
     *
     * @param  \Illuminate\Mail\Mailables\Address|array<int, \Illuminate\Mail\Mailables\Address|string>|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function bcc(Address|array|string $address, $name = null)
    {
        $this->bcc = array_merge($this->bcc, $this->normalizeAddresses(
            is_string($name) ? [new Address($address, $name)] : Arr::wrap($address),
        ));

        return $this;
    }

    /**
     * Add a "reply to" recipient to the message envelope.
     *
     * @param  \Illuminate\Mail\Mailables\Address|array<int, \Illuminate\Mail\Mailables\Address|string>|string  $address
     * @param  string|null  $name
     * @return $this
     */
    public function replyTo(Address|array|string $address, $name = null)
    {
        $this->replyTo = array_merge($this->replyTo, $this->normalizeAddresses(
            is_string($name) ? [new Address($address, $name)] : Arr::wrap($address),
        ));

        return $this;
    }

    /**
     * Set the subject of the message.
     *
     * @param  string  $subject
     * @return $this
     */
    public function subject(string $subject)
    {
        $this->subject = $subject;

        return $this;
    }

    /**
     * Add "tags" to the message.
     *
     * @param  array  $tags
     * @return $this
     */
    public function tags(array $tags)
    {
        $this->tags = array_merge($this->tags, $tags);

        return $this;
    }

    /**
     * Add a "tag" to the message.
     *
     * @param  string  $tag
     * @return $this
     */
    public function tag(string $tag)
    {
        $this->tags[] = $tag;

        return $this;
    }

    /**
     * Add metadata to the message.
     *
     * @param  string  $key
     * @param  string|int  $value
     * @return $this
     */
    public function metadata(string $key, string|int $value)
    {
        $this->metadata[$key] = $value;

        return $this;
    }

    /**
     * Add a Symfony Message customization callback to the message.
     *
     * @param  \Closure  $callback
     * @return $this
     */
    public function using(Closure $callback)
    {
        $this->using[] = $callback;

        return $this;
    }

    /**
     * Determine if the message is from the given address.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function isFrom(string $address, string $name = null)
    {
        if (is_null($name)) {
            return $this->from->address === $address;
        }

        return $this->from->address === $address &&
               $this->from->name === $name;
    }

    /**
     * Determine if the message has the given address as a recipient.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasTo(string $address, string $name = null)
    {
        return $this->hasRecipient($this->to, $address, $name);
    }

    /**
     * Determine if the message has the given address as a "cc" recipient.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasCc(string $address, string $name = null)
    {
        return $this->hasRecipient($this->cc, $address, $name);
    }

    /**
     * Determine if the message has the given address as a "bcc" recipient.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasBcc(string $address, string $name = null)
    {
        return $this->hasRecipient($this->bcc, $address, $name);
    }

    /**
     * Determine if the message has the given address as a "reply to" recipient.
     *
     * @param  string  $address
     * @param  string|null  $name
     * @return bool
     */
    public function hasReplyTo(string $address, string $name = null)
    {
        return $this->hasRecipient($this->replyTo, $address, $name);
    }

    /**
     * Determine if the message has the given recipient.
     *
     * @param  array  $recipients
     * @param  string  $address
     * @param  string|null  $name
     * @return bool
     */
    protected function hasRecipient(array $recipients, string $address, ?string $name = null)
    {
        return collect($recipients)->contains(function ($recipient) use ($address, $name) {
            if (is_null($name)) {
                return $recipient->address === $address;
            }

            return $recipient->address === $address &&
                   $recipient->name === $name;
        });
    }

    /**
     * Determine if the message has the given subject.
     *
     * @param  string  $subject
     * @return bool
     */
    public function hasSubject(string $subject)
    {
        return $this->subject === $subject;
    }

    /**
     * Determine if the message has the given metadata.
     *
     * @param  string  $key
     * @param  string  $value
     * @return bool
     */
    public function hasMetadata(string $key, string $value)
    {
        return isset($this->metadata[$key]) && (string) $this->metadata[$key] === $value;
    }
}
PK       ! l6$  $    Mailables/Content.phpnu ȯ        <?php

namespace Illuminate\Mail\Mailables;

use Illuminate\Support\Traits\Conditionable;

class Content
{
    use Conditionable;

    /**
     * The Blade view that should be rendered for the mailable.
     *
     * @var string|null
     */
    public $view;

    /**
     * The Blade view that should be rendered for the mailable.
     *
     * Alternative syntax for "view".
     *
     * @var string|null
     */
    public $html;

    /**
     * The Blade view that represents the text version of the message.
     *
     * @var string|null
     */
    public $text;

    /**
     * The Blade view that represents the Markdown version of the message.
     *
     * @var string|null
     */
    public $markdown;

    /**
     * The pre-rendered HTML of the message.
     *
     * @var string|null
     */
    public $htmlString;

    /**
     * The message's view data.
     *
     * @var array
     */
    public $with;

    /**
     * Create a new content definition.
     *
     * @param  string|null  $view
     * @param  string|null  $html
     * @param  string|null  $text
     * @param  string|null  $markdown
     * @param  array  $with
     * @param  string|null  $htmlString
     *
     * @named-arguments-supported
     */
    public function __construct(string $view = null, string $html = null, string $text = null, $markdown = null, array $with = [], string $htmlString = null)
    {
        $this->view = $view;
        $this->html = $html;
        $this->text = $text;
        $this->markdown = $markdown;
        $this->with = $with;
        $this->htmlString = $htmlString;
    }

    /**
     * Set the view for the message.
     *
     * @param  string  $view
     * @return $this
     */
    public function view(string $view)
    {
        $this->view = $view;

        return $this;
    }

    /**
     * Set the view for the message.
     *
     * @param  string  $view
     * @return $this
     */
    public function html(string $view)
    {
        return $this->view($view);
    }

    /**
     * Set the plain text view for the message.
     *
     * @param  string  $view
     * @return $this
     */
    public function text(string $view)
    {
        $this->text = $view;

        return $this;
    }

    /**
     * Set the Markdown view for the message.
     *
     * @param  string  $view
     * @return $this
     */
    public function markdown(string $view)
    {
        $this->markdown = $view;

        return $this;
    }

    /**
     * Set the pre-rendered HTML for the message.
     *
     * @param  string  $html
     * @return $this
     */
    public function htmlString(string $html)
    {
        $this->htmlString = $html;

        return $this;
    }

    /**
     * Add a piece of view data to the message.
     *
     * @param  array|string  $key
     * @param  mixed|null  $value
     * @return $this
     */
    public function with($key, $value = null)
    {
        if (is_array($key)) {
            $this->with = array_merge($this->with, $key);
        } else {
            $this->with[$key] = $value;
        }

        return $this;
    }
}
PK       ! &#      MailServiceProvider.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class MailServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerIlluminateMailer();
        $this->registerMarkdownRenderer();
    }

    /**
     * Register the Illuminate mailer instance.
     *
     * @return void
     */
    protected function registerIlluminateMailer()
    {
        $this->app->singleton('mail.manager', function ($app) {
            return new MailManager($app);
        });

        $this->app->bind('mailer', function ($app) {
            return $app->make('mail.manager')->mailer();
        });
    }

    /**
     * Register the Markdown renderer instance.
     *
     * @return void
     */
    protected function registerMarkdownRenderer()
    {
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/resources/views' => $this->app->resourcePath('views/vendor/mail'),
            ], 'laravel-mail');
        }

        $this->app->singleton(Markdown::class, function ($app) {
            $config = $app->make('config');

            return new Markdown($app->make('view'), [
                'theme' => $config->get('mail.markdown.theme', 'default'),
                'paths' => $config->get('mail.markdown.paths', []),
            ]);
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return [
            'mail.manager',
            'mailer',
            Markdown::class,
        ];
    }
}
PK       ! l[      TextMessage.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Support\Traits\ForwardsCalls;

/**
 * @mixin \Illuminate\Mail\Message
 */
class TextMessage
{
    use ForwardsCalls;

    /**
     * The underlying message instance.
     *
     * @var \Illuminate\Mail\Message
     */
    protected $message;

    /**
     * Create a new text message instance.
     *
     * @param  \Illuminate\Mail\Message  $message
     * @return void
     */
    public function __construct($message)
    {
        $this->message = $message;
    }

    /**
     * Embed a file in the message and get the CID.
     *
     * @param  string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment  $file
     * @return string
     */
    public function embed($file)
    {
        return '';
    }

    /**
     * Embed in-memory data in the message and get the CID.
     *
     * @param  string|resource  $data
     * @param  string  $name
     * @param  string|null  $contentType
     * @return string
     */
    public function embedData($data, $name, $contentType = null)
    {
        return '';
    }

    /**
     * Dynamically pass missing methods to the underlying message instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->forwardDecoratedCallTo($this->message, $method, $parameters);
    }
}
PK       ! oMة      Attachment.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Closure;
use Illuminate\Container\Container;
use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory;
use Illuminate\Support\Traits\Macroable;
use RuntimeException;

class Attachment
{
    use Macroable;

    /**
     * The attached file's filename.
     *
     * @var string|null
     */
    public $as;

    /**
     * The attached file's mime type.
     *
     * @var string|null
     */
    public $mime;

    /**
     * A callback that attaches the attachment to the mail message.
     *
     * @var \Closure
     */
    protected $resolver;

    /**
     * Create a mail attachment.
     *
     * @param  \Closure  $resolver
     * @return void
     */
    private function __construct(Closure $resolver)
    {
        $this->resolver = $resolver;
    }

    /**
     * Create a mail attachment from a path.
     *
     * @param  string  $path
     * @return static
     */
    public static function fromPath($path)
    {
        return new static(fn ($attachment, $pathStrategy) => $pathStrategy($path, $attachment));
    }

    /**
     * Create a mail attachment from in-memory data.
     *
     * @param  \Closure  $data
     * @param  string|null  $name
     * @return static
     */
    public static function fromData(Closure $data, $name = null)
    {
        return (new static(
            fn ($attachment, $pathStrategy, $dataStrategy) => $dataStrategy($data, $attachment)
        ))->as($name);
    }

    /**
     * Create a mail attachment from a file in the default storage disk.
     *
     * @param  string  $path
     * @return static
     */
    public static function fromStorage($path)
    {
        return static::fromStorageDisk(null, $path);
    }

    /**
     * Create a mail attachment from a file in the specified storage disk.
     *
     * @param  string|null  $disk
     * @param  string  $path
     * @return static
     */
    public static function fromStorageDisk($disk, $path)
    {
        return new static(function ($attachment, $pathStrategy, $dataStrategy) use ($disk, $path) {
            $storage = Container::getInstance()->make(
                FilesystemFactory::class
            )->disk($disk);

            $attachment
                ->as($attachment->as ?? basename($path))
                ->withMime($attachment->mime ?? $storage->mimeType($path));

            return $dataStrategy(fn () => $storage->get($path), $attachment);
        });
    }

    /**
     * Set the attached file's filename.
     *
     * @param  string|null  $name
     * @return $this
     */
    public function as($name)
    {
        $this->as = $name;

        return $this;
    }

    /**
     * Set the attached file's mime type.
     *
     * @param  string  $mime
     * @return $this
     */
    public function withMime($mime)
    {
        $this->mime = $mime;

        return $this;
    }

    /**
     * Attach the attachment with the given strategies.
     *
     * @param  \Closure  $pathStrategy
     * @param  \Closure  $dataStrategy
     * @return mixed
     */
    public function attachWith(Closure $pathStrategy, Closure $dataStrategy)
    {
        return ($this->resolver)($this, $pathStrategy, $dataStrategy);
    }

    /**
     * Attach the attachment to a built-in mail type.
     *
     * @param  \Illuminate\Mail\Mailable|\Illuminate\Mail\Message|\Illuminate\Notifications\Messages\MailMessage  $mail
     * @param  array  $options
     * @return mixed
     */
    public function attachTo($mail, $options = [])
    {
        return $this->attachWith(
            fn ($path) => $mail->attach($path, [
                'as' => $options['as'] ?? $this->as,
                'mime' => $options['mime'] ?? $this->mime,
            ]),
            function ($data) use ($mail, $options) {
                $options = [
                    'as' => $options['as'] ?? $this->as,
                    'mime' => $options['mime'] ?? $this->mime,
                ];

                if ($options['as'] === null) {
                    throw new RuntimeException('Attachment requires a filename to be specified.');
                }

                return $mail->attachData($data(), $options['as'], ['mime' => $options['mime']]);
            }
        );
    }

    /**
     * Determine if the given attachment is equivalent to this attachment.
     *
     * @param  \Illuminate\Mail\Attachment  $attachment
     * @param  array  $options
     * @return bool
     */
    public function isEquivalent(Attachment $attachment, $options = [])
    {
        return with([
            'as' => $options['as'] ?? $attachment->as,
            'mime' => $options['mime'] ?? $attachment->mime,
        ], fn ($options) => $this->attachWith(
            fn ($path) => [$path, ['as' => $this->as, 'mime' => $this->mime]],
            fn ($data) => [$data(), ['as' => $this->as, 'mime' => $this->mime]],
        ) === $attachment->attachWith(
            fn ($path) => [$path, $options],
            fn ($data) => [$data(), $options],
        ));
    }
}
PK       ! W      SentMessage.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Support\Traits\ForwardsCalls;
use Symfony\Component\Mailer\SentMessage as SymfonySentMessage;

/**
 * @mixin \Symfony\Component\Mailer\SentMessage
 */
class SentMessage
{
    use ForwardsCalls;

    /**
     * The Symfony SentMessage instance.
     *
     * @var \Symfony\Component\Mailer\SentMessage
     */
    protected $sentMessage;

    /**
     * Create a new SentMessage instance.
     *
     * @param  \Symfony\Component\Mailer\SentMessage  $sentMessage
     * @return void
     */
    public function __construct(SymfonySentMessage $sentMessage)
    {
        $this->sentMessage = $sentMessage;
    }

    /**
     * Get the underlying Symfony Email instance.
     *
     * @return \Symfony\Component\Mailer\SentMessage
     */
    public function getSymfonySentMessage()
    {
        return $this->sentMessage;
    }

    /**
     * Dynamically pass missing methods to the Symfony instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->forwardCallTo($this->sentMessage, $method, $parameters);
    }

    /**
     * Get the serializable representation of the object.
     *
     * @return array
     */
    public function __serialize()
    {
        $hasAttachments = collect($this->sentMessage->getOriginalMessage()->getAttachments())->isNotEmpty();

        return [
            'hasAttachments' => $hasAttachments,
            'sentMessage' => $hasAttachments ? base64_encode(serialize($this->sentMessage)) : $this->sentMessage,
        ];
    }

    /**
     * Marshal the object from its serialized data.
     *
     * @param  array  $data
     * @return void
     */
    public function __unserialize(array $data)
    {
        $hasAttachments = ($data['hasAttachments'] ?? false) === true;

        $this->sentMessage = $hasAttachments ? unserialize(base64_decode($data['sentMessage'])) : $data['sentMessage'];
    }
}
PK       ! ~Dc  c    SendQueuedMailable.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Mail\Factory as MailFactory;
use Illuminate\Contracts\Mail\Mailable as MailableContract;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueueAfterCommit;
use Illuminate\Queue\InteractsWithQueue;

class SendQueuedMailable
{
    use Queueable, InteractsWithQueue;

    /**
     * The mailable message instance.
     *
     * @var \Illuminate\Contracts\Mail\Mailable
     */
    public $mailable;

    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries;

    /**
     * The number of seconds the job can run before timing out.
     *
     * @var int
     */
    public $timeout;

    /**
     * The maximum number of unhandled exceptions to allow before failing.
     *
     * @return int|null
     */
    public $maxExceptions;

    /**
     * Indicates if the job should be encrypted.
     *
     * @var bool
     */
    public $shouldBeEncrypted = false;

    /**
     * Create a new job instance.
     *
     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
     * @return void
     */
    public function __construct(MailableContract $mailable)
    {
        $this->mailable = $mailable;

        if ($mailable instanceof ShouldQueueAfterCommit) {
            $this->afterCommit = true;
        } else {
            $this->afterCommit = property_exists($mailable, 'afterCommit') ? $mailable->afterCommit : null;
        }

        $this->connection = property_exists($mailable, 'connection') ? $mailable->connection : null;
        $this->maxExceptions = property_exists($mailable, 'maxExceptions') ? $mailable->maxExceptions : null;
        $this->queue = property_exists($mailable, 'queue') ? $mailable->queue : null;
        $this->shouldBeEncrypted = $mailable instanceof ShouldBeEncrypted;
        $this->timeout = property_exists($mailable, 'timeout') ? $mailable->timeout : null;
        $this->tries = property_exists($mailable, 'tries') ? $mailable->tries : null;
    }

    /**
     * Handle the queued job.
     *
     * @param  \Illuminate\Contracts\Mail\Factory  $factory
     * @return void
     */
    public function handle(MailFactory $factory)
    {
        $this->mailable->send($factory);
    }

    /**
     * Get the number of seconds before a released mailable will be available.
     *
     * @return mixed
     */
    public function backoff()
    {
        if (! method_exists($this->mailable, 'backoff') && ! isset($this->mailable->backoff)) {
            return;
        }

        return $this->mailable->backoff ?? $this->mailable->backoff();
    }

    /**
     * Determine the time at which the job should timeout.
     *
     * @return \DateTime|null
     */
    public function retryUntil()
    {
        if (! method_exists($this->mailable, 'retryUntil') && ! isset($this->mailable->retryUntil)) {
            return;
        }

        return $this->mailable->retryUntil ?? $this->mailable->retryUntil();
    }

    /**
     * Call the failed method on the mailable instance.
     *
     * @param  \Throwable  $e
     * @return void
     */
    public function failed($e)
    {
        if (method_exists($this->mailable, 'failed')) {
            $this->mailable->failed($e);
        }
    }

    /**
     * Get the display name for the queued job.
     *
     * @return string
     */
    public function displayName()
    {
        return get_class($this->mailable);
    }

    /**
     * Prepare the instance for cloning.
     *
     * @return void
     */
    public function __clone()
    {
        $this->mailable = clone $this->mailable;
    }
}
PK       ! IU&s'  s'    Message.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\ForwardsCalls;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\File;

/**
 * @mixin \Symfony\Component\Mime\Email
 */
class Message
{
    use ForwardsCalls;

    /**
     * The Symfony Email instance.
     *
     * @var \Symfony\Component\Mime\Email
     */
    protected $message;

    /**
     * CIDs of files embedded in the message.
     *
     * @deprecated Will be removed in a future Laravel version.
     *
     * @var array
     */
    protected $embeddedFiles = [];

    /**
     * Create a new message instance.
     *
     * @param  \Symfony\Component\Mime\Email  $message
     * @return void
     */
    public function __construct(Email $message)
    {
        $this->message = $message;
    }

    /**
     * Add a "from" address to the message.
     *
     * @param  string|array  $address
     * @param  string|null  $name
     * @return $this
     */
    public function from($address, $name = null)
    {
        is_array($address)
            ? $this->message->from(...$address)
            : $this->message->from(new Address($address, (string) $name));

        return $this;
    }

    /**
     * Set the "sender" of the message.
     *
     * @param  string|array  $address
     * @param  string|null  $name
     * @return $this
     */
    public function sender($address, $name = null)
    {
        is_array($address)
            ? $this->message->sender(...$address)
            : $this->message->sender(new Address($address, (string) $name));

        return $this;
    }

    /**
     * Set the "return path" of the message.
     *
     * @param  string  $address
     * @return $this
     */
    public function returnPath($address)
    {
        $this->message->returnPath($address);

        return $this;
    }

    /**
     * Add a recipient to the message.
     *
     * @param  string|array  $address
     * @param  string|null  $name
     * @param  bool  $override
     * @return $this
     */
    public function to($address, $name = null, $override = false)
    {
        if ($override) {
            is_array($address)
                ? $this->message->to(...$address)
                : $this->message->to(new Address($address, (string) $name));

            return $this;
        }

        return $this->addAddresses($address, $name, 'To');
    }

    /**
     * Remove all "to" addresses from the message.
     *
     * @return $this
     */
    public function forgetTo()
    {
        if ($header = $this->message->getHeaders()->get('To')) {
            $this->addAddressDebugHeader('X-To', $this->message->getTo());

            $header->setAddresses([]);
        }

        return $this;
    }

    /**
     * Add a carbon copy to the message.
     *
     * @param  string|array  $address
     * @param  string|null  $name
     * @param  bool  $override
     * @return $this
     */
    public function cc($address, $name = null, $override = false)
    {
        if ($override) {
            is_array($address)
                ? $this->message->cc(...$address)
                : $this->message->cc(new Address($address, (string) $name));

            return $this;
        }

        return $this->addAddresses($address, $name, 'Cc');
    }

    /**
     * Remove all carbon copy addresses from the message.
     *
     * @return $this
     */
    public function forgetCc()
    {
        if ($header = $this->message->getHeaders()->get('Cc')) {
            $this->addAddressDebugHeader('X-Cc', $this->message->getCC());

            $header->setAddresses([]);
        }

        return $this;
    }

    /**
     * Add a blind carbon copy to the message.
     *
     * @param  string|array  $address
     * @param  string|null  $name
     * @param  bool  $override
     * @return $this
     */
    public function bcc($address, $name = null, $override = false)
    {
        if ($override) {
            is_array($address)
                ? $this->message->bcc(...$address)
                : $this->message->bcc(new Address($address, (string) $name));

            return $this;
        }

        return $this->addAddresses($address, $name, 'Bcc');
    }

    /**
     * Remove all of the blind carbon copy addresses from the message.
     *
     * @return $this
     */
    public function forgetBcc()
    {
        if ($header = $this->message->getHeaders()->get('Bcc')) {
            $this->addAddressDebugHeader('X-Bcc', $this->message->getBcc());

            $header->setAddresses([]);
        }

        return $this;
    }

    /**
     * Add a "reply to" address to the message.
     *
     * @param  string|array  $address
     * @param  string|null  $name
     * @return $this
     */
    public function replyTo($address, $name = null)
    {
        return $this->addAddresses($address, $name, 'ReplyTo');
    }

    /**
     * Add a recipient to the message.
     *
     * @param  string|array  $address
     * @param  string  $name
     * @param  string  $type
     * @return $this
     */
    protected function addAddresses($address, $name, $type)
    {
        if (is_array($address)) {
            $type = lcfirst($type);

            $addresses = collect($address)->map(function ($address, $key) {
                if (is_string($key) && is_string($address)) {
                    return new Address($key, $address);
                }

                if (is_array($address)) {
                    return new Address($address['email'] ?? $address['address'], $address['name'] ?? null);
                }

                if (is_null($address)) {
                    return new Address($key);
                }

                return $address;
            })->all();

            $this->message->{"{$type}"}(...$addresses);
        } else {
            $this->message->{"add{$type}"}(new Address($address, (string) $name));
        }

        return $this;
    }

    /**
     * Add an address debug header for a list of recipients.
     *
     * @param  string  $header
     * @param  \Symfony\Component\Mime\Address[]  $addresses
     * @return $this
     */
    protected function addAddressDebugHeader(string $header, array $addresses)
    {
        $this->message->getHeaders()->addTextHeader(
            $header,
            implode(', ', array_map(fn ($a) => $a->toString(), $addresses)),
        );

        return $this;
    }

    /**
     * Set the subject of the message.
     *
     * @param  string  $subject
     * @return $this
     */
    public function subject($subject)
    {
        $this->message->subject($subject);

        return $this;
    }

    /**
     * Set the message priority level.
     *
     * @param  int  $level
     * @return $this
     */
    public function priority($level)
    {
        $this->message->priority($level);

        return $this;
    }

    /**
     * Attach a file to the message.
     *
     * @param  string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment  $file
     * @param  array  $options
     * @return $this
     */
    public function attach($file, array $options = [])
    {
        if ($file instanceof Attachable) {
            $file = $file->toMailAttachment();
        }

        if ($file instanceof Attachment) {
            return $file->attachTo($this);
        }

        $this->message->attachFromPath($file, $options['as'] ?? null, $options['mime'] ?? null);

        return $this;
    }

    /**
     * Attach in-memory data as an attachment.
     *
     * @param  string|resource  $data
     * @param  string  $name
     * @param  array  $options
     * @return $this
     */
    public function attachData($data, $name, array $options = [])
    {
        $this->message->attach($data, $name, $options['mime'] ?? null);

        return $this;
    }

    /**
     * Embed a file in the message and get the CID.
     *
     * @param  string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment  $file
     * @return string
     */
    public function embed($file)
    {
        if ($file instanceof Attachable) {
            $file = $file->toMailAttachment();
        }

        if ($file instanceof Attachment) {
            return $file->attachWith(
                function ($path) use ($file) {
                    $cid = $file->as ?? Str::random();

                    $this->message->addPart(
                        (new DataPart(new File($path), $cid, $file->mime))->asInline()
                    );

                    return "cid:{$cid}";
                },
                function ($data) use ($file) {
                    $this->message->addPart(
                        (new DataPart($data(), $file->as, $file->mime))->asInline()
                    );

                    return "cid:{$file->as}";
                }
            );
        }

        $cid = Str::random(10);

        $this->message->addPart(
            (new DataPart(new File($file), $cid))->asInline()
        );

        return "cid:$cid";
    }

    /**
     * Embed in-memory data in the message and get the CID.
     *
     * @param  string|resource  $data
     * @param  string  $name
     * @param  string|null  $contentType
     * @return string
     */
    public function embedData($data, $name, $contentType = null)
    {
        $this->message->addPart(
            (new DataPart($data, $name, $contentType))->asInline()
        );

        return "cid:$name";
    }

    /**
     * Get the underlying Symfony Email instance.
     *
     * @return \Symfony\Component\Mime\Email
     */
    public function getSymfonyMessage()
    {
        return $this->message;
    }

    /**
     * Dynamically pass missing methods to the Symfony instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->forwardDecoratedCallTo($this->message, $method, $parameters);
    }
}
PK       ! o      PendingMail.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Illuminate\Contracts\Mail\Mailable as MailableContract;
use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Contracts\Translation\HasLocalePreference;
use Illuminate\Support\Traits\Conditionable;

class PendingMail
{
    use Conditionable;

    /**
     * The mailer instance.
     *
     * @var \Illuminate\Contracts\Mail\Mailer
     */
    protected $mailer;

    /**
     * The locale of the message.
     *
     * @var string
     */
    protected $locale;

    /**
     * The "to" recipients of the message.
     *
     * @var array
     */
    protected $to = [];

    /**
     * The "cc" recipients of the message.
     *
     * @var array
     */
    protected $cc = [];

    /**
     * The "bcc" recipients of the message.
     *
     * @var array
     */
    protected $bcc = [];

    /**
     * Create a new mailable mailer instance.
     *
     * @param  \Illuminate\Contracts\Mail\Mailer  $mailer
     * @return void
     */
    public function __construct(MailerContract $mailer)
    {
        $this->mailer = $mailer;
    }

    /**
     * Set the locale of the message.
     *
     * @param  string  $locale
     * @return $this
     */
    public function locale($locale)
    {
        $this->locale = $locale;

        return $this;
    }

    /**
     * Set the recipients of the message.
     *
     * @param  mixed  $users
     * @return $this
     */
    public function to($users)
    {
        $this->to = $users;

        if (! $this->locale && $users instanceof HasLocalePreference) {
            $this->locale($users->preferredLocale());
        }

        return $this;
    }

    /**
     * Set the recipients of the message.
     *
     * @param  mixed  $users
     * @return $this
     */
    public function cc($users)
    {
        $this->cc = $users;

        return $this;
    }

    /**
     * Set the recipients of the message.
     *
     * @param  mixed  $users
     * @return $this
     */
    public function bcc($users)
    {
        $this->bcc = $users;

        return $this;
    }

    /**
     * Send a new mailable message instance.
     *
     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
     * @return \Illuminate\Mail\SentMessage|null
     */
    public function send(MailableContract $mailable)
    {
        return $this->mailer->send($this->fill($mailable));
    }

    /**
     * Push the given mailable onto the queue.
     *
     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
     * @return mixed
     */
    public function queue(MailableContract $mailable)
    {
        return $this->mailer->queue($this->fill($mailable));
    }

    /**
     * Deliver the queued message after (n) seconds.
     *
     * @param  \DateTimeInterface|\DateInterval|int  $delay
     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
     * @return mixed
     */
    public function later($delay, MailableContract $mailable)
    {
        return $this->mailer->later($delay, $this->fill($mailable));
    }

    /**
     * Populate the mailable with the addresses.
     *
     * @param  \Illuminate\Contracts\Mail\Mailable  $mailable
     * @return \Illuminate\Mail\Mailable
     */
    protected function fill(MailableContract $mailable)
    {
        return tap($mailable->to($this->to)
            ->cc($this->cc)
            ->bcc($this->bcc), function (MailableContract $mailable) {
                if ($this->locale) {
                    $mailable->locale($this->locale);
                }
            });
    }
}
PK       ! α3  3  
  LICENSE.mdnu ȯ        The MIT License (MIT)

Copyright (c) Taylor Otwell

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PK       ! (~      %  resources/views/text/footer.blade.phpnu ȯ        {{ $slot }}
PK       ! (~      $  resources/views/text/panel.blade.phpnu ȯ        {{ $slot }}
PK       ! (~      $  resources/views/text/table.blade.phpnu ȯ        {{ $slot }}
PK       ! (~      &  resources/views/text/subcopy.blade.phpnu ȯ        {{ $slot }}
PK       ! A      %  resources/views/text/header.blade.phpnu ȯ        {{ $slot }}: {{ $url }}
PK       ! [w%}      %  resources/views/text/layout.blade.phpnu ȯ        {!! strip_tags($header ?? '') !!}

{!! strip_tags($slot) !!}
@isset($subcopy)

{!! strip_tags($subcopy) !!}
@endisset

{!! strip_tags($footer ?? '') !!}
PK       ! yf    &  resources/views/text/message.blade.phpnu ȯ        <x-mail::layout>
    {{-- Header --}}
    <x-slot:header>
        <x-mail::header :url="config('app.url')">
            {{ config('app.name') }}
        </x-mail::header>
    </x-slot:header>

    {{-- Body --}}
    {{ $slot }}

    {{-- Subcopy --}}
    @isset($subcopy)
        <x-slot:subcopy>
            <x-mail::subcopy>
                {{ $subcopy }}
            </x-mail::subcopy>
        </x-slot:subcopy>
    @endisset

    {{-- Footer --}}
    <x-slot:footer>
        <x-mail::footer>
            © {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
        </x-mail::footer>
    </x-slot:footer>
</x-mail::layout>
PK       ! A      %  resources/views/text/button.blade.phpnu ȯ        {{ $slot }}: {{ $url }}
PK       ! =g      %  resources/views/html/footer.blade.phpnu ȯ        <tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
PK       ! 5ZV5  5  $  resources/views/html/panel.blade.phpnu ȯ        <table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>

PK       ! (H   H   $  resources/views/html/table.blade.phpnu ȯ        <div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>
PK       ! d;    '  resources/views/html/themes/default.cssnu ȯ        /* Base */

body,
body *:not(html):not(style):not(br):not(tr):not(code) {
    box-sizing: border-box;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
        'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
    position: relative;
}

body {
    -webkit-text-size-adjust: none;
    background-color: #ffffff;
    color: #718096;
    height: 100%;
    line-height: 1.4;
    margin: 0;
    padding: 0;
    width: 100% !important;
}

p,
ul,
ol,
blockquote {
    line-height: 1.4;
    text-align: left;
}

a {
    color: #3869d4;
}

a img {
    border: none;
}

/* Typography */

h1 {
    color: #3d4852;
    font-size: 18px;
    font-weight: bold;
    margin-top: 0;
    text-align: left;
}

h2 {
    font-size: 16px;
    font-weight: bold;
    margin-top: 0;
    text-align: left;
}

h3 {
    font-size: 14px;
    font-weight: bold;
    margin-top: 0;
    text-align: left;
}

p {
    font-size: 16px;
    line-height: 1.5em;
    margin-top: 0;
    text-align: left;
}

p.sub {
    font-size: 12px;
}

img {
    max-width: 100%;
}

/* Layout */

.wrapper {
    -premailer-cellpadding: 0;
    -premailer-cellspacing: 0;
    -premailer-width: 100%;
    background-color: #edf2f7;
    margin: 0;
    padding: 0;
    width: 100%;
}

.content {
    -premailer-cellpadding: 0;
    -premailer-cellspacing: 0;
    -premailer-width: 100%;
    margin: 0;
    padding: 0;
    width: 100%;
}

/* Header */

.header {
    padding: 25px 0;
    text-align: center;
}

.header a {
    color: #3d4852;
    font-size: 19px;
    font-weight: bold;
    text-decoration: none;
}

/* Logo */

.logo {
    height: 75px;
    max-height: 75px;
    width: 75px;
}

/* Body */

.body {
    -premailer-cellpadding: 0;
    -premailer-cellspacing: 0;
    -premailer-width: 100%;
    background-color: #edf2f7;
    border-bottom: 1px solid #edf2f7;
    border-top: 1px solid #edf2f7;
    margin: 0;
    padding: 0;
    width: 100%;
}

.inner-body {
    -premailer-cellpadding: 0;
    -premailer-cellspacing: 0;
    -premailer-width: 570px;
    background-color: #ffffff;
    border-color: #e8e5ef;
    border-radius: 2px;
    border-width: 1px;
    box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
    margin: 0 auto;
    padding: 0;
    width: 570px;
}

/* Subcopy */

.subcopy {
    border-top: 1px solid #e8e5ef;
    margin-top: 25px;
    padding-top: 25px;
}

.subcopy p {
    font-size: 14px;
}

/* Footer */

.footer {
    -premailer-cellpadding: 0;
    -premailer-cellspacing: 0;
    -premailer-width: 570px;
    margin: 0 auto;
    padding: 0;
    text-align: center;
    width: 570px;
}

.footer p {
    color: #b0adc5;
    font-size: 12px;
    text-align: center;
}

.footer a {
    color: #b0adc5;
    text-decoration: underline;
}

/* Tables */

.table table {
    -premailer-cellpadding: 0;
    -premailer-cellspacing: 0;
    -premailer-width: 100%;
    margin: 30px auto;
    width: 100%;
}

.table th {
    border-bottom: 1px solid #edeff2;
    margin: 0;
    padding-bottom: 8px;
}

.table td {
    color: #74787e;
    font-size: 15px;
    line-height: 18px;
    margin: 0;
    padding: 10px 0;
}

.content-cell {
    max-width: 100vw;
    padding: 32px;
}

/* Buttons */

.action {
    -premailer-cellpadding: 0;
    -premailer-cellspacing: 0;
    -premailer-width: 100%;
    margin: 30px auto;
    padding: 0;
    text-align: center;
    width: 100%;
}

.button {
    -webkit-text-size-adjust: none;
    border-radius: 4px;
    color: #fff;
    display: inline-block;
    overflow: hidden;
    text-decoration: none;
}

.button-blue,
.button-primary {
    background-color: #2d3748;
    border-bottom: 8px solid #2d3748;
    border-left: 18px solid #2d3748;
    border-right: 18px solid #2d3748;
    border-top: 8px solid #2d3748;
}

.button-green,
.button-success {
    background-color: #48bb78;
    border-bottom: 8px solid #48bb78;
    border-left: 18px solid #48bb78;
    border-right: 18px solid #48bb78;
    border-top: 8px solid #48bb78;
}

.button-red,
.button-error {
    background-color: #e53e3e;
    border-bottom: 8px solid #e53e3e;
    border-left: 18px solid #e53e3e;
    border-right: 18px solid #e53e3e;
    border-top: 8px solid #e53e3e;
}

/* Panels */

.panel {
    border-left: #2d3748 solid 4px;
    margin: 21px 0;
}

.panel-content {
    background-color: #edf2f7;
    color: #718096;
    padding: 16px;
}

.panel-content p {
    color: #718096;
}

.panel-item {
    padding: 0;
}

.panel-item p:last-of-type {
    margin-bottom: 0;
    padding-bottom: 0;
}

/* Utilities */

.break-all {
    word-break: break-all;
}
PK       ! '      &  resources/views/html/subcopy.blade.phpnu ȯ        <table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
PK       ! X|ι    %  resources/views/html/header.blade.phpnu ȯ        @props(['url'])
<tr>
<td class="header">
<a href="{{ $url }}" style="display: inline-block;">
@if (trim($slot) === 'Laravel')
<img src="https://laravel.com/img/notification-logo.png" class="logo" alt="Laravel Logo">
@else
{{ $slot }}
@endif
</a>
</td>
</tr>
PK       ! 
xU  U  %  resources/views/html/layout.blade.phpnu ȯ        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{ config('app.name') }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="color-scheme" content="light">
<meta name="supported-color-schemes" content="light">
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}

.footer {
width: 100% !important;
}
}

@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
</head>
<body>

<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
{{ $header ?? '' }}

<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0" style="border: hidden !important;">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<!-- Body content -->
<tr>
<td class="content-cell">
{{ Illuminate\Mail\Markdown::parse($slot) }}

{{ $subcopy ?? '' }}
</td>
</tr>
</table>
</td>
</tr>

{{ $footer ?? '' }}
</table>
</td>
</tr>
</table>
</body>
</html>
PK       ! aNP    &  resources/views/html/message.blade.phpnu ȯ        <x-mail::layout>
{{-- Header --}}
<x-slot:header>
<x-mail::header :url="config('app.url')">
{{ config('app.name') }}
</x-mail::header>
</x-slot:header>

{{-- Body --}}
{{ $slot }}

{{-- Subcopy --}}
@isset($subcopy)
<x-slot:subcopy>
<x-mail::subcopy>
{{ $subcopy }}
</x-mail::subcopy>
</x-slot:subcopy>
@endisset

{{-- Footer --}}
<x-slot:footer>
<x-mail::footer>
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
</x-mail::footer>
</x-slot:footer>
</x-mail::layout>
PK       ! ;J!@  @  %  resources/views/html/button.blade.phpnu ȯ        @props([
    'url',
    'color' => 'primary',
    'align' => 'center',
])
<table class="action" align="{{ $align }}" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color }}" target="_blank" rel="noopener">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
PK       ! u`D  D    MailManager.phpnu ȯ        <?php

namespace Illuminate\Mail;

use Aws\Ses\SesClient;
use Aws\SesV2\SesV2Client;
use Closure;
use Illuminate\Contracts\Mail\Factory as FactoryContract;
use Illuminate\Log\LogManager;
use Illuminate\Mail\Transport\ArrayTransport;
use Illuminate\Mail\Transport\LogTransport;
use Illuminate\Mail\Transport\SesTransport;
use Illuminate\Mail\Transport\SesV2Transport;
use Illuminate\Support\Arr;
use Illuminate\Support\ConfigurationUrlParser;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory;
use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory;
use Symfony\Component\Mailer\Transport\Dsn;
use Symfony\Component\Mailer\Transport\FailoverTransport;
use Symfony\Component\Mailer\Transport\RoundRobinTransport;
use Symfony\Component\Mailer\Transport\SendmailTransport;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory;
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;

/**
 * @mixin \Illuminate\Mail\Mailer
 */
class MailManager implements FactoryContract
{
    /**
     * The application instance.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * The array of resolved mailers.
     *
     * @var array
     */
    protected $mailers = [];

    /**
     * The registered custom driver creators.
     *
     * @var array
     */
    protected $customCreators = [];

    /**
     * Create a new Mail manager instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function __construct($app)
    {
        $this->app = $app;
    }

    /**
     * Get a mailer instance by name.
     *
     * @param  string|null  $name
     * @return \Illuminate\Contracts\Mail\Mailer
     */
    public function mailer($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return $this->mailers[$name] = $this->get($name);
    }

    /**
     * Get a mailer driver instance.
     *
     * @param  string|null  $driver
     * @return \Illuminate\Mail\Mailer
     */
    public function driver($driver = null)
    {
        return $this->mailer($driver);
    }

    /**
     * Attempt to get the mailer from the local cache.
     *
     * @param  string  $name
     * @return \Illuminate\Mail\Mailer
     */
    protected function get($name)
    {
        return $this->mailers[$name] ?? $this->resolve($name);
    }

    /**
     * Resolve the given mailer.
     *
     * @param  string  $name
     * @return \Illuminate\Mail\Mailer
     *
     * @throws \InvalidArgumentException
     */
    protected function resolve($name)
    {
        $config = $this->getConfig($name);

        if (is_null($config)) {
            throw new InvalidArgumentException("Mailer [{$name}] is not defined.");
        }

        // Once we have created the mailer instance we will set a container instance
        // on the mailer. This allows us to resolve mailer classes via containers
        // for maximum testability on said classes instead of passing Closures.
        $mailer = new Mailer(
            $name,
            $this->app['view'],
            $this->createSymfonyTransport($config),
            $this->app['events']
        );

        if ($this->app->bound('queue')) {
            $mailer->setQueue($this->app['queue']);
        }

        // Next we will set all of the global addresses on this mailer, which allows
        // for easy unification of all "from" addresses as well as easy debugging
        // of sent messages since these will be sent to a single email address.
        foreach (['from', 'reply_to', 'to', 'return_path'] as $type) {
            $this->setGlobalAddress($mailer, $config, $type);
        }

        return $mailer;
    }

    /**
     * Create a new transport instance.
     *
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Transport\TransportInterface
     *
     * @throws \InvalidArgumentException
     */
    public function createSymfonyTransport(array $config)
    {
        // Here we will check if the "transport" key exists and if it doesn't we will
        // assume an application is still using the legacy mail configuration file
        // format and use the "mail.driver" configuration option instead for BC.
        $transport = $config['transport'] ?? $this->app['config']['mail.driver'];

        if (isset($this->customCreators[$transport])) {
            return call_user_func($this->customCreators[$transport], $config);
        }

        if (trim($transport ?? '') === '' ||
            ! method_exists($this, $method = 'create'.ucfirst(Str::camel($transport)).'Transport')) {
            throw new InvalidArgumentException("Unsupported mail transport [{$transport}].");
        }

        return $this->{$method}($config);
    }

    /**
     * Create an instance of the Symfony SMTP Transport driver.
     *
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport
     */
    protected function createSmtpTransport(array $config)
    {
        $factory = new EsmtpTransportFactory;

        $scheme = $config['scheme'] ?? null;

        if (! $scheme) {
            $scheme = ! empty($config['encryption']) && $config['encryption'] === 'tls'
                ? (($config['port'] == 465) ? 'smtps' : 'smtp')
                : '';
        }

        $transport = $factory->create(new Dsn(
            $scheme,
            $config['host'],
            $config['username'] ?? null,
            $config['password'] ?? null,
            $config['port'] ?? null,
            $config
        ));

        return $this->configureSmtpTransport($transport, $config);
    }

    /**
     * Configure the additional SMTP driver options.
     *
     * @param  \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport  $transport
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport
     */
    protected function configureSmtpTransport(EsmtpTransport $transport, array $config)
    {
        $stream = $transport->getStream();

        if ($stream instanceof SocketStream) {
            if (isset($config['source_ip'])) {
                $stream->setSourceIp($config['source_ip']);
            }

            if (isset($config['timeout'])) {
                $stream->setTimeout($config['timeout']);
            }
        }

        return $transport;
    }

    /**
     * Create an instance of the Symfony Sendmail Transport driver.
     *
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Transport\SendmailTransport
     */
    protected function createSendmailTransport(array $config)
    {
        return new SendmailTransport(
            $config['path'] ?? $this->app['config']->get('mail.sendmail')
        );
    }

    /**
     * Create an instance of the Symfony Amazon SES Transport driver.
     *
     * @param  array  $config
     * @return \Illuminate\Mail\Transport\SesTransport
     */
    protected function createSesTransport(array $config)
    {
        $config = array_merge(
            $this->app['config']->get('services.ses', []),
            ['version' => 'latest', 'service' => 'email'],
            $config
        );

        $config = Arr::except($config, ['transport']);

        return new SesTransport(
            new SesClient($this->addSesCredentials($config)),
            $config['options'] ?? []
        );
    }

    /**
     * Create an instance of the Symfony Amazon SES V2 Transport driver.
     *
     * @param  array  $config
     * @return \Illuminate\Mail\Transport\SesV2Transport
     */
    protected function createSesV2Transport(array $config)
    {
        $config = array_merge(
            $this->app['config']->get('services.ses', []),
            ['version' => 'latest'],
            $config
        );

        $config = Arr::except($config, ['transport']);

        return new SesV2Transport(
            new SesV2Client($this->addSesCredentials($config)),
            $config['options'] ?? []
        );
    }

    /**
     * Add the SES credentials to the configuration array.
     *
     * @param  array  $config
     * @return array
     */
    protected function addSesCredentials(array $config)
    {
        if (! empty($config['key']) && ! empty($config['secret'])) {
            $config['credentials'] = Arr::only($config, ['key', 'secret', 'token']);
        }

        return Arr::except($config, ['token']);
    }

    /**
     * Create an instance of the Symfony Mail Transport driver.
     *
     * @return \Symfony\Component\Mailer\Transport\SendmailTransport
     */
    protected function createMailTransport()
    {
        return new SendmailTransport;
    }

    /**
     * Create an instance of the Symfony Mailgun Transport driver.
     *
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Transport\TransportInterface
     */
    protected function createMailgunTransport(array $config)
    {
        $factory = new MailgunTransportFactory(null, $this->getHttpClient($config));

        if (! isset($config['secret'])) {
            $config = $this->app['config']->get('services.mailgun', []);
        }

        return $factory->create(new Dsn(
            'mailgun+'.($config['scheme'] ?? 'https'),
            $config['endpoint'] ?? 'default',
            $config['secret'],
            $config['domain']
        ));
    }

    /**
     * Create an instance of the Symfony Postmark Transport driver.
     *
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkApiTransport
     */
    protected function createPostmarkTransport(array $config)
    {
        $factory = new PostmarkTransportFactory(null, $this->getHttpClient($config));

        $options = isset($config['message_stream_id'])
                    ? ['message_stream' => $config['message_stream_id']]
                    : [];

        return $factory->create(new Dsn(
            'postmark+api',
            'default',
            $config['token'] ?? $this->app['config']->get('services.postmark.token'),
            null,
            null,
            $options
        ));
    }

    /**
     * Create an instance of the Symfony Failover Transport driver.
     *
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Transport\FailoverTransport
     */
    protected function createFailoverTransport(array $config)
    {
        $transports = [];

        foreach ($config['mailers'] as $name) {
            $config = $this->getConfig($name);

            if (is_null($config)) {
                throw new InvalidArgumentException("Mailer [{$name}] is not defined.");
            }

            // Now, we will check if the "driver" key exists and if it does we will set
            // the transport configuration parameter in order to offer compatibility
            // with any Laravel <= 6.x application style mail configuration files.
            $transports[] = $this->app['config']['mail.driver']
                ? $this->createSymfonyTransport(array_merge($config, ['transport' => $name]))
                : $this->createSymfonyTransport($config);
        }

        return new FailoverTransport($transports);
    }

    /**
     * Create an instance of the Symfony Roundrobin Transport driver.
     *
     * @param  array  $config
     * @return \Symfony\Component\Mailer\Transport\RoundRobinTransport
     */
    protected function createRoundrobinTransport(array $config)
    {
        $transports = [];

        foreach ($config['mailers'] as $name) {
            $config = $this->getConfig($name);

            if (is_null($config)) {
                throw new InvalidArgumentException("Mailer [{$name}] is not defined.");
            }

            // Now, we will check if the "driver" key exists and if it does we will set
            // the transport configuration parameter in order to offer compatibility
            // with any Laravel <= 6.x application style mail configuration files.
            $transports[] = $this->app['config']['mail.driver']
                ? $this->createSymfonyTransport(array_merge($config, ['transport' => $name]))
                : $this->createSymfonyTransport($config);
        }

        return new RoundRobinTransport($transports);
    }

    /**
     * Create an instance of the Log Transport driver.
     *
     * @param  array  $config
     * @return \Illuminate\Mail\Transport\LogTransport
     */
    protected function createLogTransport(array $config)
    {
        $logger = $this->app->make(LoggerInterface::class);

        if ($logger instanceof LogManager) {
            $logger = $logger->channel(
                $config['channel'] ?? $this->app['config']->get('mail.log_channel')
            );
        }

        return new LogTransport($logger);
    }

    /**
     * Create an instance of the Array Transport Driver.
     *
     * @return \Illuminate\Mail\Transport\ArrayTransport
     */
    protected function createArrayTransport()
    {
        return new ArrayTransport;
    }

    /**
     * Get a configured Symfony HTTP client instance.
     *
     * @return \Symfony\Contracts\HttpClient\HttpClientInterface|null
     */
    protected function getHttpClient(array $config)
    {
        if ($options = ($config['client'] ?? false)) {
            $maxHostConnections = Arr::pull($options, 'max_host_connections', 6);
            $maxPendingPushes = Arr::pull($options, 'max_pending_pushes', 50);

            return HttpClient::create($options, $maxHostConnections, $maxPendingPushes);
        }
    }

    /**
     * Set a global address on the mailer by type.
     *
     * @param  \Illuminate\Mail\Mailer  $mailer
     * @param  array  $config
     * @param  string  $type
     * @return void
     */
    protected function setGlobalAddress($mailer, array $config, string $type)
    {
        $address = Arr::get($config, $type, $this->app['config']['mail.'.$type]);

        if (is_array($address) && isset($address['address'])) {
            $mailer->{'always'.Str::studly($type)}($address['address'], $address['name']);
        }
    }

    /**
     * Get the mail connection configuration.
     *
     * @param  string  $name
     * @return array
     */
    protected function getConfig(string $name)
    {
        // Here we will check if the "driver" key exists and if it does we will use
        // the entire mail configuration file as the "driver" config in order to
        // provide "BC" for any Laravel <= 6.x style mail configuration files.
        $config = $this->app['config']['mail.driver']
            ? $this->app['config']['mail']
            : $this->app['config']["mail.mailers.{$name}"];

        if (isset($config['url'])) {
            $config = array_merge($config, (new ConfigurationUrlParser)->parseConfiguration($config));

            $config['transport'] = Arr::pull($config, 'driver');
        }

        return $config;
    }

    /**
     * Get the default mail driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        // Here we will check if the "driver" key exists and if it does we will use
        // that as the default driver in order to provide support for old styles
        // of the Laravel mail configuration file for backwards compatibility.
        return $this->app['config']['mail.driver'] ??
            $this->app['config']['mail.default'];
    }

    /**
     * Set the default mail driver name.
     *
     * @param  string  $name
     * @return void
     */
    public function setDefaultDriver(string $name)
    {
        if ($this->app['config']['mail.driver']) {
            $this->app['config']['mail.driver'] = $name;
        }

        $this->app['config']['mail.default'] = $name;
    }

    /**
     * Disconnect the given mailer and remove from local cache.
     *
     * @param  string|null  $name
     * @return void
     */
    public function purge($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        unset($this->mailers[$name]);
    }

    /**
     * Register a custom transport creator Closure.
     *
     * @param  string  $driver
     * @param  \Closure  $callback
     * @return $this
     */
    public function extend($driver, Closure $callback)
    {
        $this->customCreators[$driver] = $callback;

        return $this;
    }

    /**
     * Get the application instance used by the manager.
     *
     * @return \Illuminate\Contracts\Foundation\Application
     */
    public function getApplication()
    {
        return $this->app;
    }

    /**
     * Set the application instance used by the manager.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return $this
     */
    public function setApplication($app)
    {
        $this->app = $app;

        return $this;
    }

    /**
     * Forget all of the resolved mailer instances.
     *
     * @return $this
     */
    public function forgetMailers()
    {
        $this->mailers = [];

        return $this;
    }

    /**
     * Dynamically call the default driver instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->mailer()->$method(...$parameters);
    }
}
PK       ! U      composer.jsonnu ȯ        {
    "name": "illuminate/mail",
    "description": "The Illuminate Mail package.",
    "license": "MIT",
    "homepage": "https://laravel.com",
    "support": {
        "issues": "https://github.com/laravel/framework/issues",
        "source": "https://github.com/laravel/framework"
    },
    "authors": [
        {
            "name": "Taylor Otwell",
            "email": "taylor@laravel.com"
        }
    ],
    "require": {
        "php": "^8.1",
        "illuminate/collections": "^10.0",
        "illuminate/container": "^10.0",
        "illuminate/contracts": "^10.0",
        "illuminate/macroable": "^10.0",
        "illuminate/support": "^10.0",
        "league/commonmark": "^2.2",
        "psr/log": "^1.0|^2.0|^3.0",
        "symfony/mailer": "^6.2",
        "tijsverkoyen/css-to-inline-styles": "^2.2.5"
    },
    "autoload": {
        "psr-4": {
            "Illuminate\\Mail\\": ""
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "10.x-dev"
        }
    },
    "suggest": {
        "aws/aws-sdk-php": "Required to use the SES mail driver (^3.235.5).",
        "symfony/http-client": "Required to use the Symfony API mail transports (^6.2).",
        "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).",
        "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2)."
    },
    "config": {
        "sort-packages": true
    },
    "minimum-stability": "dev"
}
PK       ! ^BW  W    Events/MessageSent.phpnu ȯ        <?php

namespace Illuminate\Mail\Events;

use Exception;
use Illuminate\Mail\SentMessage;

/**
 * @property \Symfony\Component\Mime\Email $message
 */
class MessageSent
{
    /**
     * The message that was sent.
     *
     * @var \Illuminate\Mail\SentMessage
     */
    public $sent;

    /**
     * The message data.
     *
     * @var array
     */
    public $data;

    /**
     * Create a new event instance.
     *
     * @param  \Illuminate\Mail\SentMessage  $message
     * @param  array  $data
     * @return void
     */
    public function __construct(SentMessage $message, array $data = [])
    {
        $this->sent = $message;
        $this->data = $data;
    }

    /**
     * Get the serializable representation of the object.
     *
     * @return array
     */
    public function __serialize()
    {
        $hasAttachments = collect($this->message->getAttachments())->isNotEmpty();

        return [
            'sent' => $this->sent,
            'data' => $hasAttachments ? base64_encode(serialize($this->data)) : $this->data,
            'hasAttachments' => $hasAttachments,
        ];
    }

    /**
     * Marshal the object from its serialized data.
     *
     * @param  array  $data
     * @return void
     */
    public function __unserialize(array $data)
    {
        $this->sent = $data['sent'];

        $this->data = (($data['hasAttachments'] ?? false) === true)
            ? unserialize(base64_decode($data['data']))
            : $data['data'];
    }

    /**
     * Dynamically get the original message.
     *
     * @param  string  $key
     * @return mixed
     *
     * @throws \Exception
     */
    public function __get($key)
    {
        if ($key === 'message') {
            return $this->sent->getOriginalMessage();
        }

        throw new Exception('Unable to access undefined property on '.__CLASS__.': '.$key);
    }
}
PK       ! >e  e    Events/MessageSending.phpnu ȯ        <?php

namespace Illuminate\Mail\Events;

use Symfony\Component\Mime\Email;

class MessageSending
{
    /**
     * The Symfony Email instance.
     *
     * @var \Symfony\Component\Mime\Email
     */
    public $message;

    /**
     * The message data.
     *
     * @var array
     */
    public $data;

    /**
     * Create a new event instance.
     *
     * @param  \Symfony\Component\Mime\Email  $message
     * @param  array  $data
     * @return void
     */
    public function __construct(Email $message, array $data = [])
    {
        $this->data = $data;
        $this->message = $message;
    }
}
PK       !        TicketReply.phpnu         <?php namespace App\Mail;

use App\Models\Reply;
use App\Models\Ticket;
use App\Services\Files\EmailStore;
use App\Services\Mail\TicketReferenceHash;
use Common\Files\FileEntry;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Headers;
use Illuminate\Queue\SerializesModels;

class TicketReply extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public string $reference;
    private string|null $previousEmailMessageId = null;
    public bool $simplifiedThreading = false;

    public function __construct(public Ticket $ticket, public Reply $reply)
    {
        $this->reference = (new TicketReferenceHash())->makeEmbedForEmail(
            $reply,
        );
    }

    public function build(): static
    {
        $this->ticket->load('user', 'latest_replies.user');
        $isFirstAgentReply =
            $this->ticket->latest_replies
                ->where('user_id', '!=', $this->ticket->user_id)
                ->count() === 1;
        $this->simplifiedThreading =
            settings('mail.simplified_threading') && !$isFirstAgentReply;

        $this->setMessageIdForPreviousEmail();

        $this->to($this->ticket->user->email)
            ->subject("RE: {$this->ticket->subject}")
            ->view('tickets.ticket-reply.ticket-reply')
            ->text('tickets.ticket-reply.ticket-reply-plain');

        //$this->addAttachments();

        return $this;
    }

    public function attachments(): array
    {
        return $this->reply->attachments
            ->map(function (FileEntry $entry) {
                return Attachment::fromStorageDisk(
                    'uploads',
                    $entry->getStoragePath(),
                )
                    ->as($entry->name)
                    ->withMime($entry->mime);
            })
            ->toArray();
    }

    public function headers(): Headers
    {
        // Add ticket reference hash as in Message-ID header
        $messageId = (new TicketReferenceHash())->makeMessageIdForEmail(
            $this->reply,
        );

        $textHeaders = [
            'X-Ticket-Reference' => $this->reply->uuid,
        ];
        if ($this->previousEmailMessageId) {
            $textHeaders['In-Reply-To'] = $this->previousEmailMessageId;
        }

        // set in reply to and references headers so email client can thread properly
        return new Headers(
            messageId: $messageId,
            references: $this->previousEmailMessageId
                ? [$this->previousEmailMessageId]
                : [],
            text: $textHeaders,
        );
    }

    /**
     * Add attachments from latest ticket reply to email.
     */
    protected function addAttachments(): void
    {
        if ($this->reply->attachments->isEmpty()) {
            return;
        }

        $basePath = rtrim(
            $this->reply->attachments
                ->first()
                ->getDisk()
                ->path(''),
            '/',
        );

        $this->reply->attachments->each(function (FileEntry $attachment) use (
            $basePath,
        ) {
            $this->attachFromStorage(
                $attachment->getStoragePath(),
                $attachment->name,
                [
                    'mime' => $attachment->mime,
                ],
            );
            //            $this->attach($basePath . '/' . $attachment->getStoragePath(), [
            //                'as' => $attachment->name,
            //                'mime' => $attachment->mime,
            //            ]);
        });
    }

    protected function setMessageIdForPreviousEmail(): void
    {
        $previousReply = $this->ticket->latest_replies
            ->where('user_id', '!=', $this->reply->user_id)
            ->where('id', '<', $this->reply->id)
            ->first();

        if ($previousReply) {
            $this->previousEmailMessageId = $previousReply->email_id;

            if (!$this->previousEmailMessageId) {
                $email = (new EmailStore())->getEmailForReply($previousReply);
                $this->previousEmailMessageId =
                    $email['headers']['Message-ID'] ??
                    ($email['headers']['Message-Id'] ?? null);
            }
        }
    }
}
PK       ! g7  7  &  CreateTicketForFailedOutgoingEmail.phpnu         <?php namespace App\Services\Mail;

use App\Models\Ticket;
use App\Models\User;
use App\Services\Ticketing\CreateTicket;
use Illuminate\Support\Arr;

class CreateTicketForFailedOutgoingEmail
{
    public function execute(array $data): Ticket
    {
        $host = parse_url(config('app.url'))['host'];
        $user = User::firstOrCreate([
            'email' => "postmaster@$host",
        ]);

        $data = Arr::only($data, [
            'recipient',
            'reason',
            'description',
            'headers',
        ]);

        if (isset($data['headers'])) {
            $data['headers'] = collect($data['headers'])
                ->map(function ($value, $key) {
                    return "{$key}: {$value}";
                })
                ->implode(PHP_EOL);
        }

        return (new CreateTicket())->execute([
            'body' => view('tickets.failed-email-ticket-body')
                ->with($data)
                ->render(),
            'subject' => 'Failed Email Delivery Report',
            'user_id' => $user->id,
        ]);
    }
}
PK       ! (\*  *  $  Transformers/MimeMailTransformer.phpnu         <?php namespace App\Services\Mail\Transformers;

use ZBateson\MailMimeParser\Message;

class MimeMailTransformer
{
    public function transform(string $emailData): array
    {
        $message = Message::from($emailData, false);

        $parsed = [
            'headers' => [],
            'body' => [
                'html' => '',
                'plain' => '',
            ],
            'attachments' => [],
        ];

        foreach ($message->getAllHeaders() as $header) {
            $parsed['headers'][$header->getName()] = $header->getValue();
        }

        $parsed['body']['html'] = $message->getHtmlContent();
        $parsed['body']['plain'] = $message->getTextContent();

        foreach ($message->getAllAttachmentParts() as $attachment) {
            $content = $attachment->getContent();
            $parsed['attachments'][] = [
                'contents' => $content,
                'original_name' => $attachment->getFilename(),
                'mime_type' => $attachment->getContentType(),
                'size' => strlen($content),
                'extension' => pathinfo(
                    $attachment->getFilename(),
                    PATHINFO_EXTENSION,
                ),
                'cid' => $attachment->getContentId(),
            ];
        }

        return $parsed;
    }
}
PK       ! =4B    '  Transformers/MailgunMailTransformer.phpnu         <?php namespace App\Services\Mail\Transformers;

use Exception;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;

class MailgunMailTransformer
{
    /**
     * Raw mailgun email data.
     */
    private array $emailData;

    public function transform(array $emailData): array
    {
        $this->emailData = $emailData;
        $attachments = $this->transformAttachments();

        return [
            'headers' => $this->transformHeaders(),
            'body' => [
                'plain' => Arr::get($emailData, 'body-plain'),
                'stripped-html' => Arr::get($emailData, 'stripped-html'),
                'html' => Arr::get($emailData, 'body-html'),
            ],
            'attachments' => $attachments,
        ];
    }

    /**
     * Transform mailgun headers into key => value array.
     */
    public function transformHeaders(): array
    {
        $headers = Arr::get($this->emailData, 'message-headers', []);

        if (!is_array($headers)) {
            $headers = json_decode($headers, true);
        }

        return collect($headers)
            ->mapWithKeys(function ($value) {
                return [
                    $value[0] => $this->convertHeaderValueToString($value[1]),
                ];
            })
            ->toArray();
    }

    /**
     * Convert mailgun header value into string if it's an array.
     */
    private function convertHeaderValueToString($value): string
    {
        if (!is_array($value)) {
            return $value;
        }

        try {
            $value = Arr::flatten($value);
            $value = $value[0] . '; ' . $value[1];
        } catch (Exception $e) {
            $value = 'Error parsing header';
        }

        return $value;
    }

    /**
     * Transform mailgun attachments into array.
     */
    private function transformAttachments(): array
    {
        $attachments = Arr::get($this->emailData, 'attachments', []);

        if (is_string($attachments)) {
            $attachments = json_decode($attachments, true);
        }

        return array_map(function ($attachment) {
            $contents = $this->getRemoteAttachmentContents($attachment['url']);
            return [
                'original_name' => $attachment['name'],
                'mime_type' => $attachment['content-type'],
                'size' => $attachment['size'],
                'contents' => $contents,
                'extension' => explode('/', $attachment['content-type'])[1],
                'cid' => $this->getAttachmentCid($attachment),
            ];
        }, $attachments);
    }

    /**
     * Find attachment CID if it's an inline attachment.
     */
    private function getAttachmentCid(array $attachment): ?string
    {
        $cidMap = Arr::get($this->emailData, 'content-id-map', []);

        if (is_string($cidMap)) {
            $cidMap = json_decode($cidMap, true);
        }

        foreach ($cidMap as $cid => $cidAttachment) {
            $url = is_array($cidAttachment)
                ? $cidAttachment['url']
                : $cidAttachment;

            if ($url === $attachment['url']) {
                return str_replace(['<', '>'], '', $cid);
            }
        }

        return null;
    }

    /**
     * Download specified attachment contents via mailgun API.
     */
    private function getRemoteAttachmentContents(string $url): ?string
    {
        $response = Http::withBasicAuth(
            'api',
            config('services.mailgun.secret'),
        )->get($url);
        return $response->body() ?? null;
    }
}
PK       ! g       Parsing/ParsedEmail.phpnu         <?php namespace App\Services\Mail\Parsing;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use InvalidArgumentException;
use ZBateson\MailMimeParser\Message;

class ParsedEmail implements Jsonable, Arrayable
{
    public function __construct(protected array $email)
    {
    }

    /**
     * This will strip quoted replies from email and
     * remove any not allowed html tags.
     */
    public function getNormalizedBody(array $cidMap = []): string
    {
        $strippedBody = $this->email['body']['stripped-html'] ?? null;
        if (!$strippedBody) {
            if ($this->hasBody('html')) {
                $strippedBody = $this->getStrippedHtml('html');
            }
            if (!$strippedBody) {
                $strippedBody = $this->getStrippedHtml('plain');
            }
        }

        // replace CIDs in img src with actual image urls
        foreach ($cidMap as $cid => $url) {
            $strippedBody = str_replace("cid:$cid", $url, $strippedBody);
        }

        $purifier = new PurifyEmailBody();
        $purifiedBody = $purifier->execute($strippedBody);
        if (!$purifiedBody) {
            $purifiedBody = $purifier->execute($this->getStrippedHtml('plain'));
        }

        return $purifiedBody;
    }

    protected function getStrippedHtml(string $type): ?string
    {
        return (new GetStrippedHtmlFromEmail())->execute(
            $this->getBody($type),
            $type,
        );
    }

    public function getSubject(): string
    {
        return $this->getHeader('Subject') ?: '(no subject)';
    }

    public function getSenderEmail(): string
    {
        $header = $this->getHeader('Reply-To') ?: $this->getHeader('From');

        // parse header and extract email address
        $email = Message::from("From: $header", false)
            ->getHeader('From')
            ->getEmail();

        if ($email) {
            return $email;
        }

        throw new InvalidArgumentException(
            "Could not extract email address from [$header]",
        );
    }

    public function getMessageId(): string
    {
        return $this->getHeader('Message-ID') ?? $this->getHeader('Message-Id');
    }

    public function getHeader(string $name): ?string
    {
        return Arr::get($this->email, "headers.$name");
    }

    public function hasHeader(string $name): bool
    {
        return Arr::has($this->email, "headers.$name");
    }

    public function getHeaders(): array
    {
        return Arr::get($this->email, 'headers', []);
    }

    public function getBody(string $type): ?string
    {
        return Arr::get($this->email, "body.$type");
    }

    public function hasBody(string $type): bool
    {
        return Arr::get($this->email, "body.$type") !== null;
    }

    public function getAttachments(string $type): Collection
    {
        $attachments = Arr::get($this->email, 'attachments', []);

        // if attachment has a CID then it's inline, otherwise it's 'regular'
        return collect($attachments)->filter(function ($attachment) use (
            $type,
        ) {
            $cidEmbedded =
                $attachment['cid'] &&
                Str::contains($this->getBody('html'), $attachment['cid']);

            //if email body does not have attachment CID embedded, treat attachment as 'regular'
            if ($type === 'inline') {
                return $cidEmbedded;
            } else {
                return !$cidEmbedded;
            }
        });
    }

    public function toJson($options = 0): string
    {
        return json_encode($this->toArray(), $options);
    }

    public function toArray(): array
    {
        return [
            'headers' => $this->getHeaders(),
            'body' => [
                'plain' => $this->getBody('plain'),
                'html' => $this->getBody('html'),
            ],
        ];
    }
}
PK       ! a       Parsing/StripQuotedEmailText.phpnu         <?php

namespace App\Services\Mail\Parsing;

use Str;
use Symfony\Component\DomCrawler\Crawler;

class StripQuotedEmailText
{
    private ?Crawler $crawler = null;

    public function execute(string $htmlBody): string
    {
        $this->crawler = new Crawler($htmlBody);

        $stripMethods = array_filter(
            get_class_methods($this),
            fn($methodName) => Str::startsWith($methodName, 'strip'),
        );

        foreach ($stripMethods as $methodName) {
            $this->$methodName();
            $html = $this->crawler->outerHtml();
            if (!Str::contains($html, '|reference=')) {
                return $html;
            }
        }

        // remove quoted content by "reply above ID" separator, if it was
        // not removed already via specific mail provider strip methods
        $separator = PurifyEmailBody::REPLY_ABOVE_ID;
        if (Str::contains($htmlBody, $separator)) {
            $matches = $this->crawler->filter('[id*="' . $separator . '"]');
            if ($matches->count()) {
                $matches->each(function (Crawler $match) {
                    $node = $match->getNode(0);
                    $node->parentNode->removeChild($node);
                });
            }
        }

        return $this->crawler->outerHtml();
    }

    private function stripGmail()
    {
        $match = $this->crawler->filter('.gmail_quote');
        if ($match->count()) {
            $node = $match->getNode(0);
            $node->parentNode->removeChild($node);
        }
    }

    private function stripAppleMail()
    {
        // remove all html after "Sent from my iPhone", crawler
        // will correct invalid html due to missing tags

        // find blockquote, Apply mail puts quoted reply in this tag
        $match = $this->crawler->filter('blockquote');
        if ($match->count()) {
            $blockquoteNode = $match->getNode(0);
            // find previous node: On Oct 17, 2017, at 12:22 PM, Name email@email.com> wrote:
            $prevNode = $match->previousAll()->first();
            if (
                $prevNode->count() &&
                Str::endsWith($prevNode->text(), 'wrote:')
            ) {
                $node = $prevNode->getNode(0);
                $node->parentNode->removeChild($node);
                $blockquoteNode->parentNode->removeChild($blockquoteNode);
            }
        }
    }

    private function stripOutlook()
    {
        // remove #divRplyFwdMsg and all nodes after it
        $match = $this->crawler->filter('#divRplyFwdMsg');
        if ($match->count()) {
            $match->nextAll()->each(function (Crawler $c) {
                $node = $c->getNode(0);
                $c->getNode(0)->parentNode->removeChild($node);
            });
            $node = $match->getNode(0);
            $node->parentNode->removeChild($node);
        }
    }

    private function stripOutlook2()
    {
        // remove all nodes after "[style=mso-bookmark:_MailEndCompose]"
        $match = $this->crawler->filter(
            '[style="mso-bookmark:_MailEndCompose"]',
        );
        if ($match->count()) {
            $match->nextAll()->each(function (Crawler $c) {
                $node = $c->getNode(0);
                $c->getNode(0)->parentNode->removeChild($node);
            });
        }
    }

    private function stripOutlook3()
    {
        // remove all nodes after "[style=mso-bookmark:_MailEndCompose]"
        $match = $this->crawler->filter(
            '[style^="mso-element:para-border-div;border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt"]',
        );
        if ($match->count() && Str::contains($match->html(), 'mailto:')) {
            $match->nextAll()->each(function (Crawler $c) {
                $node = $c->getNode(0);
                $c->getNode(0)->parentNode->removeChild($node);
            });
        }
    }

    private function stripYahoo()
    {
        $match = $this->crawler->filter('[class$="yahoo_quoted"]');
        if ($match->count()) {
            $node = $match->getNode(0);
            $node->parentNode->removeChild($node);
        }
    }
}
PK       ! t;g  g    Parsing/StripEmailSignature.phpnu         <?php

namespace App\Services\Mail\Parsing;

use Str;
use Symfony\Component\DomCrawler\Crawler;

class StripEmailSignature
{
    private ?Crawler $crawler = null;

    public function execute(string $htmlBody): string
    {
        $this->crawler = new Crawler($htmlBody);

        collect(get_class_methods($this))
            ->filter(fn($methodName) => Str::startsWith($methodName, 'strip'))
            ->each(function ($methodName) {
                $this->$methodName();
            });

        $html = $this->crawler->outerHtml();
        $html = str_replace(' Sent from my iPhone', '', $html);
        return $html;
    }

    private function stripAppleMail()
    {
        $match = $this->crawler->filter('#AppleMailSignature');
        if ($match->count()) {
            $node = $match->getNode(0);
            $node->parentNode->removeChild($node);
        }
    }

    private function stripOpenExchange()
    {
        $match = $this->crawler->filter('.io-ox-signature');
        if ($match->count()) {
            $node = $match->getNode(0);
            $node->parentNode->removeChild($node);
        }
    }
}
PK       ! A      Parsing/PurifyEmailBody.phpnu         <?php namespace App\Services\Mail\Parsing;

use HTMLPurifier;
use HTMLPurifier_Config;

class PurifyEmailBody
{
    public const REPLY_ABOVE_ID = 'bedeskReplyAboveThisLine';

    public function execute(string $htmlBody): string
    {
        $htmlBody = html_entity_decode($htmlBody);

        // remove reference if it got through
        $htmlBody = preg_replace(
            '/\|reference=[a-zA-Z0-9]{30}\|/',
            '',
            $htmlBody,
        );

        // purify email body
        $htmlBody = $this->getPurifier()->purify($htmlBody);

        // replace all newlines with "br" tag
        $htmlBody = str_replace(["\r\n", "\r", "\n"], '<br>', $htmlBody);

        // remove all whitespace/newlines from start and end of email body
        $htmlBody = trim($htmlBody);
        $htmlBody = preg_replace('/(?:<br\s*\/?>\s*)+$/', '', $htmlBody);
        $htmlBody = preg_replace('/^(?:<br\s*\/?>\s*)+/', '', $htmlBody);

        // replace 3 or more br tags with 2 to avoid excessive white space
        return preg_replace('/(?:<br\s*\/?>\s*){3,}/', '<br><br>', $htmlBody);
    }

    protected function getPurifier(): HTMLPurifier
    {
        $config = HTMLPurifier_Config::createDefault();

        $config->set('Core.Encoding', 'UTF-8');
        $config->set('HTML.Doctype', 'HTML 4.01 Transitional');
        $config->set('Cache.SerializerPath', storage_path('app/purifier'));
        $config->set(
            'HTML.Allowed',
            'b,strong,i,u,a[href],ul,ol,li,br,img[src|width|height],font[color]',
        );
        $config->set('HTML.TargetBlank', true);
        $config->set('AutoFormat.Linkify', true);
        $config->set('AutoFormat.RemoveEmpty', true);
        $config->set('AutoFormat.RemoveEmpty.RemoveNbsp', true);

        return new HTMLPurifier($config);
    }
}
PK       ! %    $  Parsing/GetStrippedHtmlFromEmail.phpnu         <?php

namespace App\Services\Mail\Parsing;

use EmailReplyParser\Parser\EmailParser;
use Illuminate\Support\Facades\Http;

class GetStrippedHtmlFromEmail
{
    public function execute(string $email, string $type): string|null
    {
        $strippedHtml = null;
        if (config('services.openai.api_key')) {
            $strippedHtml = $this->usingOpenAi($email);
        }
        if (!$strippedHtml) {
            $strippedHtml = $this->usingLocalParsers($email, $type);
        }
        return $strippedHtml;
    }

    protected function usingOpenAi(string $email): string|null
    {
        $email = (new PurifyEmailBody())->execute($email);
        $response = Http::withToken(config('services.openai.api_key'))->post(
            'https://api.openai.com/v1/chat/completions',
            [
                'model' => 'gpt-3.5-turbo',
                'temperature' => 0.1,
                'messages' => [
                    [
                        'role' => 'user',
                        'content' =>
                            'Remove all previous replies quoted by email client and user signatures from this email body: ' .
                            $email,
                    ],
                ],
            ],
        );

        return $response->json()['choices'][0]['message']['content'] ?? null;
    }

    protected function usingLocalParsers(
        string $email,
        string $type,
    ): string|null {
        if ($type === 'plain') {
            $email = (new EmailParser())->parse($email)->getVisibleText();
        }

        $email = (new StripQuotedEmailText())->execute($email);
        return (new StripEmailSignature())->execute($email);
    }
}
PK       ! 2      TicketReferenceHash.phpnu         <?php namespace App\Services\Mail;

use App\Models\Reply;

class TicketReferenceHash
{
    /**
     * Make reference hash string embed for email.
     */
    public function makeEmbedForEmail(Reply $reply): string
    {
        return "|reference=$reply->uuid|";
    }

    /**
     * Create message id for email with ticket and reply reference embedded.
     */
    public function makeMessageIdForEmail(Reply $reply): string
    {
        $host = parse_url(config('app.url'))['host'];

        return "{$reply->uuid}@$host";
    }

    /**
     * Extract ticket reference from specified string;
     */
    public function extractFromString(string $string): ?string
    {
        preg_match('/\|reference=(.+?)\|/', $string, $matches);

        return $matches[1] ?? null;
    }

    /**
     * Extract reply UUID from email Message-ID header.
     */
    public function extractFromMessageId(string $string): string
    {
        $uuid = explode('@', $string)[0];

        return str_replace(['<', '>'], '', $uuid);
    }
}
PK       ! fj    #  TransformEmailIntoTicketOrReply.phpnu         <?php namespace App\Services\Mail;

use App\Events\TicketCreated;
use App\Models\Reply;
use App\Models\Ticket;
use App\Models\User;
use App\Notifications\TicketIsLocked;
use App\Notifications\TicketRejected;
use App\Services\Files\EmailStore;
use App\Services\Mail\Parsing\ParsedEmail;
use App\Services\Ticketing\CreateTicket;
use App\Services\Ticketing\SubmitTicketReply;
use Common\Auth\Actions\CreateUser;
use Common\Files\Actions\CreateFileEntry;
use Common\Files\Actions\StoreFile;
use Common\Files\FileEntry;
use Common\Files\FileEntryPayload;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;

class TransformEmailIntoTicketOrReply
{
    public function __construct(protected ParsedEmail $parsedEmail)
    {
    }

    public function execute(array $options = []): void
    {
        $createNewTickets =
            $options['createNewTickets'] ??
            settings('tickets.create_from_emails');
        $createReplies =
            $options['createReplies'] ?? settings('replies.create_from_emails');
        $ticket = $this->getTicketEmailIsInReplyTo();

        // prevent replies from the same email from being created
        $emailId = $this->parsedEmail->getMessageId();
        if ($emailId && Reply::where('email_id', $emailId)->exists()) {
            return;
        }

        if ($ticket?->status === 'locked') {
            Notification::route(
                'mail',
                $this->parsedEmail->getSenderEmail(),
            )->notify(new TicketIsLocked($ticket));
            return;
        }

        //create new ticket from email
        if (!$ticket && $createNewTickets) {
            $newTicket = $this->createTicketFromEmail();
            $reply = $newTicket->replies->first();
        }

        //create reply for existing ticket from email
        if ($ticket && $createReplies) {
            $reply = $this->createReplyFromEmail($ticket);
        }

        if (!$ticket && !$createNewTickets) {
            $this->maybeSendTicketRejectedNotification();
        }

        $this->storeOriginalEmail($reply ?? null);
    }

    private function getTicketEmailIsInReplyTo(): ?Ticket
    {
        $reply = null;
        $referenceHash = new TicketReferenceHash();

        if ($this->parsedEmail->hasHeader('In-Reply-To')) {
            $inReplyToMessageId = $this->parsedEmail->getHeader('In-Reply-To');
            $uuid = $referenceHash->extractFromMessageId($inReplyToMessageId);
            // find reply either by email message ID or by BeDesk specific UUID for reply
            $reply = Reply::when(
                $uuid,
                fn($builder) => $builder->where('uuid', $uuid),
            )
                ->orWhere('email_id', $inReplyToMessageId)
                ->first();
        }

        if (!$reply && $this->parsedEmail->hasBody('plain')) {
            $uuid = $referenceHash->extractFromString(
                $this->parsedEmail->getBody('plain'),
            );
            if ($uuid) {
                $reply = Reply::where('uuid', $uuid)->first();
            }
        }

        if (!$reply && $this->parsedEmail->hasBody('html')) {
            $uuid = str_replace(
                '<wbr>',
                '',
                $referenceHash->extractFromString(
                    $this->parsedEmail->getBody('html'),
                ),
            );
            if ($uuid) {
                $reply = Reply::where('uuid', $uuid)->first();
            }
        }

        return $reply?->ticket;
    }

    private function createTicketFromEmail(): Ticket
    {
        $email = $this->parsedEmail->getSenderEmail();
        $user =
            User::where(['email' => $email])->first() ??
            (new CreateUser())->execute(['email' => $email]);

        $cidMap = $this->generateCidMap($user->id);

        $ticket = (new CreateTicket())->execute([
            'body' => $this->parsedEmail->getNormalizedBody($cidMap),
            'subject' => $this->parsedEmail->getSubject(),
            'user_id' => $user->id,
            'attachments' => $this->getEntryIdsFromAttachments($user->id),
            'email_id' => $this->parsedEmail->getMessageId(),
            'received_at_email' =>
                $this->parsedEmail->getHeader('Delivered-To') ??
                $this->parsedEmail->getHeader('To'),
        ]);

        event(new TicketCreated($ticket));

        return $ticket;
    }

    private function createReplyFromEmail(Ticket $ticket): Reply
    {
        $cidMap = $this->generateCidMap($ticket->user_id);

        return (new SubmitTicketReply())->execute(
            $ticket,
            [
                'body' => $this->parsedEmail->getNormalizedBody($cidMap),
                'user_id' => $ticket->user_id,
                'attachments' => $this->getEntryIdsFromAttachments(
                    $ticket->user_id,
                ),
                'email_id' => $this->parsedEmail->getMessageId(),
            ],
            Reply::REPLY_TYPE,
            Reply::SOURCE_EMAIL,
        );
    }

    /**
     * Store inline images and generate CID map for them.
     */
    private function generateCidMap(int $userId): array
    {
        $inlineAttachments = $this->parsedEmail->getAttachments('inline');

        return $inlineAttachments
            ->mapWithKeys(function ($attachment) use ($userId) {
                $fileEntry = $this->storeAttachment($attachment, [
                    'ownerId' => $userId,
                    'diskPrefix' => 'ticket_images',
                    'disk' => 'public',
                ]);
                return [$attachment['cid'] => url($fileEntry->url)];
            })
            ->toArray();
    }

    private function getEntryIdsFromAttachments(int $userId): array
    {
        $attachments = $this->parsedEmail->getAttachments('regular');

        $uploadIds = $attachments->map(function ($attachment) use ($userId) {
            $fileEntry = $this->storeAttachment($attachment, [
                'ownerId' => $userId,
            ]);
            return $fileEntry->id;
        });

        return $uploadIds->toArray();
    }

    protected function storeAttachment(
        array $attachment,
        array $additionalData,
    ): FileEntry {
        $data = [
            'clientName' => $attachment['original_name'],
            'clientMime' => $attachment['mime_type'],
            'clientExtension' => $attachment['extension'],
            'filename' => (string) Str::uuid(),
            'clientSize' => $attachment['size'],
        ];
        $data = array_merge($data, $additionalData);
        if (Arr::get($data, 'disk') === 'public') {
            $data[
                'filename'
            ] = "{$data['filename']}.{$data['clientExtension']}";
        }
        $payload = new FileEntryPayload($data);
        (new StoreFile())->execute($payload, [
            'contents' => $attachment['contents'],
        ]);
        return (new CreateFileEntry())->execute($payload);
    }

    private function storeOriginalEmail(Reply $reply = null): void
    {
        (new EmailStore())->storeEmail($this->parsedEmail, $reply);
    }

    /**
     * Send rejected notification to sender if
     * ticket creation via email channel is disabled.
     */
    private function maybeSendTicketRejectedNotification(): void
    {
        if (settings('tickets.send_ticket_rejected_notification')) {
            Notification::route(
                'mail',
                $this->parsedEmail->getSenderEmail(),
            )->notify(new TicketRejected());
        }
    }
}
PK       ! Z:      OutgoingEmailLogController.phpnu ȯ        <?php

namespace Common\Logging\Mail;

use Common\Core\BaseController;
use Common\Database\Datasource\Datasource;
use ZBateson\MailMimeParser\Message;

class OutgoingEmailLogController extends BaseController
{
    public function __construct()
    {
        $this->middleware('isAdmin');
    }

    public function show(int $id): mixed
    {
        $logItem = OutgoingEmailLogItem::findOrFail($id);

        $message = Message::from($logItem->mime, true);

        $logItem->parsed_message = [
            'headers' => collect($message->getAllHeaders())->mapWithKeys(
                fn($header) => [$header->getName() => $header->getValue()],
            ),
            'body' => [
                'text' => $message->getTextContent(),
                'html' => $message->getHtmlContent(),
            ],
        ];

        return $this->success([
            'logItem' => $logItem,
        ]);
    }

    public function index(): mixed
    {
        $pagination = (new Datasource(
            OutgoingEmailLogItem::query(),
            request()->all(),
        ))->paginate();

        return $this->success([
            'pagination' => $pagination,
        ]);
    }

    public function downloadLog()
    {
        $log = json_encode(
            OutgoingEmailLogItem::limit(1000)->get(),
            JSON_PRETTY_PRINT,
        );

        return response($log)
            ->header('Content-Type', 'application/json')
            ->header(
                'Content-Disposition',
                'attachment; filename="outgoing-email-log.json"',
            );
    }

    public function downloadLogItem(int $id)
    {
        $logItem = OutgoingEmailLogItem::findOrFail($id);

        return response($logItem->mime)
            ->header('Content-Type', 'message/rfc822')
            ->header(
                'Content-Disposition',
                "attachment; filename=\"{$logItem->subject}.eml\"",
            );
    }
}
PK       ! *F      OutgoingEmailLogItem.phpnu ȯ        <?php

namespace Common\Logging\Mail;

use Common\Core\BaseModel;
use Illuminate\Database\Eloquent\Casts\Attribute;

class OutgoingEmailLogItem extends BaseModel
{
    const MODEL_TYPE = 'outgoing_email_log_item';

    protected $table = 'outgoing_email_log';

    protected $guarded = ['id'];

    protected $casts = [
        'id' => 'integer',
    ];

    protected $hidden = ['mime'];

    protected function mime(): Attribute
    {
        return Attribute::make(get: fn(string $value) => utf8_decode($value));
    }

    public static function filterableFields(): array
    {
        return ['id', 'status', 'from', 'to', 'created_at'];
    }

    public function toNormalizedArray(): array
    {
        return [
            'id' => $this->id,
            'name' => $this->subject,
            'description' => $this->message_id,
            'model_type' => self::MODEL_TYPE,
        ];
    }

    public function toSearchableArray(): array
    {
        return [
            'id' => $this->id,
            'subject' => $this->subject,
            'to' => $this->to,
        ];
    }

    public static function getModelTypeAttribute(): string
    {
        return self::MODEL_TYPE;
    }
}
PK       ! nV
3  3    OutgoingEmailLogSubscriber.phpnu ȯ        <?php

namespace Common\Logging\Mail;

use Exception;
use Illuminate\Events\Dispatcher;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Mail\Events\MessageSent;
use ZBateson\MailMimeParser\Message;

class OutgoingEmailLogSubscriber
{
    public function handleSending(MessageSending $event): void
    {
        // make sure any logic exceptions from symfony don't break the app
        try {
            $headers = $event->message->getPreparedHeaders();
        } catch (Exception $e) {
            return;
        }

        $parsedMessage = Message::from($event->message->toString(), true);

        foreach (
            $parsedMessage->getAllAttachmentParts()
            as $index => $attachment
        ) {
            // only store attachments that are less than 500KB
            if (strlen($attachment->getContent()) > 500000) {
                $parsedMessage->removeAttachmentPart($index);
            }
        }

        $logItem = OutgoingEmailLogItem::create([
            'message_id' => $headers->get('Message-ID')->getBodyAsString(),
            'from' => $headers->get('From')->getBodyAsString(),
            'to' => $headers->get('To')->getBodyAsString(),
            'subject' => $headers->get('Subject')->getBodyAsString(),
            'mime' => utf8_encode((string) $parsedMessage),
            'status' => 'not-sent',
        ]);

        $event->message
            ->getHeaders()
            ->addTextHeader('X-BE-LOG-ID', $logItem->id);
    }

    public function handleSent(MessageSent $event): void
    {
        $logId = $event->message
            ->getHeaders()
            ->get('X-BE-LOG-ID')
            ->getBodyAsString();

        OutgoingEmailLogItem::where('id', $logId)->update([
            'status' => 'sent',
            'message_id' => $event->sent
                ->getSymfonySentMessage()
                ->getMessageId(),
        ]);
    }

    public function subscribe(Dispatcher $events): array
    {
        return [
            MessageSending::class => 'handleSending',
            MessageSent::class => 'handleSent',
        ];
    }
}
PK       ! q\    !  ConnectGmailAccountController.phpnu ȯ        <?php

namespace Common\Settings\Mail;

use Common\Auth\Oauth;
use Common\Core\BaseController;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Session;
use Laravel\Socialite\Facades\Socialite;

class ConnectGmailAccountController extends BaseController
{
    public function connectGmail()
    {
        Session::flash(
            Oauth::OAUTH_CALLBACK_HANDLER_KEY,
            HandleConnectGmailOauthCallback::class,
        );

        $driver = Socialite::driver('google')
            ->scopes([
                'https://www.googleapis.com/auth/gmail.readonly',
                'https://www.googleapis.com/auth/gmail.send',
            ])
            ->with([
                'access_type' => 'offline',
                'prompt' => 'consent select_account',
            ]);

        return $driver->redirect();
    }

    public static function getConnectedEmail(): ?string
    {
        if (!class_exists(GmailClient::class)) {
            return null;
        }

        try {
            $data = json_decode(File::get(GmailClient::tokenPath()), true);
            return $data['email'] ?? null;
        } catch (FileNotFoundException $e) {
            return null;
        }
    }
}
PK       ! ]    #  HandleConnectGmailOauthCallback.phpnu ȯ        <?php

namespace Common\Settings\Mail;

use Common\Auth\Oauth;
use Illuminate\Contracts\View\View as ViewContract;
use Illuminate\Support\Facades\File;
use Laravel\Socialite\Facades\Socialite;

class HandleConnectGmailOauthCallback
{
    public function execute(string $provider): ViewContract
    {
        $profile = Socialite::with('google')->user();

        File::ensureDirectoryExists(dirname(GmailClient::tokenPath()));
        File::put(
            GmailClient::tokenPath(),
            json_encode([
                'access_token' => $profile->token,
                'refresh_token' => $profile->refreshToken,
                'created' => now()->timestamp,
                'expires_in' => $profile->expiresIn,
                'email' => $profile->email,
            ]),
        );

        if (settings('incoming_email.gmail.enabled')) {
            (new GmailClient())->watch();
        }

        return (new Oauth())->getPopupResponse('SUCCESS', [
            'profile' => $profile,
        ]);
    }
}
PK       ! Z      GmailApiMailTransport.phpnu ȯ        <?php

namespace Common\Settings\Mail;

use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;

class GmailApiMailTransport extends AbstractTransport
{
    public function doSend(SentMessage $message): void
    {
        (new GmailClient())->sendEmail($message->toString());
    }

    public function __toString(): string
    {
        return 'gmailApi';
    }
}
PK       ! AU      GmailClient.phpnu ȯ        <?php

namespace Common\Settings\Mail;

use File;
use Google\Service\Gmail\Message;
use Google\Service\Gmail\WatchRequest;
use Google\Service\Gmail\WatchResponse;
use Google_Client;
use Google_Service_Gmail;

class GmailClient
{
    private Google_Service_Gmail $gmail;

    private Google_Client $googleClient;

    public function __construct()
    {
        $this->buildGoogleClient();
    }

    public static function tokenPath(): string
    {
        return storage_path('app/tokens/gmail.json');
    }

    public static function tokenExists(): bool
    {
        return file_exists(self::tokenPath());
    }

    public function sendEmail(string $rawContent): Message
    {
        $encoded = strtr(base64_encode($rawContent), ['+' => '-', '/' => '_']);
        $msg = new Message();
        $msg->setRaw($encoded);
        return $this->gmail->users_messages->send('me', $msg);
    }

    public function listHistory(int $historyId): array
    {
        $response = $this->gmail->users_history->listUsersHistory('me', [
            'startHistoryId' => $historyId,
        ]);

        $messageIds = collect($response['history'])
            ->map(function ($history) {
                $msg = $history['messagesAdded'][0]['message'] ?? null;
                $labels = $msg['labelIds'] ?? [];

                if ($msg && !in_array('SENT', $labels)) {
                    return $msg['id'];
                }
            })
            ->filter();

        if ($messageIds->isEmpty()) {
            return [];
        }

        $this->googleClient->setUseBatch(true);
        $batch = $this->gmail->createBatch();

        $messageIds->each(function ($msgId) use ($batch) {
            $request = $this->gmail->users_messages->get('me', $msgId, [
                'format' => 'raw',
            ]);
            $batch->add($request);
        });

        $this->googleClient->setUseBatch(false);

        return array_values($batch->execute());
    }

    public function watch(): WatchResponse
    {
        $payload = new WatchRequest();
        $payload->topicName = settings('incoming_email.gmail.topicName');
        $payload->labelIds = ['UNREAD'];
        $payload->labelFilterAction = 'include';
        return $this->gmail->users->watch('me', $payload);
    }

    private function buildGoogleClient(): void
    {
        $this->googleClient = new Google_Client();
        $this->googleClient->setClientId(config('services.google.client_id'));
        $this->googleClient->setClientSecret(
            config('services.google.client_secret'),
        );

        if (self::tokenExists()) {
            $tokenJson = file_get_contents(self::tokenPath());
            $accessToken = json_decode($tokenJson, true);
            $this->googleClient->setAccessToken($accessToken);
        }

        if ($this->googleClient->isAccessTokenExpired()) {
            $newToken = $this->googleClient->fetchAccessTokenWithRefreshToken(
                $this->googleClient->getRefreshToken(),
            );
            $oldToken = json_decode(File::get(self::tokenPath()), true);
            $mergedToken = array_merge($oldToken, $newToken);
            File::put(self::tokenPath(), json_encode($mergedToken));
        }

        $this->gmail = new Google_Service_Gmail($this->googleClient);
    }
}
PK       !       PaymentReminder.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class PaymentReminder extends Mailable
{
    use Queueable, SerializesModels;

    public $payment;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($payment)
    {
        $this->payment = $payment;
    }

    /**
     * Build the message.
     *
     * @return $this
     */

    public function build()
    {
        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.payment_reminder', compact($this->payment))->subject('Ragarding to overdue payment reminder');
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.payment_reminder', compact($this->payment))->subject('Ragarding to overdue payment reminder');

        }


    }
}
PK       ! c      CustomerInvoiceSend.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class CustomerInvoiceSend extends Mailable
{
    use Queueable, SerializesModels;
    public $invoice;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($invoice)
    {
        $this->invoice = $invoice;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.customer_invoice_send')->with('invoice', $this->invoice)->subject('Ragarding to Send Invoice');
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.customer_invoice_send')->with('invoice', $this->invoice)->subject('Ragarding to Send Invoice');
        }


    }
}
PK       ! @S      InvoicePaymentCreate.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class InvoicePaymentCreate extends Mailable
{
    use Queueable, SerializesModels;
    public $payment;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($payment)
    {
        $this->payment = $payment;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.invoice_payment_create')->subject('Ragarding to payment succesfully received')->with('payment', $this->payment);
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.invoice_payment_create')->subject('Ragarding to payment succesfully received')->with('payment', $this->payment);

        }


    }
}
PK       ! `      BillSend.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class BillSend extends Mailable
{
    use Queueable, SerializesModels;

    public $bill;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($bill)
    {
        $this->bill = $bill;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.bill_send')->with('bill', $this->bill)->subject('Ragarding to product/service bill generator.');
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.bill_send')->with('bill', $this->bill)->subject('Ragarding to product/service bill generator.');
        }


    }
}
PK       ! 8g&9      VenderBillSend.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class VenderBillSend extends Mailable
{
    use Queueable, SerializesModels;
    public $bill;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($bill)
    {
        $this->bill = $bill;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.vender_bill_send')->with('bill', $this->bill)->subject('Ragarding to send bill');
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.vender_bill_send')->with('bill', $this->bill)->subject('Ragarding to send bill');

        }

    }
}
PK       ! ($  $    testMail.phpnu         <?php

namespace App\Models\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class testMail extends Mailable
{
    use Queueable, SerializesModels;


    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {

    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('email.test_mail')->subject('Ragarding to testing purpose mail send.');
    }
}
PK       ! Y      BillPaymentCreate.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class BillPaymentCreate extends Mailable
{
    use Queueable, SerializesModels;
    public $payment;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($payment)
    {
        $this->payment = $payment;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.bill_payment_create')->subject('Ragarding to payment succesfully sent')->with('payment', $this->payment);
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.bill_payment_create')->subject('Ragarding to payment succesfully sent')->with('payment', $this->payment);
        }

    }
}
PK       ! R5      ProposalSend.phpnu         <?php

namespace App\Models\Mail;

use App\Models\Utility;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class ProposalSend extends Mailable
{
    use Queueable, SerializesModels;

    public $proposal;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($proposal)
    {
        $this->proposal = $proposal;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        if(\Auth::user()->type == 'super admin')
        {
            return $this->view('email.proposal_send')->with('proposal', $this->proposal)->subject('Ragarding to product/service proposal generator.');
        }
        else
        {
            return $this->from(Utility::getValByName('company_email'), Utility::getValByName('company_email_from_name'))->view('email.proposal_send')->with('proposal', $this->proposal)->subject('Ragarding to product/service proposal generator.');
        }


    }
}
PK         ! -                    PayslipSend.phpnu [        PK         ! O!                  LeaveActionSend.phpnu [        PK         ! \Ϫ_  _                UserCreate.phpnu         PK         ! CE  E              r
  AppraisalCreatedMail.phpnu [        PK         ! o8                  TestMail.phpnu [        PK         ! ?                T  TerminationSend.phpnu [        PK         !  e  e              3  CommonEmailTemplate.phpnu [        PK         ! 
|U                  TransferSend.phpnu [        PK         ! s                  AppraisalReviewReminderMail.phpnu [        PK         ! ;!
                0   PeerAddedMail.phpnu [        PK         ! .C                Z&  AwardSend.phpnu [        PK         ! S{  {              )  TicketSend.phpnu [        PK         ! `                +  PromotionSend.phpnu [        PK         ! \3]  ]              .  TripSend.phpnu [        PK         ! 0L                G1  ComplaintsSend.phpnu [        PK         ! {
  
               4  GoalReminderMail.phpnu [        PK         ! @                n9  ResignationSend.phpnu [        PK         ! "HSx  x              M<  WarningSend.phpnu [        PK         !                 ?  Cap.podnu [        PK         ! /Z    	            XN  Filter.pmnu [        PK         ! Ni8  8              ST  Field/Generic.podnu [        PK         ! F8  8              `  Field/AddrList.podnu [        PK         ! 2&  &              Fq  Field/Date.pmnu [        PK         ! 4>bx                v  Field/Date.podnu [        PK         ! T
  
              Ȃ  Field/Generic.pmnu [        PK         ! WO  O                Field/AddrList.pmnu [        PK         ! l݁e  e  
              Mailer.podnu [        PK         ! I7  I7  	            A  Header.pmnu [        PK         ! R                  Field.pmnu [        PK         ! (AߟL  L                Address.podnu [        PK         ! B(  (              C  Internet.podnu [        PK         ! pFh  h              r" Send.pmnu [        PK         ! Cy                ( Cap.pmnu [        PK         ! V(F  F  
            A Filter.podnu [        PK         ! W    	            yM Mailer.pmnu [        PK         ! V[  [              va Util.podnu [        PK         ! ZAͱ                	n Send.podnu [        PK         ! G    	            z Field.podnu [        PK         ! @*1  1              ю Internet.pmnu [        PK         ! j                 Mailer/sendmail.pmnu [        PK         ! k	  	              ( Mailer/smtps.pmnu [        PK         ! ~)  )              { Mailer/rfc822.pmnu [        PK         ! ڰ&	  &	               Mailer/smtp.pmnu [        PK         ! ]                H Mailer/qmail.pmnu [        PK         ! Sqlh  h              u Mailer/testfile.pmnu [        PK         ! [;<    
             Header.podnu [        PK         ! p  p              e Util.pmnu [        PK         ! 6q      
             Address.pmnu [        PK         ! wP[H  H  
            	 Mailer.phpnu ȯ        PK         ! s                Y StockAlertSend.phpnu ̗        PK         ! ň                \ InvoiceSend.phpnu         PK         ! |9  9              ` Markdown.phpnu ȯ        PK         ! J  J              Cs Transport/SesV2Transport.phpnu ȯ        PK         ! ^p                ف Transport/SesTransport.phpnu ȯ        PK         ! 8	  	               Transport/LogTransport.phpnu ȯ        PK         ! zȋ                u Transport/ResendTransport.phpnu Iw        PK         ! F.                M Transport/ArrayTransport.phpnu ȯ        PK         ! r                F Mailable.phpnu ȯ        PK         ! 
B3                ul Mailables/Headers.phpnu ȯ        PK         ! G_)  )              t Mailables/Address.phpnu ȯ        PK         !                   w Mailables/Attachment.phpnu ȯ        PK         ! %?%  ?%              x Mailables/Envelope.phpnu ȯ        PK         ! l6$  $               Mailables/Content.phpnu ȯ        PK         ! &#                  MailServiceProvider.phpnu ȯ        PK         ! l[                , TextMessage.phpnu ȯ        PK         ! oMة                 Attachment.phpnu ȯ        PK         ! W                 SentMessage.phpnu ȯ        PK         ! ~Dc  c               SendQueuedMailable.phpnu ȯ        PK         ! IU&s'  s'               Message.phpnu ȯ        PK         ! o                V	 PendingMail.phpnu ȯ        PK         ! α3  3  
            p LICENSE.mdnu ȯ        PK         ! (~      %             resources/views/text/footer.blade.phpnu ȯ        PK         ! (~      $            > resources/views/text/panel.blade.phpnu ȯ        PK         ! (~      $             resources/views/text/table.blade.phpnu ȯ        PK         ! (~      &             resources/views/text/subcopy.blade.phpnu ȯ        PK         ! A      %            ` resources/views/text/header.blade.phpnu ȯ        PK         ! [w%}      %             resources/views/text/layout.blade.phpnu ȯ        PK         ! yf    &             resources/views/text/message.blade.phpnu ȯ        PK         ! A      %            ! resources/views/text/button.blade.phpnu ȯ        PK         ! =g      %            " resources/views/html/footer.blade.phpnu ȯ        PK         ! 5ZV5  5  $            G# resources/views/html/panel.blade.phpnu ȯ        PK         ! (H   H   $            $ resources/views/html/table.blade.phpnu ȯ        PK         ! d;    '            l% resources/views/html/themes/default.cssnu ȯ        PK         ! '      &            7 resources/views/html/subcopy.blade.phpnu ȯ        PK         ! X|ι    %            8 resources/views/html/header.blade.phpnu ȯ        PK         ! 
xU  U  %             : resources/views/html/layout.blade.phpnu ȯ        PK         ! aNP    &            ? resources/views/html/message.blade.phpnu ȯ        PK         ! ;J!@  @  %            B resources/views/html/button.blade.phpnu ȯ        PK         ! u`D  D              D MailManager.phpnu ȯ        PK         ! U                 composer.jsonnu ȯ        PK         ! ^BW  W               Events/MessageSent.phpnu ȯ        PK         ! >e  e              4 Events/MessageSending.phpnu ȯ        PK         !                   TicketReply.phpnu         PK         ! g7  7  &            > CreateTicketForFailedOutgoingEmail.phpnu         PK         ! (\*  *  $            ˯ Transformers/MimeMailTransformer.phpnu         PK         ! =4B    '            I Transformers/MailgunMailTransformer.phpnu         PK         ! g                  Parsing/ParsedEmail.phpnu         PK         ! a                  Parsing/StripQuotedEmailText.phpnu         PK         ! t;g  g               Parsing/StripEmailSignature.phpnu         PK         ! A                 Parsing/PurifyEmailBody.phpnu         PK         ! %    $             Parsing/GetStrippedHtmlFromEmail.phpnu         PK         ! 2                 TicketReferenceHash.phpnu         PK         ! fj    #            A TransformEmailIntoTicketOrReply.phpnu         PK         ! Z:                6 OutgoingEmailLogController.phpnu ȯ        PK         ! *F                ! OutgoingEmailLogItem.phpnu ȯ        PK         ! nV
3  3              & OutgoingEmailLogSubscriber.phpnu ȯ        PK         ! q\    !            . ConnectGmailAccountController.phpnu ȯ        PK         ! ]    #            3 HandleConnectGmailOauthCallback.phpnu ȯ        PK         ! Z                8 GmailApiMailTransport.phpnu ȯ        PK         ! AU                9 GmailClient.phpnu ȯ        PK         !                 !G PaymentReminder.phpnu         PK         ! c                WK CustomerInvoiceSend.phpnu         PK         ! @S                O InvoicePaymentCreate.phpnu         PK         ! `                S BillSend.phpnu         PK         ! 8g&9                X VenderBillSend.phpnu         PK         ! ($  $              \ testMail.phpnu         PK         ! Y                ^ BillPaymentCreate.phpnu         PK         ! R5                b ProposalSend.phpnu         PK    v v 6'  !g