The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#! /usr/bin/perl

use strict;
use warnings;

use Encode;
use Encode::Locale;
use Getopt::Long;
use Pod::Usage;
use SOAP::Lite;
use App::OTRS::CreateTicket;

print "$0 version " .  App::OTRS::CreateTicket->VERSION() . "\n\n";

@ARGV = map { decode(locale => $_, 1) } @ARGV if -t STDIN;
binmode STDOUT, ":encoding(console_out)" if -t STDOUT;
binmode STDERR, ":encoding(console_out)" if -t STDERR;

my @TicketFields
    = qw ( Title CustomerUser Queue Priority State Type Service SLA Owner Responsible );
my @ArticleFields = qw ( Subject Body ContentType ArticleType SenderType );

my @TicketOptions  = map { $_ . '=s' } @TicketFields;
my @ArticleOptions = map { $_ . '=s' } @ArticleFields;

my %Param = ();
GetOptions(
    \%Param,
    # options for connection
    'UserLogin=s',
    'Password=s',
    'Server=s',
    'Operation=s',
    'Namespace=s',
    'Url=s',
    'BodyFile=s',
    'Ssl',
    'help',
    # options for ticket
    @TicketOptions,
    # options for article
    @ArticleOptions,
    # dynamic fields; can be multiple
    'DynamicField=s%',
);

if ( $Param{help} || !$Param{Url} && !$Param{Server} ) {
    pod2usage( -exitstatus => 0 );
}

my $URL;

# if we do not have a URL, compose one
if ( !$Param{Url} ) {
    my $Server = $Param{Server} || 'localhost';
    my $HTTPType = $Param{Ssl} ? 'https://' : 'http://';
    $URL = $HTTPType . $Server . '/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnector'
}
else {
    $URL = $Param{Url};
}

# this name space should match the specified name space in the SOAP transport for the web service
my $NameSpace = $Param{Namespace} || 'http://www.otrs.org/TicketConnector/';

# this is operation to execute, it could be TicketCreate, TicketUpdate, TicketGet, TicketSearch
# or SessionCreate. and they must to be defined in the web service.
my $Operation = $Param{Operation} || 'TicketCreate';

# assign values for ticket and article data if undefined
$Param{Queue}    ||= 'Postmaster';
$Param{Priority} ||= '3 normal';
$Param{Type}     ||= 'default';
$Param{Title}    ||= 'No title';
$Param{State}    ||= 'new';

$Param{ContentType} ||= 'text/plain; charset=utf8';
$Param{Subject}     ||= $Param{Title};
$Param{SenderType}  ||= 'customer';

if ( $Param{BodyFile} ) {
    open my $Filehandle, '<', $Param{BodyFile} or die "Can't open file $Param{BodyFile}: $!";
    # read in file at once as in PBP
    $Param{Body} = do { local $/; <$Filehandle> };
    close $Filehandle;
} elsif ( !$Param{Body} ) {
    binmode STDIN;
    while ( my $Line = <> ) {
        $Param{Body} .= $Line;
    }
}

# Converting Ticket and Article data into SOAP data structure
my @TicketData;
for my $Element (@TicketFields) {
    if ( defined $Param{$Element} ) {
        my $Param = SOAP::Data->name( $Element => $Param{$Element} );
        push @TicketData, $Param;
    }
}

my @ArticleData;
for my $Element (@ArticleFields) {
    if ( defined $Param{$Element} ) {
        my $Param = SOAP::Data->name( $Element => $Param{$Element} );
        push @ArticleData, $Param;
    }
}

my $DynamicFieldXML;
if ($Param{DynamicField}) {
    for my $DynamicField ( keys %{$Param{DynamicField}} ) {
        $DynamicFieldXML .= "<DynamicField>\n"
            . "\t<Name><![CDATA[$DynamicField]]></Name>\n"
            . "\t<Value><![CDATA[$Param{DynamicField}->{$DynamicField}]]></Value>\n"
            . "</DynamicField>\n";
    }
}

