PHP Stream Notification Callback Not Invoked - php

I've been playing around with PHP Streams and have been experimenting by beginning to write the class shown here. The PHP docs are bit lean in this area to say the least.
I'm having a difficult time with getting my stream context to invoke the callback method specified. If I use a function like file_get_contents or fopen to connect to a socket the callback is invoked, but if I use stream_socket_client it does not.
I assume it should because I'm passing the context to stream_socket_client and if I use stream_socket_recvfrom I get the same string back from the socket as fgets would return.
Relevant PHP docs are linked at the end of the post.
class IMAP {
// Connection Parameters
private $host;
private $port;
private $timeout;
// Credentials
private $email;
private $password;
private $client;
private $transcript;
function __construct($connection, $credentials) {
// Set Connection Settings
$this->host = $connection['host'];
$this->port = $connection['port'];
$this->timeout = $connection['timeout'];
// Set Credentials
$this->email = $credentials['email'];
$this->password = $credentials['password'];
// Connect to the IMAP server
$params = array('notification'=>array($this, 'getLine'));
$ctx = stream_context_create();
stream_context_set_params($ctx, $params);
$this->client = stream_socket_client("tcp://$this->host:$this->port",$errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $ctx);
stream_socket_sendto($this->client, "a001 NOOP\r\n");
}
function getLine($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
$args = func_get_args();
var_dump($args);
}
}
$connection = array(
'host' => 'somehost',
'port' => 143,
'timeout' => 10
);
$credentails = array(
'email' => 'someemail',
'password' => 'somepassword'
);
$imap = new IMAP($connection, $credentails);
?>
http://us3.php.net/manual/en/function.stream-context-set-params.php
http://us3.php.net/manual/en/context.params.php
I found this somewhat related PHP bug report too, but it looks like the report was pointless:
http://bugs.php.net/bug.php?id=42387&edit=1

Looks like this isn't supported by the socket streams as of php 5.3.0.
The only function I could find that calls the notifier function (in the C code) is php_stream_notification_notify in main/streams/streams.c. There are also some #defines
#define php_stream_notify_info
#define php_stream_notify_progress
#define php_stream_notify_progress_init
#define php_stream_notify_progress_increment
#define php_stream_notify_file_size
#define php_stream_notify_error
which boil down to a call to php_stream_notification_notify. The ftp wrapper e.g. calls
php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);
in php_ftp_fopen_connect. Same with curl and the http wrapper. But there's no such call for stream_socket_client() or related functions. And the examples at http://php.net/function.stream-notification-callback don't work if you replace the protocol wrapper by a transport like tcp: (or even file:).

Related

PHP RPC Client fails to connect a bitcoin node behind TOR network with modified EasyBitcoin-PHP

I'm trying to connect to a bitcoin node using a modified version of EasyBitcoin-PHP. Here are the modifications I made to make it work over TOR:
I created new fields for the TOR proxy: code-listing 1.1
...
public $response;//was there already
// TOR and other SOCK5 proxies
private $using_proxy; #new
private $proxy_host; #new
private $proxy_port; #new
private $id = 0;//was there already
...
Then I created two new methods: code-listing 1.2
...
/**
* #param string $host
* #param int $port
*/
public function set_proxy($host, $port)
{
$this->proxy_host = $host;
$this->proxy_port = $port;
}
/**
* #param boolean|TRUE $proxy_usage
*/
public function use_proxy($proxy_usage = TRUE)
{
$this->using_proxy = $proxy_usage;
}
...
Finally I added a few lines in the __call() method: code-listing 1.3
// Build the cURL session
$curl = curl_init("{$this->proto}://{$this->host}:{$this->port}/{$this->url}");
$options = array(
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => $this->username . ':' . $this->password,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_HTTPHEADER => array('Content-type: application/json'),
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $request
);
//What Follows is new!!!
if($this->using_proxy){
$options[CURLOPT_PROXY] = $this->proxy_host;
$options[CURLOPT_PROXYPORT] = $this->proxy_port;
$options[CURLOPT_PROXYTYPE] = CURLPROXY_SOCKS5_HOSTNAME;
}
The fields and methods I created, and the extra lines I added in the __call() method are the same I use on my other TOR PHP/cURL projects (whenever I want PHP/cURL to connect to a site via TOR) and they work just fine.
I then installed and run the TOR Expert Bundle (on Windows) and use the following script to get the info from the bitcoin node.
code-listing 2
<?php
require_once('easybitcoin.php');
$bitcoin = new Bitcoin('username','password','6cmgzwu3x57dr6z7.onion');
$bitcoin->set_proxy("127.0.0.1", "9050");
$bitcoin->use_proxy();
if($bitcoin->getinfo()){
} else {
var_dump($bitcoin->status);
var_dump($bitcoin->error);
}
And Here is what I get when running the script on a command prompt:
int(0)
string(50) "Can't complete SOCKS5 connection to 0.0.0.0:0. (5)"
And sometimes I get this:
int(0)
string(50) "Can't complete SOCKS5 connection to 0.0.0.0:0. (1)"
Problem solved. The script is perfect. The problem lay in the way the server was set.

