This is the directory where I have my RTSoap.pm, I wanted to have the SOAP related modules independent. you can easily place it in local/lib/RT and remove that line or modify it to /opt/rt3/local/lib/RT

Regards;
Roy
Torsten Brumm wrote:
Hi Roy,

in your SOAP Perl Scrip, you  do: use lib ("/opt/rt3/local/html/SOAP");
which file is this? Is it the attached perl from your mail?

Torsten

2006/9/29, Roy El-Hames <[EMAIL PROTECTED]>:

Yeah sure attached .. Its basic and far from perfect, it was my first
attempt writing soap service and just to serve my immediate needs, I
Just hope somebody can use it as a base and develop a full service that
can be used by .net and jave clients, The REST interface is very good,
but all the rage (what managers like) is SOAP and web services etc  ..
Please bare in mind it includes some of our custom status'es , which you
may have to remove ..
Regards;
Roy


Torsten Brumm wrote:
> Hi Roy,
>
> can you also share this soap services, the "old" one from BPS seams to
be
> outdated or only for rt2x i think.
>
> Torsten
>
> 2006/9/8, Roy El-Hames <[EMAIL PROTECTED]>:
>>
>> rt-3.6.1/Apache2
>> I've written a simple soap service  for rt that can communicate with
>> windows .net client via a wsdl file, however I can't find the best way
>> to send attachments, as rt will expect a mime object that combine the
>> update/create text with the attachment, I'll need to create this object
>> on the server (rt side), can anyone suggest the best way to do this,
the
>> .net developers would prefer to send attachments separate as a DIME
>> Referenced binary.
>> Thanks;
>> Roy
>> _______________________________________________
>> http://lists.bestpractical.com/cgi-bin/mailman/listinfo/rt-users
>>
>> Community help: http://wiki.bestpractical.com
>> Commercial support: [EMAIL PROTECTED]
>>
>>
>> Discover RT's hidden secrets with RT Essentials from O'Reilly Media.
>> Buy a copy at http://rtbook.bestpractical.com
>>
>
>
>



#!/usr/bin/perl

use lib ("/opt/rt3/local/html/SOAP");
use RTSoap;
use SOAP::Transport::HTTP;

SOAP::Transport::HTTP::CGI
  -> dispatch_to('/opt/rt3/local/html/SOAP','RTSoap')
  -> handle;

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="RTSoap" xmlns:soap="
http://schemas.xmlsoap.org/wsdl/soap/";
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/";
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/";
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"; xmlns:tns="urn:RTSoap"
xmlns:xsd="http://www.w3.org/2001/XMLSchema";
xmlns:xsd-ENV="http://schemas.xmlsoap.org/soap/envelope/";
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/";
xmlns:http="http://schemas.xmlsoap.org/wsdl/"; targetNamespace="urn:RTSoap"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/";>
  <wsdl:types>
    <xsd:schema elementFormDefault="qualified"
targetNamespace="urn:RTSoap">
      <xsd:element name="GetTicket">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element minOccurs="0" maxOccurs="1" name="Requester"
type="xsd:string" />
            <xsd:element minOccurs="1" maxOccurs="1" name="TicketId"
type="xsd:int" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>

      <xsd:element name="GetTicketsByRequestor">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element minOccurs="1" maxOccurs="1" name="Requester"
type="xsd:string" />
            <xsd:element minOccurs="1" maxOccurs="1" name="Status"
type="xsd:string" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>

     <xsd:element name="CreateTicket">
      <xsd:complexType>
                <xsd:sequence>
<xsd:element name="Requester" type="xsd:string" />
                        <xsd:element name="Cc" type="xsd:string" />
                        <xsd:element name="Subject" type="xsd:string" />
                        <xsd:element name="Queue" type="xsd:string" />
                        <xsd:element name="Attachment" type="xsd:string"
/>
                        <xsd:element name="Text" type="xsd:string" />
                </xsd:sequence>
       </xsd:complexType>
    </xsd:element>

     <xsd:element name="UpdateTicket">
      <xsd:complexType>
                <xsd:sequence>
<xsd:element name="Requester" type="xsd:string" />
                        <xsd:element name="Cc" type="xsd:string" />
                        <xsd:element name="TicketId" type="xsd:int" />
                        <xsd:element name="Subject" type="xsd:string" />
                        <xsd:element name="Attachment" type="xsd:string"
/>
                        <xsd:element name="Text" type="xsd:string" />
                        <xsd:element name="Status" type="xsd:string"/>
                </xsd:sequence>
      </xsd:complexType>
    </xsd:element>

      <xsd:element name="GetTicketResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element minOccurs="0" maxOccurs="1" name="Basics"