my $SOAPObject = SOAP::Lite
    ->uri($NameSpace)
    ->proxy($URL)
    ->$Operation(
    SOAP::Data->name('UserLogin')->value($Param{UserLogin}),
    SOAP::Data->name('Password')->value($Param{Password}),
    SOAP::Data->name(
        'Ticket' => \SOAP::Data->value(
            @TicketData,
        )
    ),
    SOAP::Data->name(
        'Article' => \SOAP::Data->value(
            @ArticleData,
        )
    ),
    SOAP::Data->type( 'xml'=> $DynamicFieldXML ),
);

# check for a fault in the soap code
if ( $SOAPObject->fault ) {
    print $SOAPObject->faultcode, "\n";
    print $SOAPObject->faultstring, "\n";
    exit 0;
}

# otherwise print the results
else {
    # get the XML response part from the SOAP message
    my $XMLResponse = $SOAPObject->context()->transport()->proxy()->http_response()->content();
    # deserialize response (convert it into a perl structure)
    my $Deserialized = eval {
        SOAP::Deserializer->deserialize($XMLResponse);
    };

    # remove all the headers and other not needed parts of the SOAP message
    my $Body = $Deserialized->body();

    # just output relevant data and no the operation name key (like TicketCreateResponse)
    if ( defined $Body->{TicketCreateResponse}->{Error} ) {
        print "Could not create ticket.\n\n";
        print "ErrorCode:    $Body->{TicketCreateResponse}->{Error}->{ErrorCode}\n";
        print "ErrorMessage: $Body->{TicketCreateResponse}->{Error}->{ErrorMessage}\n\n";
        exit 0;
    }
    else {
        print "Created ticket $Body->{TicketCreateResponse}->{TicketNumber}\n";
    }
}

exit 1;

__END__

=head1 NAME

otrs.CreateTicket.pl - create tickets in OTRS via web services.

=head1 SYNOPSIS

Example 1: all arguments on the command line

otrs.CreateTicket.pl --Server otrs.example.com --Ssl --UserLogin myname  \
--Password secretpass --Title 'The ticket title' \
--CustomerUser customerlogin --Body 'The ticket body'
--DynamicField Branch="Sales UK" --DynamicField Source=Monitoring

Example 2: read body in from a file

otrs.CreateTicket.pl --Server otrs.example.com --Ssl --UserLogin myname  \
--Password secretpass --Title 'The ticket title' \
--CustomerUser customerlogin --BodyFile description.txt

Example 3: read body in from STDIN

otrs.CreateTicket.pl --Server otrs.example.com --Ssl --UserLogin myname  \
--Password secretpass --Title 'The ticket title' \
--CustomerUser customerlogin < description.txt

=head1 SYNTAX

otrs.CreateTicket.pl command syntax:

    otrs.CreateTicket.pl [arguments]

Arguments:

    SERVER CONNECTION
    --Server        Name of OTRS server.
    --Ssl (boolean) If SSL (https) should be used.
    
    Alternatively:
    --Url           Full URL to GenericTicket web service.
    
    USER AUTHENTICATION
    --UserLogin     Login name of valid Agent account.
    --Password      Password for user.
    
    TICKET DATA
    --Title         Title of ticket.
    --CustomerUser  Customer of ticket (mandatory!).
    --Priority      Defaults to '3 normal' if not specified.
    --Queue         Defaults to 'Postmaster' if not specified.
    --Owner         Optional.
    --Responsible   Optional, and only if activated on the server.
    --Service       Optional, and only if activated on the server.
    --SLA           Optional, and only if activated on the server.
    --Type          Optional, and only if activated on the server.
    
    ARTICLE DATA
    --Subject       Optional, defaults to title if not defined.
    --BodyFile      Name of file that contains body text of the message
    --Body          Body text of the message.
    --SenderType    Optional, defaults to 'Customer'.
    --ArticleType   Optional, defaults to 'web-request'.

    DYNAMIC FIELDS
    --DynamicField  Optional. Can be passed multiple times. Takes Name=Value pairs.


=cut