Full Customer Data is not adding up in quickbooks desktop

I am using keith's codeignitor example to add customer data in quickbook desktop from my online pos.
I fetched the data from database and created XML which is perfect and showing all 5109 records. However after running the web service I can only import 165 entries .
Please keep following in mind -
1)There is no XML breakage . The XML is fine
2)Webservice time is set to 500 min so no interruption.
Attached is my code for controller and database file -
Controller -
http://quicktotals.com/quickbooks.txt
SQL -
http://quicktotals.com/phppos_people.sql
<?php
class QuickBooks extends CI_Controller
{
public function __construct()
{
parent::__construct();
// QuickBooks config
$this->load->config('quickbooks');
// Load your other models here...
//$this->load->model('yourmodel1');
//$this->load->model('yourmodel2');
//$this->load->model('yourmodel3');
}
/**
* Generate and return a .QWC Web Connector configuration file
*/
public function config()
{
$name = 'CodeIgniter QuickBooks Demo'; // A name for your server (make it whatever you want)
$descrip = 'CodeIgniter QuickBooks Demo'; // A description of your server
$appurl = 'https://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']) . '/qbwc'; // This *must* be httpS:// (path to your QuickBooks SOAP server)
$appsupport = $appurl; // This *must* be httpS:// and the domain name must match the domain name above
//$dsn = 'mysql://quicktot_admin:hunter2001#localhost/quicktot_pointofsale';
//QuickBooks_Utilities::createUser($dsn,'vijay','test#1234');
$username = $this->config->item('quickbooks_user'); // This is the username you stored in the 'quickbooks_user' table by using QuickBooks_Utilities::createUser()
$fileid = QuickBooks_WebConnector_QWC::fileID(); // Just make this up, but make sure it keeps that format
$ownerid = QuickBooks_WebConnector_QWC::ownerID(); // Just make this up, but make sure it keeps that format
$qbtype = QUICKBOOKS_TYPE_QBFS; // You can leave this as-is unless you're using QuickBooks POS
$readonly = false; // No, we want to write data to QuickBooks
$run_every_n_seconds = 600; // Run every 600 seconds (10 minutes)
// Generate the XML file
$QWC = new QuickBooks_WebConnector_QWC($name, $descrip, $appurl, $appsupport, $username, $fileid, $ownerid, $qbtype, $readonly, $run_every_n_seconds);
$xml = $QWC->generate();
// Send as a file download
header('Content-type: text/xml');
//header('Content-Disposition: attachment; filename="my-quickbooks-wc-file.qwc"');
print($xml);
exit;
}
/**
* SOAP endpoint for the Web Connector to connect to
*/
public function qbwc()
{
$user = $this->config->item('quickbooks_user');
$pass = $this->config->item('quickbooks_pass');
// Memory limit
ini_set('memory_limit', $this->config->item('quickbooks_memorylimit'));
// We need to make sure the correct timezone is set, or some PHP installations will complain
if (function_exists('date_default_timezone_set'))
{
// * MAKE SURE YOU SET THIS TO THE CORRECT TIMEZONE! *
// List of valid timezones is here: http://us3.php.net/manual/en/timezones.php
date_default_timezone_set($this->config->item('quickbooks_tz'));
}
// Map QuickBooks actions to handler functions
$map = array(
QUICKBOOKS_ADD_CUSTOMER => array( array( $this, '_addCustomerRequest' ), array( $this, '_addCustomerResponse' )
),
);
// Catch all errors that QuickBooks throws with this function
$errmap = array(
'*' => array( $this, '_catchallErrors' ),
);
// Call this method whenever the Web Connector connects
$hooks = array(
QuickBooks_WebConnector_Handlers::HOOK_LOGINSUCCESS => array( array( $this, '_loginSuccess' ) ), // Run this function whenever a successful login occurs
);
// An array of callback options
$callback_options = array();
// Logging level
$log_level = $this->config->item('quickbooks_loglevel');
// What SOAP server you're using
$soapserver = QUICKBOOKS_SOAPSERVER_PHP; // The PHP SOAP extension, see: www.php.net/soap
//$soapserver = QUICKBOOKS_SOAPSERVER_BUILTIN; // A pure-PHP SOAP server (no PHP ext/soap extension required, also makes debugging easier)
$soap_options = array( // See http://www.php.net/soap
);
$handler_options = array(
'deny_concurrent_logins' => false,
'deny_reallyfast_logins' => false,
); // See the comments in the QuickBooks/Server/Handlers.php file
$driver_options = array( // See the comments in the QuickBooks/Driver/<YOUR DRIVER HERE>.php file ( i.e. 'Mysql.php', etc. )
'max_log_history' => 32000, // Limit the number of quickbooks_log entries to 1024
'max_queue_history' => 1024, // Limit the number of *successfully processed* quickbooks_queue entries to 64
);
// Build the database connection string
$dsn = 'mysql://' . $this->db->username . ':' . $this->db->password . '#' . $this->db->hostname . '/' . $this->db->database;
// Check to make sure our database is set up
if (!QuickBooks_Utilities::initialized($dsn))
{
//echo "aaa"; die;
// Initialize creates the neccessary database schema for queueing up requests and logging
QuickBooks_Utilities::initialize($dsn);
// This creates a username and password which is used by the Web Connector to authenticate
QuickBooks_Utilities::createUser($dsn, $user, $pass);
}
QuickBooks_Utilities::createUser($dsn, $user, $pass);
//echo "bbb"; die;
// Set up our queue singleton
QuickBooks_WebConnector_Queue_Singleton::initialize($dsn);
// Create a new server and tell it to handle the requests
//__construct($dsn_or_conn, $map, $errmap = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_PHP, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array(), $callback_options = array()
$Server = new QuickBooks_WebConnector_Server($dsn, $map, $errmap, $hooks, $log_level, $soapserver, QUICKBOOKS_WSDL, $soap_options,$handler_options, $driver_options, $callback_options);
$Queue = new QuickBooks_WebConnector_Queue($dsn);
$Queue->enqueue(QUICKBOOKS_ADD_CUSTOMER);
$response = $Server->handle(true, true);
}
/**
* Issue a request to QuickBooks to add a customer
*/
public function _addCustomerRequest($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
// Do something to store the form data here
$this->load->model('Myquickbooks');
$data = $this->Myquickbooks->getCustomerData();
$xml ='';
$xml .= '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="11.0"?>';
$xml .= '<QBXML>';
$xml .= '<QBXMLMsgsRq onError="stopOnError">';
foreach ($data as $dta)
{
$xml .='<CustomerAddRq requestID="' . $requestID . '">
<CustomerAdd>
<Name>'.$dta['first_name'].'_'.$dta['person_id'].'</Name>
<CompanyName>'.$dta['company_name'].'</CompanyName>
<FirstName>'.$dta['first_name'].'</FirstName>
<LastName>'.$dta['last_name'].'</LastName>
<BillAddress>
<Addr1>'.$dta['address_1'].'</Addr1>
<Addr2>'.$dta['address_2'].'</Addr2>
<City>'.$dta['city'].'</City>
<State>'.$dta['state'].'</State>
<PostalCode>'.$dta['zip'].'</PostalCode>
<Country>'.$dta['country'].'</Country>
</BillAddress>
<Phone>'.$dta['phone_number'].'</Phone>
<AltPhone>'.$dta['phone_number'].'</AltPhone>
<Fax>'.$dta['phone_number'].'</Fax>
<Email>'.$dta['email'].'</Email>
<Contact>'.$dta['phone_number'].'</Contact>
</CustomerAdd>
</CustomerAddRq>';
}
$xml .='</QBXMLMsgsRq>';
$xml .='</QBXML>';
$xml = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $xml);
return $xml;
}
/**
* Handle a response from QuickBooks indicating a new customer has been added
*/
public function _addCustomerResponse($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
return true;
}
/**
* Catch and handle errors from QuickBooks
*/
public function _catchallErrors($requestID, $user, $action, $ID, $extra, &$err, $xml, $errnum, $errmsg)
{
if ($action == QUICKBOOKS_ADD_CUSTOMER and $errnum == 3100)
{
return true; // Ignore this error, all is OK - customer already exists
}
// Some other error occurred, stop processing
return false;
}
/**
* Whenever the Web Connector connects, do something (e.g. queue some stuff up if you want to)
*/
public function _loginSuccess($requestID, $user, $hook, &$err, $hook_data, $callback_config)
{
return true;
}
}
?>
Your code has numerous problems, and for anyone to help you, you're going to have to fix the problems first. Details:
You're using a 13-year-old version of qbXML. Why? You should use the highest version your QuickBooks supports:
<?qbxml version="2.0"?>
You're bundling multiple customers up into a single qbXML request. This is bad -- it destroys any ability you have to actually track and report on errors. Do not do this. Each customer add request should be it's own individual request, with it's own specific entry in the queue. Refer to the documentation.
foreach ($data as $dta)
You are specifically bypassing all error checking and error reporting. Why? This makes it impossible to know if something went wrong or not.
return true;
Fix your code, and then re-post the actual code instead of a .txt file.