type="tns:Basics" />
            <xsd:element minOccurs="0" maxOccurs="1" name="Updates"
type="tns:Updates" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>

      <xsd:element name="GetTicketsByRequestorResponse">
        <xsd:complexType>
          <xsd:sequence>
                <xsd:element minOccurs="0" maxOccurs="1"
name="TicketsList" type="tns:TicketsList" />
          </xsd:sequence>
       </xsd:complexType>
      </xsd:element>

      <xsd:element name="CreateTicketResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element minOccurs="0" maxOccurs="1" name="TicketId"
type="tns:TicketId" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>

      <xsd:element name="UpdateTicketResponse">
        <xsd:complexType>
          <xsd:sequence>
                <xsd:element minOccurs="0" maxOccurs="1"
name="UpdateStatus" type="xsd:string" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>

     <xsd:complexType name="TicketId">
        <xsd:sequence>
         <xsd:element minOccurs="1" maxOccurs="1" name="TicketId"
type="xsd:string" />
        </xsd:sequence>
     </xsd:complexType>

      <xsd:complexType name="Basics">
        <xsd:sequence>
          <xsd:element minOccurs="1" maxOccurs="1" name="TicketId"
type="xsd:string" />
          <xsd:element minOccurs="0" maxOccurs="1" name="Subject"
type="xsd:string" />
          <xsd:element minOccurs="0" maxOccurs="1" name="Status"
type="xsd:string" />
          <xsd:element minOccurs="0" maxOccurs="1" name="LastUpdateTime"
type="xsd:string" />
          <xsd:element minOccurs="0" maxOccurs="1" name="ResolvedTime"
type="xsd:string" />
          <xsd:element minOccurs="0" maxOccurs="1" name="Requesters"
type="xsd:string" />
        </xsd:sequence>
      </xsd:complexType>

      <xsd:complexType name="Update">
        <xsd:sequence>
                <xsd:element minOccurs="0" name="TransactionCreator"
type="xsd:string" />
                <xsd:element minOccurs="0" name="TransactionCreated"
type="xsd:string" />
                <xsd:element minOccurs="0" name="Correspondence"
type="xsd:string" />
        </xsd:sequence>
      </xsd:complexType>

      <xsd:complexType name="Updates">
        <xsd:sequence>
               <xsd:element minOccurs="0" maxOccurs="unbounded"
name="Update" type="tns:Update" />
        </xsd:sequence>
      </xsd:complexType>

      <xsd:complexType name="TicketParams">
        <xsd:sequence>
                <xsd:element minOccurs="0" name="TicketId"
type="xsd:string" />
                <xsd:element minOccurs="0" name="Subject"
type="xsd:string" />
<xsd:element minOccurs="0" name="Status" type="xsd:string"
/>
        </xsd:sequence>
      </xsd:complexType>

      <xsd:complexType name="TicketsList">
        <xsd:sequence>
                <xsd:element minOccurs="0" maxOccurs="unbounded"
name="TicketParams" type="tns:TicketParams" />
        </xsd:sequence>
      </xsd:complexType>

    </xsd:schema>
  </wsdl:types>
<wsdl:message name="GetTicketSoapIn">
    <wsdl:part name="parameters" element="tns:GetTicket" />
  </wsdl:message>
  <wsdl:message name="GetTicketSoapOut">
    <wsdl:part name="parameters" element="tns:GetTicketResponse" />
  </wsdl:message>

  <wsdl:message name="GetTicketsByRequestorSoapIn">
    <wsdl:part name="parameters" element="tns:GetTicketsByRequestor" />
  </wsdl:message>
  <wsdl:message name="GetTicketsByRequestorSoapOut">
    <wsdl:part name="parameters"
