Sending Very Specific SOAP::Lite requests - perl

My company uses has a lot of internal APIs that use very specific header and formatting requirements. I am new to SOAP::Lite and I'm trying to make it work within the company's framework.
Try #1:
Ideally, I would like to be able to just take the raw XML template (see bottom of the post), populate some placeholder variables, and send it to the endpoint using the following code:
my $client = SOAP::Lite->new( proxy => "$serviceURL");
my $reply = $client->InquireEnterpriseOrderDataRequest($rawxml);
However, this results in my header and request sections being enclosed in it's own "envelope", "body" and "InquireEnterpriseOrderDataRequest" which is rejected by the service.
Try #2:
The next thing I tried was to break my request into two pieces: header and request and use SOAP::Data and SOAP::Header to send those:
my $rawxmlheader = '<ns2:MessageHeader xmlns:ns2="http://mycompany.com/MessageHeader.xsd" xmlns="http://mycompany.com/CingularDataModel.xsd">
<ns2:TrackingMessageHeader>
<version>111</version>
<originalVersion/>
<messageId/>
<originatorId>ABC</originatorId>
<responseTo/>
<returnURL/>
<timeToLive>360000</timeToLive>
<conversationId>9AF0E9281A524262980F5284F4C57888_CCE423E277C74FA9A84D2155CD612EB3_0</conversationId>
<routingRegionOverride/>
<dateTimeStamp>2017-05-12T12:47:53Z</dateTimeStamp>
<uniqueTransactionId>mytransid</uniqueTransactionId>
</ns2:TrackingMessageHeader>
<ns2:SecurityMessageHeader>
<userName>myusername</userName>
<userPassword>mypass</userPassword>
</ns2:SecurityMessageHeader>
<ns2:SequenceMessageHeader>
<sequenceNumber/>
<totalInSequence/>
</ns2:SequenceMessageHeader>
</ns2:MessageHeader>';
my $rawxmlrequest = '<OrderSearchCriteria>
<OrderDetails>
<SearchByOrderAction>
<orderActionNumber>12345654</orderActionNumber>
<orderActionVersion>1</orderActionVersion>
</SearchByOrderAction>
</OrderDetails>
</OrderSearchCriteria>
<provisioningDetailsIndicator>true</provisioningDetailsIndicator>';
my $client = SOAP::Lite->new( proxy => "$serviceURL");
my $header = SOAP::Header->type('xml' => $rawxmlheader);
my $elem = SOAP::Data->type('xml' => $rawxmlrequest);
my #arguments;
push(#arguments, $header);
push(#arguments, $elem);
my $reply = $client->InquireEnterpriseOrderDataRequest(#arguments);
This produced a very similar request to what was needed with the exception that the InquireEnterpriseOrderDataRequest blob did not contain the xsi:schemaLocation, xmlns or xmlns:xsi values that seem to be required.
Try #3:
Now I was grasping at straws, so I also tried to granularly create my own XML using something like this:
my $temp_elements =
SOAP::Data->name("OrderSearchCriteria" => \SOAP::Data->value(
SOAP::Data->name("OrderDetails" => \SOAP::Data->value(
SOAP::Data->name("SearchByOrderAction" => \SOAP::Data->value(
SOAP::Data->name("orderActionNumber" => '301496944'),
SOAP::Data->name("orderActionVersion" => '3')
)
)
)
))
)->type("SomeObject");
my $client = SOAP::Lite->new( proxy => "$serviceURL");
my $reply = $client->InquireEnterpriseOrderDataRequest($temp_elements);
The problem here was that I don't know how to include the xsi:schemaLocation, xmlns and xmlns:xsi values or prepend the header information.
Of course, I'd like to go with the simplest possible implementation but any suggestions are appreciated! Thanks in advance!
Required request format:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<ns2:MessageHeader xmlns:ns2="http://mycompany.com/MessageHeader.xsd" xmlns="http://mycompany.com/CingularDataModel.xsd">
<ns2:TrackingMessageHeader>
<version>111</version>
<originalVersion/>
<messageId/>
<originatorId>ABC</originatorId>
<responseTo/>
<returnURL/>
<timeToLive>360000</timeToLive>
<conversationId>9AF0E9281A524262980F5284F4C57888_CCE423E277C74FA9A84D2155CD612EB3_0</conversationId>
<routingRegionOverride/>
<dateTimeStamp>2017-04-11T18:47:53Z</dateTimeStamp>
<uniqueTransactionId>mytransid</uniqueTransactionId>
</ns2:TrackingMessageHeader>
<ns2:SecurityMessageHeader>
<userName>myusername</userName>
<userPassword>mypass</userPassword>
</ns2:SecurityMessageHeader>
<ns2:SequenceMessageHeader>
<sequenceNumber/>
<totalInSequence/>
</ns2:SequenceMessageHeader>
</ns2:MessageHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<InquireEnterpriseOrderDataRequest xsi:schemaLocation="http://mycompany.com/InquireEnterpriseOrderDataRequest.xsd" xmlns="http://mycompany.com/InquireEnterpriseOrderDataRequest.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OrderSearchCriteria>
<OrderDetails>
<SearchByOrderAction>
<orderActionNumber>12345654</orderActionNumber>
<orderActionVersion>1</orderActionVersion>
</SearchByOrderAction>
</OrderDetails>
</OrderSearchCriteria>
<provisioningDetailsIndicator>true</provisioningDetailsIndicator>
</InquireEnterpriseOrderDataRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This should generate the required response using SOAP::Lite request.
use strict;
use warnings;
use SOAP::Lite +trace=>'all';
$on_action = '';
$proxy = 'http://serviceURL';
$soap = SOAP::Lite->new(proxy => $proxy);
$soap->on_action(sub {$on_action});
$soap->readable(1);
$soap->autotype(0);
$soap->serializer->register_ns('http://mycompany.com/InquireEnterpriseOrderDataRequest.xsd' => 'xsi');
$soap->serializer->register_ns('http://mycompany.com/InquireEnterpriseOrderDataRequest.xsd' => 'xsi:schemaLocation');
$soap->default_ns('http://mycompany.com/InquireEnterpriseOrderDataRequest.xsd');
$soap->envprefix('SOAP-ENV');
$sheader = SOAP::Header->name(MessageHeader =>\SOAP::Header->value(SOAP::Header->name(TrackingMessageHeader => \SOAP::Header->value(
SOAP::Header->name(version => 111),
SOAP::Header->name(originalVersion => ''),
SOAP::Header->name(messageId => ''),
SOAP::Header->name(originatorId => 'ABC'),
SOAP::Header->name(responseTo => ''),
SOAP::Header->name(returnURL => ''),
SOAP::Header->name(timetoLive => 360000),
SOAP::Header->name(conversationId => '9AF0E9281A524262980F5284F4C57888_CCE423E277C74FA9A84D2155CD612EB3_0'),
SOAP::Header->name(routingRegionOverride => ''),
SOAP::Header->name(dateTimeStamp => '2017-04-11T18:47:53Z'),
SOAP::Header->name(timetoLive => 'mytransid'),
))->prefix('ns2')))->attr({'xmlns:ns2' => 'http://mycompany.com/MessageHeader.xsd',xmlns => 'http://mycompany.com/CingularDataModel.xsd'})->prefix('ns2');
push #request,(
SOAP::Data->name(OrderSearchCriteria => \SOAP::Data->value(
SOAP::Data->name(OrderDetails => \SOAP::Data->value(
SOAP::Data->name(SearchByOrderAction => \SOAP::Data->value(
SOAP::Data->name(orderActionNumber => 12345654),
SOAP::Data->name(orderActionVersion => 1),
)))))));
$reply = $soap->InquireEnterpriseOrderDataRequest($sheader,#request);

Related

How can I send mail in a Perl script?

I have adapted a script from the Perl Cookbook. I am testing it to send mail to myself in gmail.
#!/usr/bin/perl
use strict;
use warnings;
use MIME::Lite;
my $msg;
$msg = MIME::Lite->new(From => 'zmumba#gmail.com',
To => 'zmumba#gmail.com',
Subject => 'My office photo',
Type => 'multipart/mixed');
$msg->attach(Type => 'image/png',
Path => '/home/zmumba/ZMD_Proj/Docs/Reporting',
Filename => 'office_lyout.png');
$msg->attach(Type => 'TEXT',
Data => 'I hope you can use this!');
$msg->send( );
When I run this script, I get the message "/home/zmumba/ZMD_Proj/Docs/Reporting" not readable.
From here How can I send mail through Gmail with Perl? , I now understand that I have to send mail through a mailserver to use MIME::Lite. So I replaced
$msg = MIME::Lite->new(From => 'zmumba#gmail.com
with
$msg = Email::Send::Gmail->new(From => 'zmumba#gmail.com
and I get the error "Can't locate object method "new" via package Email::Send::Gmail".
Then I tried
$msg = Net::IMAP::Simple::SSL->new(From => 'zmumba#gmail.com',
and I get "Odd number of elements in hash assignment at /home/zmumba/perl5/lib/perl5/Net/IMAP/Simple.pm line 25.
Can't call method "attach" on an undefined value at ...".
Any assistance on how to go about it?
Thanks in anticipation.
The Perl Cookbook is 20 years old and its recommendations will be out of date. Using MIME::Lite is discouraged.
MIME::Lite is not recommended by its current maintainer. There are a number of alternatives, like Email::MIME or MIME::Entity and Email::Sender, which you should probably use instead. MIME::Lite continues to accrue weird bug reports, and it is not receiving a large amount of refactoring due to the availability of better alternatives. Please consider using something else.
You should probably follow their recommendation and use Email::Sender.
"Can't locate object method "new" via package Email::Send::Gmail"
You need to load Email::Send::Gmail with use Email::Send::Gmail.
You may need to install the Email::Send::Gmail module. It's simplest to do this using either cpanminus or install a fresh Perl with perlbrew and then use cpanminus.
Then I tried
$msg = Net::IMAP::Simple::SSL->new(From => 'zmumba#gmail.com',
and I get "Odd number of elements in hash assignment at /home/zmumba/perl5/lib/perl5/Net/IMAP/Simple.pm line 25.
MIME::Lite, Email::Send::Gmail, and Net::IMAP::Simple::SSL are different libraries with different interfaces that take different arguments differently. Refer to their documentation for how to use them.
As mentioned before, both MIME::Lite and Email::Send is discouraged -
Email::Send is going away... well, not really going away, but it's
being officially marked "out of favor." It has API design problems
that make it hard to usefully extend and rather than try to deprecate
features and slowly ease in a new interface, we've released
Email::Sender which fixes these problems and others
I have created a script which uses Email::MIME, Email::Sender::Simple, Email::Sender::Transport::SMTP for sending mail. You can take a look at https://github.com/rai-gaurav/perl-toolkit/tree/master/Mail and use it as per your requirement.
Important lines from that code are -
sub create_mail {
my ( $self, $file_attachments, $mail_subject, $mail_body ) = #_;
my #mail_attachments;
if (#$file_attachments) {
foreach my $attachment (#$file_attachments) {
my $single_attachment = Email::MIME->create(
attributes => {
filename => basename($attachment),
content_type => "application/json",
disposition => 'attachment',
encoding => 'base64',
name => basename($attachment)
},
body => io->file($attachment)->all
);
push( #mail_attachments, $single_attachment );
}
}
# Multipart message : It contains attachment as well as html body
my #parts = (
#mail_attachments,
Email::MIME->create(
attributes => {
content_type => 'text/html',
encoding => 'quoted-printable',
charset => 'US-ASCII'
},
body_str => $mail_body,
),
);
my $mail_to_users = join ', ', #{ $self->{config}->{mail_to} };
my $cc_mail_to_users = join ', ', #{ $self->{config}->{mail_cc_to} };
my $email = Email::MIME->create(
header => [
From => $self->{config}->{mail_from},
To => $mail_to_users,
Cc => $cc_mail_to_users,
Subject => $mail_subject,
],
parts => [#parts],
);
return $email;
}
sub send_mail {
my ( $self, $email ) = #_;
my $transport = Email::Sender::Transport::SMTP->new(
{
host => $self->{config}->{smtp_server}
}
);
eval { sendmail( $email, { transport => $transport } ); };
if ($#) {
return 0, $#;
}
else {
return 1;
}
}
Gmail and other mail servers will not allow unauthorized relay. In most cases you will need authorized access.
Here is what I use, after also trying many modules. Maybe this solution seems a little bit overdone, but it's easy to change for HTML with plain text as alternative or other attachments. Parameters of the transport are the most common ones.
#!perl
use strict;
use warnings;
use utf8;
use Email::Sender::Simple qw(sendmail try_to_sendmail);
use Email::Sender::Transport::SMTPS;
use Email::Simple ();
use Email::Simple::Creator ();
use Email::MIME;
send_my_mail('john.doe#hisdomain.com','Test','Test, pls ignore');
sub send_my_mail {
my ($to_mail_address, $subject, $body_text) = #_;
my $smtpserver = 'smtp.mydomain.com';
my $smtpport = 587;
my $smtpuser = 'me#mydomain.com';
my $smtppassword = 'mysecret';
my $transport = Email::Sender::Transport::SMTPS->new({
host => $smtpserver,
ssl => 'starttls',
port => $smtpport,
sasl_username => $smtpuser,
sasl_password => $smtppassword,
#debug => 1,
});
my $text_part = Email::MIME->create(
attributes => {
'encoding' => 'quoted-printable',
'content_type' => 'text/plain',
'charset' => 'UTF-8',
},
'body_str' => $body_text,
);
my $alternative_part = Email::MIME->create(
attributes => {
'content_type' => 'multipart/alternative',
},
parts => [ $text_part, ],
);
my $email = Email::MIME->create(
header_str => [
To => $to_mail_address,
From => "Website <$smtpuser>",
Subject => $subject,
],
attributes => {
'content_type' => 'multipart/mixed',
},
parts => [ $alternative_part ],
);
my $status = try_to_sendmail($email, { transport => $transport });
return $status;
}
Take a look at Email::Send::Gmail module. It might solve your problem.

Debugging HTTP::Tiny

I am using HTTP::Tiny to interact with rest api below an example :
my $req = $ua->get('https://myapi.com/user?xyz', {headers =>
{token => 'xyzzz',
data => '5343'.
}});
i am using Data::Dumper to see the response header and body
print Dumper($req); -#i can see the response
is there an option to see also the HTTP request i am sending using HTTP::Tiny ,without the need of other tool like wireshark etc..
something like what mojo::useragent achieve with MOJO_CLIENT_DEBUG=1 any idea?
I don't see such a feature mentioned in the HTTP::Tiny documentation, nor do I see it facilitated in the source code for the module. However, you can use a module such as Test::MockModule to get a closer look at what is going on. Here is an example:
#!/usr/bin/env perl
use strict;
use warnings;
use HTTP::Tiny;
use Test::MockModule;
use Data::Dumper;
my $t = HTTP::Tiny->new();
my $mock = Test::MockModule->new('HTTP::Tiny');
$mock->redefine('_request' => sub {
warn "In _request: ", Dumper {self => $_[0], method => $_[1], url => $_[2], args => $_[3]};
return $mock->original('_request')->(#_);
});
print "Response: ", Dumper $t->get('http://localhost:3000');
So in this snippet we are mocking _request, but the mock method we install in behalf of _request calls out to the original _request method, so our mocked method becomes a wrapper around the original method, and we can dump the underlying object and args passed into the method. I'm doing the dump prior to the request being made, but I could have done the initial dump, captured the return value of the real request, and then dump the object again afterward if I thought it might contain additional useful information. So long as we return the actual response, the calling code is none the wiser.
Here's an example of the output. The URL I'm hitting is just a default Mojolicious::Lite app.
In _request: $VAR1 = {
'method' => 'GET',
'self' => bless( {
'agent' => 'HTTP-Tiny/0.076',
'no_proxy' => [],
'max_redirect' => 5,
'keep_alive' => 1,
'verify_SSL' => 0,
'timeout' => 60
}, 'HTTP::Tiny' ),
'args' => {},
'url' => 'http://localhost:3000'
};
Response: $VAR1 = {
'headers' => {
'content-length' => '146',
'content-type' => 'text/html;charset=UTF-8',
'server' => 'Mojolicious (Perl)',
'date' => 'Sun, 26 May 2019 03:19:57 GMT'
},
'protocol' => 'HTTP/1.1',
'reason' => 'OK',
'url' => 'http://localhost:3000',
'success' => 1,
'status' => '200',
'content' => '<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body><h1>Welcome to the Mojolicious real-time web framework!</h1>
</body>
</html>
'
};
Before I was able to know that _request was where I wanted to target my mock, I had to look at the source code for HTTP::Tiny. Fortunately the ::Tiny part of the module means there's not a whole lot to look at. It's really a relatively simple module.
You may decide that for your own purposes it makes more sense to introduce your wrapper at some other point in HTTP::Tiny, but wrapping _request seems like a pretty good choice for most cases.

Posting metric to google analytics from perl script

I need to generate a google analytic event from my perl cron job.
Based on https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide
POST to www.google-analytics.com/collect will accept the metrics.
I tried posting metric using following code, but I am not seeing them in google analytics realtime page.
use LWP::UserAgent;
$ua=LWP::UserAgent->new;
$ua->default_header("Content-type" => "application/x-rm-urlencoded");
my $response = $ua->post("http://www.google-analytics.com/collect",
"v" => 1,
"tid" => "UA-XXXXXXXX-Y",
"cid" => 125,
"t" => "pageview",
"dh" => "Cron",
"dp" => "Cron.php",
"dt" => "CronTitle");
print $response->status_line;'
Response is "200 OK", but I am not seeing the data in google analytics webpage.
I am able to post analytics from my php scripts, but could not post analytics from perl script.
I solved my problem as follows.
Created an utility php script and called that php script from my perl script. Then it worked.
<?php
$ec = $_GET['ec'];
$ea = $_GET['ea'];
$el = $_GET['el'];
$ev = $_GET['ev'];
// Send event tracking as well.
$url = 'http://www.google-analytics.com/collect';
$event_data = array(
'v' => '1',
'tid' => 'UA-XXXXXX-Y',
'cid' => $_SERVER["REMOTE_ADDR"],
't' => 'event',
'ec' => $ec,
'ea' => $ea,
'el' => $el,
'ev' => $ev);
$event_options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($event_data),
),
);
$event_context = stream_context_create($event_options);
$result = file_get_contents($url, false, $event_context);
echo "XXX";
?>

Logging into Jenkins via Perl script

I am able to change a build's description with the following program. This will change the build's description to "FOO FOO FOO". Unfortunately, my login doesn't work. Right now, this test Jenkins build server has no security on it. However, on our regular Jenkins server, you need to be logged in to change a build's description.:
#! /usr/bin/env perl
use 5.12.0;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);
use Data::Dumper;
use constant {
JENKINS_BASE => "http://build.vegibank.com/",
USER_ID => "buildguy",
PASSWORD => "swordfish",
};
use constant {
LOGIN_URL => JENKINS_BASE . '/j_acegi_security_check',
JOB_URL => JENKINS_BASE . '/job',
SUBMIT_DESCRIPTION => 'submitDescription',
};
my $job_number = 4;
my $job_name = "proginator-2.0";
my $description = "FOO FOO FOO";
my $user_agent = LWP::UserAgent->new or die qq(Can't get User Agent);
#
# My Login Stuff (but it doesn't do anything w/ security off
#
my $response = $user_agent->request (
POST LOGIN_URL,
[
j_username => USER_ID,
j_password => PASSWORD,
],
);
$response = $user_agent->request (
POST "#{[JOB_URL]}/$job_name/$job_number/#{[SUBMIT_DESCRIPTION]}",
[
description => "$description",
],
);
I'm trying to connect to the Jenkins login session, but I don't believe I'm doing it quite right. When I attempt to login, I get a 302 response and the following dump of my response object:
$VAR1 = bless( {
'_protocol' => 'HTTP/1.1',
'_content' => '',
'_rc' => '302',
'_headers' => bless( {
'connection' => 'close',
'client-response-num' => 1,
'set-cookie' => 'JSESSIONID=1D5DF6FAF8714B2ACA4D496FBFE6E983; Path=/jenkins/; HttpOnly',
'location' => 'http://build.vegicorp.com/;jsessionid=1D5DF6FAF8714B2ACA4D496FBFE6E983',
'date' => 'Mon, 13 May 2013 20:02:35 GMT',
'client-peer' => '10.10.21.96:80',
'content-length' => '0',
'client-date' => 'Mon, 13 May 2013 20:02:35 GMT',
'content-type' => 'text/plain; charset=UTF-8',
'server' => 'Apache-Coyote/1.1'
}, 'HTTP::Headers' ),
'_msg' => 'Moved Temporarily',
'_request' => bless( {
'_content' => 'j_username=buildguy&j_password=swordfish',
'_uri' => bless( do{\(my $o = 'http://build.vegicorp.com/j_acegi_security_check')}, 'URI::http' ),
'_headers' => bless( {
'user-agent' => 'libwww-perl/6.03',
'content-type' => 'application/x-www-form-urlencoded',
'content-length' => 42
}, 'HTTP::Headers' ),
'_method' => 'POST',
'_uri_canonical' => $VAR1->{'_request'}{'_uri'}
}, 'HTTP::Request' )
}, 'HTTP::Response' );
I figure I must be hitting a valid page since I'm getting a 302 code, but my fields might not be correct (or I'm going to the wrong page).
Can anyone help?
My authorization is failing because ...what is the technical term? Oh yeah... "doing it all wrong."
After Googling and getting a lot of unhelpful junk, I, on a lark, decided to see if the Jenkins website might have something on this. And, it did right under a page called Authenticating scripted clients. In fact, they even give a Perl LWP example for a scripted client.
Ha ha, I was trying too hard. It seems that Jenkins will use the basic HTTP authentication mechanism, and I don't have to go through conniptions trying to figure out how their login form works. Apparently, Jenkins is simplifying the basic authentication mechanism for you even if your authentication mechanism is far from basic -- like a good program should do.
So, all I had to do was use the basic authentication mechanism.
my $browser = LWP::UserAgent->new or die qq(Cannot get User Agent);
my $request = HTTP::Request->new;
$request->authorization_basic(USER_ID, PASSWORD);
$request->method("GET");
$request->url("$jenkins_url");
my $response = $browser->request($request);
if ( not $response->is_success ) {
die qq(Something went horribly wrong...);
}
I've seen the redirect when the login is successful -- it sets the session cookie and redirects you to the main page.
Your post might be failing because the UA object isn't persisting the session cookie. Per the documentation, 'The default is to have no cookie_jar, i.e. never automatically add "Cookie" headers to the requests.' Try:
my $ua = LWP::UserAgent->new( cookie_jar => HTTP::Cookies->new() );
To store and reuse your session for the description change post.
(Also, credentials are visible in your header dump, may want to edit... Edit: I'm an idiot, they're in your constants too and're likely fake.)

Soap: Upload binary data

I'm making a Drupal/PHP Module to upload information to Taleo (Talent Management) database using SOAP. This works well with regular data like text and dates, but not with a file.
The manual shows an example of a file attachment:
createAttachment Test Case:
<soapenv:Header/>
<soapenv:Body>
<urn:createAttachment>
<in0>webapi-5616904436472928038</in0>
<in1>15</in1>
<in2>test1.docx</in2>
<in3>test1.docx</in3>
<in4>application/vnd.openxmlformatsofficedocument.
wordprocessingml.document</in4>
<in5>
<!--type: base64Binary-->
<array>JVBERi0xLjQNJeLjz9MNCjYgMCBvYmogPDwvTGluZWFyaX==</array>
</in5>
</urn:createAttachment>
</soapenv:Body>
</soapenv:Envelope>
So I made a PHP file like this:
// Send attachment
$fileName = drupal_get_path('module', 'taleo') . '/test.txt';
$rawFile = fread(fopen($fileName, "r"), filesize($fileName));
$B64File = base64_encode($rawFile);
$params = array(
'in0' => $session,
'in1' => $candidate_id,
'in2' => 'test.txt',
'in3' => 'test.txt',
'in4' => 'text/plain',
'in5' => $B64File
);
$client_taleo->__call('createAttachment', $params);
When I do "echo $B64File" I get this: RmlsZSB1cGxvYWQgd2l0aCBEcnVwYWwgIQ==, so the file is being read correct.
But I always get this error:
ERROR: soapenv:Server.generalException-attBinDataArr is null.
Any ideas?
You forgot to encapsulate the base64-data in array-tags.
<array>JVBERi0xLjQNJeLjz9MNCjYgMCBvYmogPDwvTGluZWFyaX==</array>
Something like this should work:
$params = array(
'in0' => $session,
'in1' => $candidate_id,
'in2' => 'test.txt',
'in3' => 'test.txt',
'in4' => 'text/plain',
'in5' => array('array' => $B64File)
);
It was clear I had to do something with the array-tag, that's for sure.
The answer above deserves an "upvote", so I gave it one. But I found the correct answer myself... After a few seconds of "logic" thinking. :)
'in5' => array('array' => $B64File)