How do I verify that Zend\Mail has an open IMAP Stream?

My host company will not enable the IMAP extension on our web server so I cannot utilize IMAP functions. I am trying to use Zend\Mail as a replacement, but I have been unable to map out a one to one ratio of native IMAP functions with Zend\Mail functions.
Part of my scripts checks to see if an open IMAP stream exists and if it does, to close it and then reopen it. Here's how my php script does it using IMAP functions:
function open($box='INBOX') {
if ($this->mbox)
$this->close();
$args = array($this->srvstr.$this->mailbox_encode($box),
$this->getUsername(), $this->getPassword());
// Disable Kerberos and NTLM authentication if it happens to be
// supported locally or remotely
if (version_compare(PHP_VERSION, '5.3.2', '>='))
$args += array(NULL, 0, array(
'DISABLE_AUTHENTICATOR' => array('GSSAPI', 'NTLM')));
$this->mbox = call_user_func_array('imap_open', $args);
return $this->mbox;
}
function close($flag=CL_EXPUNGE) {
imap_close($this->mbox, $flag);
}
How do I do the same thing with Zend\Mail v2.2?
I have tried using this code, but it errors out with Status 500 and just returns a blank screen:
function open($box='INBOX') {
if ($mail)
$this->close();
$args = array($this->srvstr.$this->mailbox_encode($box),
$this->getUsername(), $this->getPassword());
// Disable Kerberos and NTLM authentication if it happens to be
// supported locally or remotely
if (version_compare(PHP_VERSION, '5.3.2', '>='))
$args += array(NULL, 0, array(
'DISABLE_AUTHENTICATOR' => array('GSSAPI', 'NTLM')));
$this->getProtocol();
$protocol = $this->ht['protocol'];
if ($protocol=="Imap")
$mail = new Zend\Mail\Storage\Imap($args);
else
$mail = new Zend\Mail\Storage\Pop3($args);
return $mail;
}
function close() {
$mail->close();
$mail='';
}
Your $args are wrong. That's how the params are written:
$mail = new Zend_Mail_Storage_Pop3(array('host' => 'localhost',
'user' => 'test',
'password' => 'test'));