element="tns:GetTicketsByRequestorResponse" />
  </wsdl:message>

  <wsdl:message name="CreateTicketSoapIn">
    <wsdl:part name="parameters" element="tns:CreateTicket" />
  </wsdl:message>
  <wsdl:message name="CreateTicketSoapOut">
    <wsdl:part name="parameters" element="tns:CreateTicketResponse" />
  </wsdl:message>

  <wsdl:message name="UpdateTicketSoapIn">
    <wsdl:part name="parameters" element="tns:UpdateTicket" />
  </wsdl:message>
  <wsdl:message name="UpdateTicketSoapOut">
    <wsdl:part name="parameters" element="tns:UpdateTicketResponse" />
  </wsdl:message>

  <wsdl:portType name="ServiceSoap">

    <wsdl:operation name="GetTicket">
      <wsdl:input message="tns:GetTicketSoapIn" />
      <wsdl:output message="tns:GetTicketSoapOut" />
    </wsdl:operation>

    <wsdl:operation name="GetTicketsByRequestor">
      <wsdl:input message="tns:GetTicketsByRequestorSoapIn" />
      <wsdl:output message="tns:GetTicketsByRequestorSoapOut" />
    </wsdl:operation>

   <wsdl:operation name="CreateTicket">
      <wsdl:input message="tns:CreateTicketSoapIn" />
      <wsdl:output message="tns:CreateTicketSoapOut" />
    </wsdl:operation>

   <wsdl:operation name="UpdateTicket">
      <wsdl:input message="tns:UpdateTicketSoapIn" />
      <wsdl:output message="tns:UpdateTicketSoapOut" />
    </wsdl:operation>

  </wsdl:portType>

  <wsdl:binding name="ServiceSoap" type="tns:ServiceSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"; />

    <wsdl:operation name="GetTicket">
      <soap:operation soapAction="urn:RTSoap/GetTicket" style="document"
/>
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="GetTicketsByRequestor">
      <soap:operation soapAction="urn:RTSoap/GetTicketsByRequestor"
style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>

    <wsdl:operation name="CreateTicket">
      <soap:operation soapAction="urn:RTSoap/CreateTicket"
style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>

   <wsdl:operation name="UpdateTicket">
      <soap:operation soapAction="urn:RTSoap/UpdateTicket"
style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>

  </wsdl:binding>
  <wsdl:binding name="ServiceSoap12" type="tns:ServiceSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"; />

    <wsdl:operation name="GetTicket">
<soap12:operation soapAction="urn:RTSoap/GetTicket" style="document"
/>
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>

    <wsdl:operation name="GetTicketsByRequestor">
      <soap12:operation soapAction="urn:RTSoap/GetTicketsByRequestor"
style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>

    <wsdl:operation name="CreateTicket">
<soap12:operation soapAction="urn:RTSoap/Create" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  <wsdl:operation name="UpdateTicket">
      <soap12:operation soapAction="urn:RTSoap/UpdateTicket"
style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>

</wsdl:binding>
  <wsdl:service name="Service">
    <wsdl:port name="ServiceSoap" binding="tns:ServiceSoap">
      <soap:address location="http://YOURRTADDRESS/res.cgi"; />
    </wsdl:port>
    <wsdl:port name="ServiceSoap12" binding="tns:ServiceSoap12">
      <soap12:address location="http://YOURRTADDRESS/res.cgi"; />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>




package RTSoap ;

use lib ( "/opt/rt3/lib", "/opt/rt3/local/lib" );
use RT;
use RT::Ticket;
use RT::Users;
CleanEnv;
RT::LoadConfig;
RT::Init;
our @ISA = qw( SOAP::Server::Parameters );

sub new {
        $self = shift;
        my $class = ref($self) || $self;
        bless {_tic => shift} => $class;
        return $class;
}

sub Hello {
        ### test function ##is soap lite working
        my $self = shift;
        my ($string) = @_;
        return "Hello " . $string;
}

sub RT_User {
        my ($address) = shift;
        unless ($address) {
                return (undef);
        }
        my $user = RT::CurrentUser->new();
        $user->LoadByEmail($address);
        unless ($user->Id){
                return undef;
        }
        return $user;
}