JAXL based chat client. Need help connecting to Gtalk or other server to test

I wish to send a message to XMPP based chat servers using php.
I am using JAXL, which seems the best (of limited) options for pure PHP server based chat.
However, I have yet to establish any connect, let alone send a message.
I am having a hard time working out if the problem is my code, my server (which is shared server, but has Cpanel, and a very helpfull host), or my settings.
The code I am using to try to connect to GTalk is;
$client = new JAXL(array(
'jid' => 'name#gmail.com',
'pass' => 'password',
'host'=> 'talk.google.com',
'port'=> 5222,
'domain'=> 'gmail.com', //unsure if this is the right setting.
'force_tls' => true,
'auth_type' => #$argv[3] ? $argv[3] : 'PLAIN',
));
//
// required XEP's
//
$client->require_xep(array(
'0199' // XMPP Ping
));
//
// add necessary event callbacks here
//
$client->add_cb('on_auth_success', function() {
global $client;
_info("got on_auth_success cb, jid ".$client->full_jid->to_string());
// fetch roster list
$client->get_roster();
// fetch vcard
$client->get_vcard();
// set status
$client->set_status("available!", "dnd", 10);
});
$client->add_cb('on_connect_error', function() {
echo 'Connect Error';
});
$client->add_cb('on_auth_failure', function() {
echo 'Auth Error';
});
$client->add_cb('on_auth_success', function() {
global $client;
echo 'connected';
$client->send_chat_msg('test2#domain.com', 'webtest');
$client->shutdown();
});
//
// finally start configured xmpp stream
//
$client->start(array(
'--with-debug-shell' => true,
'--with-unix-sock' => true
));
echo "done\n";
Triggering the php (from a browser) then results in the server getting stuck. (no "done" message, just constant loading till a timeout from the browser)
The server logs show;
strict mode enabled, adding exception handlers. Set 'strict'=>TRUE inside JAXL config to disable this[0m
error handler called with 8, Undefined index: priv_dir,
And then lots of;
unable to connect tcp://talk.google.com:5222 with error no: 110, error str: Connection timed out
So I would appreciate help with any of the following;
Any specific problems with my code
Any issues with the gtalk connection settings at the start
Alternative recommendations to investigate this issue.
Or any general advice from people that have successfully used JAXL.
Thanks,
Thomas Wrobel
Ok problem might be TCP port is closed for your hosting , try to open it with your hosting first, also try to run your code locally to see if it's work fine .
Some people reported the issue was fixed by override method connect from file XMLStream.php by this one
/**
* Connect to XMPP Host
*
* #param integer $timeout
* #param boolean $persistent
* #param boolean $sendinit
*/
public function connect($timeout = 30, $persistent = false, $sendinit = true) {
$this->sent_disconnect = false;
$starttime = time();
do {
$this->disconnected = false;
$this->sent_disconnect = false;
if($persistent) {
$conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
} else {
$conflag = STREAM_CLIENT_CONNECT;
}
$conntype = 'tcp';
if($this->use_ssl) $conntype = 'ssl';
$this->log->log("Connecting to $conntype://{$this->host}:{$this->port}");
try {
$this->socket = #stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag);
} catch (Exception $e) {
throw new XMPPHP_Exception($e->getMessage());
}
if(!$this->socket) {
$this->log->log("Could not connect.", XMPPHP_Log::LEVEL_ERROR);
$this->disconnected = true;
# Take it easy for a few seconds
sleep(min($timeout, 5));
}
} while (!$this->socket/* && (time() - $starttime) < $timeout*/);
if ($this->socket) {
stream_set_blocking($this->socket, 0);
if($sendinit) $this->send($this->stream_start);
} else {
throw new XMPPHP_Exception("Could not connect before timeout.");
}
}

SoapClient set custom HTTP Header

I am doing some work writing a PHP-based SOAP client application that uses the SOAP libraries native to PHP5. I need to send a an HTTP cookie and an additional HTTP header as part of the request. The cookie part is no problem:
Code:
$client = new SoapClient($webServiceURI, array("exceptions" => 0, "trace" => 1, "encoding" => $phpInternalEncoding));
$client->__setCookie($kkey, $vvalue);
My problem is the HTTP header. I was hoping there would have been a function named
__setHeader
or
__setHttpHeader
in the SOAP libraries. But no such luck.
Anyone else dealt with this? Is there a workaround? Would a different SOAP library be easier to work with? Thanks.
(I found this unanswerd question here http://www.phpfreaks.com/forums/index.php?topic=125387.0, I copied it b/c i've the same issue)
Try setting a stream context for the soap client:
$client = new SoapClient($webServiceURI, array(
"exceptions" => 0,
"trace" => 1,
"encoding" => $phpInternalEncoding,
'stream_context' => stream_context_create(array(
'http' => array(
'header' => 'SomeCustomHeader: value'
),
)),
));
This answer is the proper way to do it in PHP 5.3+ SoapClient set custom HTTP Header
However, PHP 5.2 does not take all of the values from the stream context into consideration. To get around this, you can make a subclass that handles it for you (in a hacky way, but it works).
class SoapClientBackport extends SoapClient {
public function __construct($wsdl, $options = array()){
if($options['stream_context'] && is_resource($options['stream_context'])){
$stream_context_options = stream_context_get_options($options['stream_context']);
$user_agent = (isset($stream_context_options['http']['user_agent']) ? $stream_context_options['http']['user_agent'] : "PHP-SOAP/" . PHP_VERSION) . "\r\n";
if(isset($stream_context_options['http']['header'])){
if(is_string($stream_context_options['http']['header'])){
$user_agent .= $stream_context_options['http']['header'] . "\r\n";
}
else if(is_array($stream_context_options['http']['header'])){
$user_agent .= implode("\r\n", $stream_context_options['http']['header']);
}
}
$options['user_agent'] = $user_agent;
}
parent::__construct($wsdl, $options);
}
}
I ran into a situation where I had to provide a hash of all the text of the soap request in the HTTP header of the request for authentication purposes. I accomplished this by subclassing SoapClient and using the stream_context option to set the header:
class AuthenticatingSoapClient extends SoapClient {
private $secretKey = "secretKeyString";
private $context;
function __construct($wsdl, $options) {
// Create the stream_context and add it to the options
$this->context = stream_context_create();
$options = array_merge($options, array('stream_context' => $this->context));
parent::SoapClient($wsdl, $options);
}
// Override doRequest to calculate the authentication hash from the $request.
function __doRequest($request, $location, $action, $version, $one_way = 0) {
// Grab all the text from the request.
$xml = simplexml_load_string($request);
$innerText = dom_import_simplexml($xml)->textContent;
// Calculate the authentication hash.
$encodedText = utf8_encode($innerText);
$authHash = base64_encode(hash_hmac("sha256", $encodedText, $this->secretKey, true));
// Set the HTTP headers.
stream_context_set_option($this->context, array('http' => array('header' => 'AuthHash: '. $authHash)));
return (parent::__doRequest($request, $location, $action, $version, $one_way));
}
}
Maybe someone searching will find this useful.
its easy to implement in nuSoap:
NUSOAP.PHP
add to class nusoap_base:
var additionalHeaders = array();
then goto function send of the same class
and add
foreach ($this->additionalHeaders as $key => $value) {
$http->setHeader($key, $value);
}
somewhere around (just before)
$http->setSOAPAction($soapaction); (line 7596)
now you can easy set headers:
$soapClient = new nusoap_client('wsdl adress','wsdl');
$soapClient->additionalHeaders = array('key'=>'val','key2'=>'val');
The SoapClient::__soapCall method has an $input_headers argument, which takes an array of SoapHeaders.
You could also use Zend Framework's SOAP client, which provides an addSoapInputHeader convenience method.

Categories