sub GetTicketsByRequestor {
        #### you get a requester and may get status resolved
        ## if you get status = resolved only return a list of resolved
tickets else return all non resolved tickets for this requestor

        my $self = shift;
        my %args = %{pop->method};
        my $requestor = $args{'Requester'};
        my $status = $args{'Status'};
        my $error = undef;

        ## Get user
        my $CurrentUser = RT_User($requestor);
        if (!$CurrentUser) {
                $error = "Not RT user, verify email address";
                die $error;
        }
        $RT::Logger->error("Web service invoked do I get back here
$requestor");
        my $tickets = new RT::Tickets($CurrentUser);
        if (!$tickets) {
                $error = "No tickets of this status for this user";
                die $error;
        }
        my @collection;
        $tickets->LimitWatcher(TYPE => 'Requestor', VALUE =>$requestor);
        if ($status =~ /resolved/) {
                $tickets->LimitStatus(VALUE => "resolved");
                $tickets->LimitStatus(VALUE => "completed");
        } else {
                $tickets->LimitStatus(VALUE => "open");
                $tickets->LimitStatus(VALUE => "new");
                $tickets->LimitStatus(VALUE => "stalled");
                $tickets->LimitStatus(VALUE => "re-opened");
                $tickets->LimitStatus(VALUE => "monitored");
        }
        $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
        while (my $ticket = $tickets->Next) {
                my @list;
                @list = (
                        SOAP::Data->name("TicketParams" =>
\SOAP::Data->value(

SOAP::Data->name('TicketId')->value($ticket->Id()),

SOAP::Data->name('Subject')->value($ticket->Subject()),

SOAP::Data->name('Status')->value($ticket->Status())))
                );
                push @collection,@list
        }
        if ([EMAIL PROTECTED]) {
                $error = "Failed to retrieve ticket list ";
                die $error;
        }
        return (
                SOAP::Data->name("TicketsList" => \SOAP::Data->value(

SOAP::Data->name('CallItWhatEver')->value(@collection)))
        );

}

sub GetTicket(){
        ### you get a ticket id return the ticket providing the supplied
requestor is in this ticket requesters string
        use RT::Transactions;
        my $self = shift;
        my %args = %{pop->method};
        my $requestor = $args{'Requester'};
        my $ticketId = $args{'TicketId'};
        my $error = undef;
### debug
#       $RT::Logger->error("Web service invoked and my requestor is
$requestor");
#####
        my @collection;
        my $CurrentUser = RT_User($requestor);
        if (!$CurrentUser) {
                $error = "Not RT user, verify email address";
                die $error;
        }
        my $ticket = new RT::Ticket($CurrentUser,$self->{_tic});
        $ticket->Load($ticketId);
        unless ( $ticket->id ) {
$error = "Error loading ticket, maybe invalid ticket id" ;
                die $error;
        }
        if ($ticket->Id()){
                unless ($ticket->IsWatcher(Type => 'Requestor',
PrincipalId => $CurrentUser->PrincipalId)) {
                        $error = "Permission denied. $requestor is not a
requestor for this ticket";
                        die $error;
                }
                $Tid = $ticket->Id();
                $TSubject = $ticket->Subject();
                $TStatus = $ticket->Status();
                $TRequestors =
$ticket->Requestors->MemberEmailAddressesAsString;
                $TLastUpdate = $ticket->LastUpdated;
                $TResolved = $ticket->Resolved;
                my $trans = RT::Transactions->new($RT::SystemUser);
                $trans->LimitToTicket($Tid);
                $trans->Limit(
                        FIELD    => 'Type',
                        OPERATOR => '=',
                        VALUE    => "Correspond"
                   );
                   $trans->Limit(
                        FIELD    => 'Type',
                        OPERATOR => '=',
                        VALUE    => "Create"
                   );
                while (my $tran = $trans->Next ) {
                        my $attachments =
RT::Attachments->new($RT::SystemUser);
                        $attachments->Limit(FIELD    => 'TransactionId',
                                            OPERATOR => '=',
                                            VALUE    => $tran->Id()
                        );
                        $attachments->Limit(FIELD    => 'ContentType',
                                        OPERATOR => '=',
                                        VALUE    => 'text/plain'
                        );
                        $attachments->Limit(FIELD    => 'Content',
                                        OPERATOR => '!=',
                                        VALUE    => ''
                        );
                        my ($update,@updates) ;
                        while (my $attachment = $attachments->Next) {
                                my $aid = $attachment->Id() ;
my $atid = $attachment->TransactionId() ;
                                my $acontent = $attachment->Content();
                                $update = (

SOAP::Data->type('string')->name('Correspondence')->value($acontent)
                                );
                         push @updates,$update ;
                        }
                        my @t = (
                            SOAP::Data->name("Update" =>
\SOAP::Data->value(

SOAP::Data->type('string')->name('TransactionCreator')->value($tran->CreatorObj->Name),

SOAP::Data->type('string')->name('TransactionCreated')->value($tran->Created),

SOAP::Data->type('complexType')->name('Correspondence')->value(@updates)
                                        ))
                        );
                        push  @collection,@t ;
                }
        } else {
                $error = "Error loading ticket updates";
                die $error;
        }
        # format SOAP::Data Object and return
        return (
                SOAP::Data->name('Basics' => \SOAP::Data->value(
                        SOAP::Data->name('TicketId')->value($Tid),
                        SOAP::Data->name('Subject')->value($TSubject),
                        SOAP::Data->name('Status')->value($TStatus),


SOAP::Data->name('LastUpdateTime')->value($TLastUpdate),

SOAP::Data->name('ResolvedTime')->value($TResolved),

SOAP::Data->name('Requesters')->value($TRequestors))),
                SOAP::Data->name('Updates' => \SOAP::Data->value(

SOAP::Data->name('CallItWhatEver')->value(@collection)))
        );
}

sub CreateTicket {
        #### attachments handling is rubbish ... need looking at later
        use MIME::Entity;
        my $MimeMessage;
        my $self = shift;
        my %args = %{pop->method};
        my $requestor = $args{'Requester'};
        my $cc = $args{'Cc'};
        my $queue = $args{'Queue'};
        my $subject = $args{'Subject'};
        my $txt = $args{'Text'};
        my $attach = $args{'Attachment'};
###### for debug purposes ###
        $RT::Logger->error("SOAPING:do I get text? ($txt)");
        if ($attach) {
                 $RT::Logger->error("SOAPING:I have attachment");
        }

############
        my $CurrentUser = RT_User($requestor);
        if (!$CurrentUser) {
                 $CurrentUser = $RT::SystemUser ; ### we need to create
tickets for new users as well
        }

        if ($attach) {
                $MimeMessage = $attach;
        } else {
                $Mime = MIME::Entity->build(
                              From => $requestor,
                              Subject =>$subject,
                              Cc => $cc,
                              Type => 'text/plain',
                              Data => $txt
                        );
                 $MimeMessage = $Mime->stringify();
        }


        my $newticket = new RT::Ticket($CurrentUser);
        my $parser = RT::EmailParser->new();
        $parser->ParseMIMEEntityFromScalar($MimeMessage);
        #$parser->ParseMIMEEntityFromScalar($attach);

        my %create_args = (
                Queue => $queue,
                Requestor => $requestor,
                Cc => $cc,
                Subject => $subject,
                MIMEObj => $parser->Entity
                );

        my ( $id, $Trans, $ErrMsg ) = $newticket->Create(%create_args);
        unless ( $id && $Trans ) {
                die $ErrMsg ;
        }
                    return (SOAP::Data->name('TicketId' =>
\SOAP::Data->value(
SOAP::Data->name('TicketId')->value($id))));
}

sub UpdateTicket {
### only update if the supplied requester is the ticket requester
        ### attachments attchments attachments
        use MIME::Entity;
        my $MimeMessage;
        my $self = shift;
        my %args = %{pop->method};
        my $txt = $args{'Text'};
        my $attach = $args{'Attachment'};
        my $CurrentUser = RT_User($args{'Requester'});
        my $error = undef;
        if (!$CurrentUser) {
                $error = "Not RT user, verify email address";
                die $error;
        }

        if ($attach && $attach != '') {
                $MimMessage = $attach;
        } else {
                $Mime = MIME::Entity->build(
                              From => $requestor,
                              Subject =>$subject,
                              Cc => $cc,
                              Type => 'text/plain',
                              Data => $txt
                        );
                 $MimeMessage = $Mime->stringify();
        }

        my $ticket = new RT::Ticket($CurrentUser);
        $ticket->Load($args{'TicketId'}) ;
        unless ( $ticket->id ) {
$error = "Error loading ticket, maybe invalid ticket id";
                die $error;
        }
        unless ($ticket->IsWatcher(Type => 'Requestor', PrincipalId =>
$CurrentUser->PrincipalId)) {
                        $error = "Permission denied. $requestor is not a
requestor for this ticket";
                        die $error;
        }
        my $parser = RT::EmailParser->new();
        $parser->ParseMIMEEntityFromScalar($MimeMessage);
        my ($val,$msg);
        if ( $args{'Status'} && $args{'Status'} ne $ticket->Status ) {
                ( $val, $msg ) = $ticket->SetStatus( $args{'Status'} );
                if ($val == 0) {
                        $msg = "Failed to update status";
                        die $msg;
                }
        }
        if ($MimeMessage) {
                ( $val, $msg ) = $ticket->Correspond( MIMEObj =>
$parser->Entity,
                                                        CcMessageTo  =>
$args{'Cc'});
                if ($val == 0) {
                        $msg = "Failed to update ticket";
                        die $msg;
                }
        }
        return (SOAP::Data->name('UpdateStatus')->value("OK"));
}
1;









_______________________________________________
http://lists.bestpractical.com/cgi-bin/mailman/listinfo/rt-users

Community help: http://wiki.bestpractical.com
Commercial support: [EMAIL PROTECTED]


Discover RT's hidden secrets with RT Essentials from O'Reilly Media. Buy a copy at http://rtbook.bestpractical.com

Reply via email to