migrated http proxy and ws client

This commit is contained in:
JT Smith 2005-01-03 03:24:43 +00:00
parent 15c48e14d0
commit 7416bda04f
14 changed files with 282 additions and 1306 deletions

View file

@ -0,0 +1,345 @@
package WebGUI::Asset::Wobject::HttpProxy;
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2004 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#-------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
use strict;
use URI;
use LWP;
use HTTP::Cookies;
use HTTP::Request::Common;
use HTML::Entities;
use WebGUI::HTTP;
use WebGUI::International;
use WebGUI::Privilege;
use WebGUI::Session;
use WebGUI::Storage;
use WebGUI::Asset::Wobject;
use WebGUI::Asset::Wobject::HttpProxy::Parse;
use WebGUI::Cache;
our @ISA = qw(WebGUI::Asset::Wobject);
#-------------------------------------------------------------------
sub definition {
my $class = shift;
my $definition = shift;
push(@{$definition}, {
tableName=>'HttpProxy',
className=>'WebGUI::Asset::Wobject::HttpProxy',
properties=>{
proxiedUrl=>{
fieldType=>"url",
defaultValue=>'http://'
},
timeout=>{
fieldType=>"selectList",
defaultValue=>30
},
removeStyle=>{
fieldType=>"yesNo",
defaultValue=>1
},
filterHtml=>{
fieldType=>"filterContent",
defaultValue=>"javascript"
},
followExternal=>{
fieldType=>"yesNo",
defaultValue=>1
},
rewriteUrls=>{
fieldType=>"yesNo",
defaultValue=>1
},
followRedirect=>{
fieldType=>"yesNo",
defaultValue=>0
},
searchFor=>{
fieldType=>"text",
defaultValue=>undef
},
stopAt=>{
fieldType=>"text",
defaultValue=>undef
},
}
});
return $class->SUPER::definition($definition);
}
#-------------------------------------------------------------------
sub getCookieJar {
my $self = shift;
my $storage;
unless ($self->get("cookieJarStorageId")) {
$storage = WebGUI::Storage->create;
$self->update({cookieJarStorageId=>$storage->getId});
} else {
$storage = WebGUI::Storage->get($self->get("cookieJarStorageId"));
}
return $storage;
}
#-------------------------------------------------------------------
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm();
my %hash;
tie %hash, 'Tie::IxHash';
%hash=(5=>5,10=>10,20=>20,30=>30,60=>60);
$tabform->getTab("properties")->url(
-name=>"proxiedUrl",
-label=>WebGUI::International::get(1,"HttpProxy"),
-value=>$self->getValue("proxiedUrl")
);
$tabform->getTab("security")->yesNo(
-name=>"followExternal",
-label=>WebGUI::International::get(5,"HttpProxy"),
-value=>$self->getValue("followExternal")
);
$tabform->getTab("properties")->yesNo(
-name=>"followRedirect",
-label=>WebGUI::International::get(8,"HttpProxy"),
-value=>$self->getValue("followRedirect")
);
$tabform->getTab("properties")->yesNo(
-name=>"rewriteUrls",
-label=>WebGUI::International::get(12,"HttpProxy"),
-value=>$self->getValue("rewriteUrls")
);
$tabform->getTab("display")->yesNo(
-name=>"removeStyle",
-label=>WebGUI::International::get(6,"HttpProxy"),
-value=>$self->getValue("removeStyle")
);
$tabform->getTab("display")->filterContent(
-name=>"filterHtml",
-value=>$self->getValue("filterHtml")
);
$tabform->getTab("properties")->selectList(
-name=>"timeout",
-options=>\%hash,
-label=>WebGUI::International::get(4,"HttpProxy"),
-value=>[$self->getValue("timeout")]
);
$tabform->getTab("display")->text(
-name=>"searchFor",
-label=>WebGUI::International::get(13,"HttpProxy"),
-value=>$self->getValue("searchFor")
);
$tabform->getTab("display")->text(
-name=>"stopAt",
-label=>WebGUI::International::get(14,"HttpProxy"),
-value=>$self->getValue("stopAt")
);
return $tabform;
}
#-------------------------------------------------------------------
sub getIcon {
my $self = shift;
my $small = shift;
return $session{config}{extrasURL}.'/assets/small/httpProxy.gif' if ($small);
return $session{config}{extrasURL}.'/assets/httpProxy.gif';
}
#-------------------------------------------------------------------
sub getName {
return WebGUI::International::get(3,"HttpProxy");
}
#-------------------------------------------------------------------
sub getUiLevel {
return 5;
}
#-------------------------------------------------------------------
sub purge {
my $self = shift;
$self->getCookieJar->delete;
$self->SUPER::purge;
}
#-------------------------------------------------------------------
sub view {
my $self = shift;
my $cookiebox = WebGUI::URL::escape($session{var}{sessionId});
$cookiebox =~ s/[^A-Za-z0-9\-\.\_]//g; #removes all funky characters
$cookiebox .= '.cookie';
my $jar = HTTP::Cookies->new(File => $self->getCookieJar->getPath($cookiebox), AutoSave => 1, Ignore_Discard => 1);
my (%var, %formdata, @formUpload, $redirect, $response, $header, $userAgent, $proxiedUrl, $request, $ttl);
if($session{form}{func}!~/editSave/i) {
$proxiedUrl = $session{form}{FormAction} || $session{form}{proxiedUrl} || $self->get("proxiedUrl") ;
} else {
$proxiedUrl = $self->get("proxiedUrl");
$session{env}{REQUEST_METHOD}='GET';
}
$redirect=0;
return $self->processTemplate({},"HttpProxy",$self->get("templateId")) unless ($proxiedUrl ne "");
my $cachedContent = WebGUI::Cache->new($proxiedUrl,"URL");
my $cachedHeader = WebGUI::Cache->new($proxiedUrl,"HEADER");
$var{header} = $cachedHeader->get;
$var{content} = $cachedContent->get;
unless ($var{content} && $session{env}{REQUEST_METHOD}=~/GET/i) {
$redirect=0;
until($redirect == 5) { # We follow max 5 redirects to prevent bouncing/flapping
$userAgent = new LWP::UserAgent;
$userAgent->agent($session{env}{HTTP_USER_AGENT});
$userAgent->timeout($self->get("timeout"));
$userAgent->env_proxy;
$proxiedUrl = URI->new($proxiedUrl);
#my $allowed_url = URI->new($self->get('proxiedUrl'))->abs;;
#if ($self->get("followExternal")==0 && $proxiedUrl !~ /\Q$allowed_url/i) {
if ($self->get("followExternal")==0 &&
(URI->new($self->get('proxiedUrl'))->host) ne (URI->new($proxiedUrl)->host) ) {
$var{header} = "text/html";
return "<h1>You are not allowed to leave ".$self->get("proxiedUrl")."</h1>";
}
$header = new HTTP::Headers;
$header->referer($self->get("proxiedUrl")); # To get around referrer blocking
if($session{env}{REQUEST_METHOD}=~/GET/i || $redirect != 0) { # request_method is also GET after a redirection. Just to make sure we're
# not posting the same data over and over again.
if($redirect == 0) {
foreach my $input_name (keys %{$session{form}}) {
next if ($input_name !~ /^HttpProxy_/); # Skip non proxied form var's
$input_name =~ s/^HttpProxy_//;
$proxiedUrl=WebGUI::URL::append($proxiedUrl,"$input_name=$session{form}{'HttpProxy_'.$input_name}");
}
}
$request = HTTP::Request->new(GET => $proxiedUrl, $header) || return "wrong url"; # Create GET request
} else { # It's a POST
my $contentType = 'application/x-www-form-urlencoded'; # default Content Type header
# Create a %formdata hash to pass key/value pairs to the POST request
foreach my $input_name (keys %{$session{form}}) {
next if ($input_name !~ /^HttpProxy_/); # Skip non proxied form var's
$input_name =~ s/^HttpProxy_//;
my $uploadFile = $session{cgi}->tmpFileName($session{form}{'HttpProxy_'.$input_name});
if(-r $uploadFile) { # Found uploaded file
@formUpload=($uploadFile, qq/$session{form}{'HttpProxy_'.$input_name}/);
$formdata{$input_name}=\@formUpload;
$contentType = 'form-data'; # Different Content Type header for file upload
} else {
$formdata{$input_name}=qq/$session{form}{'HttpProxy_'.$input_name}/;
}
}
# Create POST request
$request = HTTP::Request::Common::POST($proxiedUrl, \%formdata, Content_Type => $contentType);
}
$jar->add_cookie_header($request);
$response = $userAgent->simple_request($request);
$jar->extract_cookies($response);
if ($response->is_redirect) { # redirected by http header
$proxiedUrl = URI::URL::url($response->header("Location"))->abs($proxiedUrl);;
$redirect++;
} elsif ($response->content_type eq "text/html" && $response->content =~
/<meta[^>]+refresh[^>]+content[^>]*url=([^\s'"<>]+)/gis) {
# redirection through meta refresh
my $refreshUrl = $1;
if($refreshUrl=~ /^http/gis) { #Refresh value is absolute
$proxiedUrl=$refreshUrl;
} else { # Refresh value is relative
$proxiedUrl =~ s/[^\/\\]*$//; #chop off everything after / in $proxiedURl
$proxiedUrl .= URI::URL::url($refreshUrl)->rel($proxiedUrl); # add relative path
}
$redirect++;
} else {
$redirect = 5; #No redirection found. Leave loop.
}
$redirect=5 if (not $self->get("followRedirect")); # No redirection. Overruled by setting
}
if($response->is_success) {
$var{content} = $response->content;
$var{header} = $response->content_type;
if($response->content_type eq "text/html" ||
($response->content_type eq "" && $var{content}=~/<html/gis)) {
$var{"search.for"} = $self->getValue("searchFor");
$var{"stop.at"} = $self->getValue("stopAt");
if ($var{"search.for"}) {
$var{content} =~ /^(.*?)\Q$var{"search.for"}\E(.*)$/gis;
$var{"content.leading"} = $1 || $var{content};
$var{content} = $2;
}
if ($var{"stop.at"}) {
$var{content} =~ /(.*?)\Q$var{"stop.at"}\E(.*)$/gis;
$var{content} = $1 || $var{content};
$var{"content.trailing"} = $2;
}
my $p = WebGUI::Asset::Wobject::HttpProxy::Parse->new($proxiedUrl, $var{content}, $self->getId,$self->get("rewriteUrls"));
$var{content} = $p->filter; # Rewrite content. (let forms/links return to us).
$p->DESTROY;
if ($var{content} =~ /<frame/gis) {
$var{header} = "text/html";
$var{content} = "<h1>HttpProxy: Can't display frames</h1>
Try fetching it directly <a href='$proxiedUrl'>here.</a>";
} else {
$var{content} =~ s/\<style.*?\/style\>//isg if ($self->get("removeStyle"));
$var{content} = WebGUI::HTML::cleanSegment($var{content});
$var{content} = WebGUI::HTML::filter($var{content}, $self->get("filterHtml"));
}
}
} else { # Fetching page failed...
$var{header} = "text/html";
$var{content} = "<b>Getting <a href='$proxiedUrl'>$proxiedUrl</a> failed</b>".
"<p><i>GET status line: ".$response->status_line."</i>";
}
if ($session{user}{userId} eq '1') {
$ttl = $session{page}{cacheTimeoutVisitor};
} else {
$ttl = $session{page}{cacheTimeout};
}
$cachedContent->set($var{content},$ttl);
$cachedHeader->set($var{header},$ttl);
}
if($var{header} ne "text/html") {
WebGUI::HTTP::setMimeType($var{header});
return $var{content};
} else {
return $self->processTemplate(\%var,"HttpProxy",$self->get("templateId"));
}
}
#-------------------------------------------------------------------
sub www_edit {
my $self = shift;
return $self->getAdminConsole->render(WebGUI::Privilege::insufficient()) unless $self->canEdit;
$self->getAdminConsole->setHelp("http proxy add/edit");
return $self->getAdminConsole->render($self->getEditForm->print,WebGUI::International::get("2","HttpProxy"));
}
1;

View file

@ -0,0 +1,158 @@
package WebGUI::Asset::Wobject::HttpProxy::Parse;
# -------------------------------------------------------------------
# WebGUI is Copyright 2001-2004 Plain Black Corporation.
# -------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
# -------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
# -------------------------------------------------------------------
use HTML::Parser;
use HTML::Entities;
use URI::URL;
use WebGUI::URL;
use vars qw(@ISA);
@ISA = qw(HTML::Parser);
my %linkElements = # from HTML::Element.pm
(
body => 'background',
base => 'href',
a => 'href',
img => [qw(src lowsrc usemap)], # lowsrc is a Netscape invention
form => 'action',
input => 'src',
'link' => 'href', # need quoting since link is a perl builtin
frame => 'src',
iframe => 'src',
applet => 'codebase',
area => 'href',
script => 'src',
iframe => 'src',
);
my %tag_attr;
for my $tag (keys %linkElements) {
my $tagval = $linkElements{$tag};
for my $attr (ref $tagval ? @$tagval : $tagval) {
$tag_attr{"$tag $attr"}++;
}
}
sub new {
my $pack = shift;
my $self = $pack->SUPER::new();
$self->{Url} = shift;
$self->{Content} = shift;
$self->{assetId} = shift;
$self->{rewriteUrls} = shift;
$self->{Filtered} ="";
$self->{FormAction} = "";
$self->{FormActionIsDefined} = 0;
$self->{recurseCheck} = 0;
$self;
}
sub filter {
my $self=shift;
$self->parse($self->{Content}); # Make paths absolute and let them return to us
$self->eof;
return "<p>Error: HttpProxy can't recursively proxy its own content.</p>" if ($self->{recurseCheck});
return $self->{Filtered};
}
## some items stolen from HTML::Filter
sub output {
$_[0]->{Filtered} .= $_[1];
}
sub declaration {
$_[0]->output("<!$_[1]>")
}
sub comment {
$_[0]->output("<!--$_[1]-->")
}
sub text {
$_[0]->output($_[1])
}
sub end {
$_[0]->output("</$_[1]>")
}
sub start {
my $self = shift;
my ($tag, $attr, $attrseq, $origtext) = @_;
# Check on the div class and div id attributes to see if we're proxying ourself.
if($tag eq "div" && $attr->{'class'} eq 'wobjectHttpProxy' && $attr->{'id'} eq ('assetId'.$self->{assetId})) {
$self->{recurseCheck} = 1;
}
$self->output("<$tag");
for (keys %$attr) {
if ($_ eq '/') {
$self->output('/');
next;
}
$self->output(" $_=\"");
my $val = $attr->{$_};
if ((lc($tag) eq "input" || lc($tag) eq "textarea" || lc($tag) eq "select")
&& (lc($_) eq "name" || lc($_) eq "submit")) { # Rewrite input type names
$val = 'HttpProxy_' . $val;
}
if (lc($tag) eq "form" && not $self->{FormActionIsDefined}) {
$self->{FormAction} = $self->{Url};
}
if ($tag_attr{"$tag $_"}) { # needs rewrite
if ($val =~ /^\?/) { # link that starts with ? i.e. <a href="?var=hello">
my @urlBase = split(/\?/, $self->{Url});
$val = URI::URL::url($urlBase[0] . $val);
# catch internal # anchors
} elsif ($val =~ /^#/){
$val = URI::URL::url($val);
} else {
$val = URI::URL::url($val)->abs($self->{Url},1); # make absolute
}
if ($val->scheme eq "http") {
if ($self->{rewriteUrls} && lc($tag) ne "iframe") {
if (lc($tag) eq "form" && lc($_) eq "action") { # Found FORM ACTION
$self->{FormActionIsDefined}=1;
$self->{FormAction} = $val; # set FormAction to include hidden field later
$val = WebGUI::URL::page; # Form Action returns to us
} else {
$val =~ s/\n//g; # Bugfix 757068
$val = WebGUI::URL::page('proxiedUrl='.WebGUI::URL::escape($val).'&func=view'); # return to us
}
}
}
}
$self->output($val.'"');
}
$self->output(">");
if ($self->{FormAction} ne "") {
$self->output('<input type="hidden" name="FormAction" value="'.$self->{FormAction}.'">');
$self->output('<input type="hidden" name="func" value="view">');
$self->{FormAction} = '';
$self->{FormActionIsDefined}=0;
}
}
1;

View file

@ -0,0 +1,530 @@
package WebGUI::Asset::Wobject::WSClient;
use strict;
use Data::Dumper;
use Digest::MD5;
use SOAP::Lite;
use Storable;
use WebGUI::Cache;
use WebGUI::ErrorHandler;
use WebGUI::International;
use WebGUI::Macro;
use WebGUI::Paginator;
use WebGUI::Privilege;
use WebGUI::Session;
use WebGUI::Asset::Wobject;
my ($hasUnblessAcme, $hasUnblessData, $hasUtf8, $utf8FieldType);
# we really would like to be able to unbless references and strip utf8 data,
# but that requires non-standard and possibly difficult to install modules
BEGIN {
# check for Data::Structure::Util, which requires perl 5.8.0 :-P
eval { require Data::Structure::Util; };
if ($@) {
$utf8FieldType = 'hidden';
# try Acme::Damn as partial fallback
eval { require Acme::Damn; };
$hasUnblessAcme = 1 if !$@;
} else {
$utf8FieldType = 'yesNo';
$hasUnblessData = 1;
$hasUtf8 = 1 if $] >= 5.008;
}
}
our @ISA = qw(WebGUI::Asset::Wobject);
#-------------------------------------------------------------------
sub definition {
my $class = shift;
my $definition = shift;
my $httpHeaderFieldType;
if ($session{'config'}{'soapHttpHeaderOverride'}) {
$httpHeaderFieldType = 'text';
} else {
$httpHeaderFieldType = 'hidden';
}
push(@{$definition}, {
tableName=>'WSClient',
className=>'WebGUI::Asset::Wobject::WSClient',
properties=>{
callMethod => {
fieldType => 'textarea',
defaultValue=>undef
},
debugMode => {
fieldType => 'integer',
defaultValue => 0,
},
execute_by_default => {
fieldType => 'yesNo',
defaultValue => 1,
},
paginateAfter => {
defaultValue => 100,
fieldType=>"integer"
},
paginateVar => {
fieldType => 'text',
defaultValue=>undef
},
params => {
fieldType => 'textarea',
},
preprocessMacros => {
fieldType => 'integer',
defaultValue => 0,
},
proxy => {
fieldType => 'text',
defaultValue => $session{'config'}{'soapproxy'},
},
uri => {
fieldType => 'text',
defaultValue => $session{'config'}{'soapuri'}
},
decodeUtf8 => {
fieldType => $utf8FieldType,
defaultValue => 0,
},
httpHeader => {
fieldType => $httpHeaderFieldType,
defaultValue=>undef
},
cacheTTL => {
fieldType => 'integer',
defaultValue => 60,
},
sharedCache => {
fieldType => 'integer',
defaultValue => '0',
}
}
});
return $class->SUPER::definition($definition);
}
#-------------------------------------------------------------------
sub getIcon {
my $self = shift;
my $small = shift;
return $session{config}{extrasURL}.'/assets/small/web_services.gif' if ($small);
return $session{config}{extrasURL}.'/assets/web_services.gif';
}
#-------------------------------------------------------------------
sub getName {
return WebGUI::International::get(1, "WSClient");
}
#-------------------------------------------------------------------
sub getUiLevel {
return 9;
}
#-------------------------------------------------------------------
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm();
$tabform->getTab("display")->template(
-name => 'templateId',
-value => $self->getValue('templateId'),
-namespace => "WSClient",
);
$tabform->getTab("display")->yesNo (
-name => 'preprocessMacros',
-label => WebGUI::International::get(8, "WSClient"),
-value => $self->get('preprocessMacros'),
);
$tabform->getTab("display")->integer(
-name => 'paginateAfter',
-label => WebGUI::International::get(13, "WSClient"),
-value => $self->getValue("paginateAfter")
);
$tabform->getTab("display")->text (
-name => 'paginateVar',
-label => WebGUI::International::get(14, "WSClient"),
-value => $self->get('paginateVar'),
);
$tabform->getTab("properties")->text (
-name => 'uri',
-label => WebGUI::International::get(2, "WSClient"),
-value => $self->get('uri'),
);
$tabform->getTab("properties")->text (
-name => 'proxy',
-label => WebGUI::International::get(3, "WSClient"),
-value => $self->get('proxy'),
);
$tabform->getTab("properties")->text (
-name => 'callMethod',
-label => WebGUI::International::get(4, "WSClient"),
-value => $self->get('callMethod'),
);
$tabform->getTab("properties")->textarea (
-name => 'params',
-label => WebGUI::International::get(5, "WSClient"),
-value => $self->get('params'),
);
if ($session{'config'}{'soapHttpHeaderOverride'}) {
$tabform->getTab("properties")->text (
-name => 'httpHeader',
-label => WebGUI::International::get(16, "WSClient"),
-value => $self->get('httpHeader'),
);
} else {
$tabform->getTab("properties")->hidden (
-name => 'httpHeader',
-label => WebGUI::International::get(16, "WSClient"),
-value => $self->get('httpHeader'),
);
}
$tabform->getTab("properties")->yesNo (
-name => 'execute_by_default',
-label => WebGUI::International::get(11, "WSClient"),
-value => $self->get('execute_by_default'),
);
$tabform->getTab("properties")->yesNo (
-name => 'debugMode',
-label => WebGUI::International::get(9, "WSClient"),
-value => $self->get('debugMode'),
);
if ($utf8FieldType eq 'yesNo') {
$tabform->getTab("properties")->yesNo (
-name => 'decodeUtf8',
-label => WebGUI::International::get(15, "WSClient"),
-value => $self->get('decodeUtf8'),
);
} else {
$tabform->getTab("properties")->hidden (
-name => 'decodeUtf8',
-label => WebGUI::International::get(15, "WSClient"),
-value => $self->get('decodeUtf8'),
);
}
my $cacheopts = {
0 => WebGUI::International::get(29, "WSClient"),
1 => WebGUI::International::get(19, "WSClient"),
};
$tabform->getTab("properties")->radioList (
-name => 'sharedCache',
-options => $cacheopts,
-label => WebGUI::International::get(28, "WSClient"),
-value => $self->get('sharedCache'),
);
$tabform->getTab("properties")->text (
-name => 'cacheTTL',
-label => WebGUI::International::get(27, "WSClient"),
-value => $self->get('cacheTTL'),
);
return $tabform;
}
#-------------------------------------------------------------------
sub www_edit {
my $self = shift;
return $self->getAdminConsole->render(WebGUI::Privilege::insufficient()) unless $self->canEdit;
$self->getAdminConsole->setHelp("web services client add/edit");
return $self->getAdminConsole->render($self->getEditForm->print,WebGUI::International::get("20","WSClient"));
}
#-------------------------------------------------------------------
sub view {
my ( $arr_ref, # temp var holding params
$cache_key, # unique cache identifier
$cache, # cache object
$call, # SOAP method call
$p, # pagination object
$param_str, # raw SOAP params before parsing
@params, # params to soap method call
@result, # SOAP result reference
$soap, # SOAP object
@targetWobjects, # list of non-default wobjects to exec
$url, # current page url
%var # HTML::Template variables
);
my $self= shift;
# this page, with important params
$url = $self->getUrl("func=view");
# snag our SOAP call and preprocess if needed
if ($self->get('preprocessMacros')) {
$call = WebGUI::Macro::process($self->get("callMethod"));
$param_str = WebGUI::Macro::process($self->get("params"));
} else {
$call = $self->get('callMethod');
$param_str = $self->get('params');
}
# see if we can shortcircuit this whole process
if ((ref $session{'form'}{'disableWobjects'} && grep /^$call$/,
@{$session{'form'}{'disableWobjects'}}) ||
($session{'form'}{'disableWobjects'} && grep /^$call$/,
$session{'form'}{'disableWobjects'})) {
WebGUI::ErrorHandler::warn("disabling soap call $call");
$var{'disableWobject'} = 1;
return $self->processTemplate(\%var,"WSClient",$self->get("templateId"));
}
# advanced use, if you want to pass SOAP results to a single, particular
# wobject on a page
if (ref $session{'form'}{'targetWobjects'}) {
@targetWobjects = @{$session{'form'}{'targetWobjects'}};
} else {
push @targetWobjects, $session{'form'}{'targetWobjects'};
}
# check to see if this exact query has already been cached, using either
# a cache specific to this session, or a shared global cache
$cache_key = $self->get('sharedCache')
? Digest::MD5::md5_hex($call, $param_str)
: Digest::MD5::md5_hex($call, $param_str, $session{'var'}{'sessionId'});
WebGUI::ErrorHandler::warn(($self->get('sharedCache')?'shared':'session')
. " cache_key=$cache_key md5_hex($call, $param_str)");
$cache = WebGUI::Cache->new($cache_key,
WebGUI::International::get(4, "WSClient"));
# passing a form param WSClient_skipCache lets us ignore even good caches
if (!$session{'form'}{'WSClient_skipCache'}) {
@result = Storable::thaw($cache->get);
}
# prep SOAP unless we found cached data
if (!$result[0]) {
# this is the magic right here. We're allowing perl to parse out
# the ArrayOfHash info so that we don't have to regex it ourselves
# FIXME: we need to protect against eval-ing unknown strings
# the solution is to normalize all params to another table
eval "\$arr_ref = [$param_str];";
eval { @params = @$arr_ref; };
WebGUI::ErrorHandler::warn(WebGUI::International::get(22, "WSClient")) if $@ && $self->get('debugMode');
if ($self->get('execute_by_default') || grep /^$call$/,
@targetWobjects) {
# there's certainly a better pattern match than this to check for
# valid looking uri, but I haven't hunted for the relevant RFC yet
if ($self->get("uri") =~ m!.+/.+!) {
WebGUI::ErrorHandler::warn('uri=' . $self->get("uri"))
if $self->get('debugMode');
$soap = $self->_instantiate_soap;
} else {
WebGUI::ErrorHandler::warn(WebGUI::International::get(23, "WSClient")) if $self->get('debugMode');
}
}
}
# continue if our SOAP connection was successful or we have cached data
if (defined $soap || $result[0]) {
if (!$result[0]) {
eval {
# here's the rub. `perldoc SOAP::Lite` says, "the method in
# question will return the current object (if not stated
# otherwise)". That "not stated otherwise" bit is important.
my $return = $soap->$call(@params);
WebGUI::ErrorHandler::warn("$call(" . (join ',', @params) . ')')
if $self->get('debugMode');
# The possible return types I've come across include a SOAP object,
# a hash reference, a blessed object or a simple scalar. Each type
# requires different handling (woohoo!) before being passed to the
# template system
WebGUI::ErrorHandler::warn(WebGUI::International::get(24, "WSClient") . (ref $return ? ref $return : 'scalar')) if $self->get('debugMode');
# SOAP object
if ((ref $return) =~ /SOAP/i) {
@result = $return->paramsall;
# hashref
} elsif (ref $return eq 'HASH') {
@result = $return;
# blessed object, to be stripped with Acme::Damn
} elsif ($hasUnblessAcme && ref $return) {
WebGUI::ErrorHandler::warn("Acme::Damn::unbless($return)");
@result = Acme::Damn::unbless($return);
# blessed object, to be stripped with Data::Structure::Util
} elsif ($hasUnblessData && ref $return) {
WebGUI::ErrorHandler::warn("Data::Structure::Util::unbless($return)");
@result = Data::Structure::Util::unbless($return);
# scalar value, we hope
} else {
# there's got to be a way to get into the SOAP body and find the
# key name for the value returned, but I haven't figured it out
@result = { 'result' => $return };
}
$cache->set(Storable::freeze(@result),
$self->get('cacheTTL'));
};
# did the soap call fault?
if ($@) {
WebGUI::ErrorHandler::warn($@) if $self->get('debugMode');
$var{'soapError'} = $@;
WebGUI::ErrorHandler::warn(WebGUI::International::get(25, "WSClient") . $var{'soapError'})
if $self->get('debugMode');
}
# cached data was found
} else {
WebGUI::ErrorHandler::warn("Using cached data");
}
WebGUI::ErrorHandler::warn(Dumper(@result)) if
$self->get('debugMode');
# Do we need to decode utf8 data? Will only decode if modules were
# loaded and the wobject requests it
if ($self->{'decodeUtf8'} && $hasUtf8) {
if (Data::Structure::Util::has_utf8(\@result)) {
@result = @{Data::Structure::Util::utf8_off(\@result)};
}
}
# pagination is tricky because we don't know the specific portion of the
# data we need to paginate. Trust the user to have told us the right
# thing. If not, try to Do The Right Thing
if (scalar @result > 1) {
# this case hasn't ever happened running against my dev SOAP::Lite
# services, but let's assume it might. If our results array has
# more than one element, let's hope if contains scalars
$p = WebGUI::Paginator->new($url, $self->get('paginateAfter'));
$p->setDataByArrayRef(\@result);
@result = ();
@result = @$p;
} else {
# In my experience, the most common case. We have an array
# containing a single hashref for which we have been given a key name
if (my $aref = $result[0]->{$self->get('paginateVar')}) {
$var{'numResults'} = scalar @$aref;
$p = WebGUI::Paginator->new($url, $self->get('paginateAfter'));
$p->setDataByArrayRef($aref);
$result[0]->{$self->get('paginateVar')} = $p->getPageData;
} else {
if ((ref $result[0]) =~ /HASH/) {
# this may not paginate the one that they want, but it will
# prevent the wobject from dying
for (keys %{$result[0]}) {
if ((ref $result[0]->{$_}) =~ /ARRAY/) {
$p = WebGUI::Paginator->new($url, $self->get('paginateAfter'));
$p->setDataByArrayRef($result[0]->{$_});
last;
}
}
$p ||= WebGUI::Paginator->new($url);
$result[0]->{$_} = $p->getPageData;
} elsif ((ref $result[0]) =~ /ARRAY/) {
$p = WebGUI::Paginator->new($url, $self->get('paginateAfter'));
$p->setDataByArrayRef($result[0]);
$result[0] = $p->getPageData;
} else {
$p = WebGUI::Paginator->new($url, $self->get('paginateAfter'));
$p->setDataByArrayRef([$result[0]]);
$result[0] = $p->getPageData;
}
}
}
# set pagination links
if ($p) {
$p->appendTemplateVars(\%var);
for ('pagination.firstPage','pagination.lastPage','pagination.nextPage','pagination.pageList',
'pagination.previousPage', 'pagination.pageList.upTo20', 'pagination.pageList.upTo10') {
$var{$_} =~ s/\?/\?cache=$cache_key\&/g;
}
}
} else {
WebGUI::ErrorHandler::warn(WebGUI::International::get(26, "WSClient") . $@) if $self->get('debugMode');
}
# did they request a funky http header?
if ($session{'config'}{'soapHttpHeaderOverride'} &&
$self->get("httpHeader")) {
$session{'header'}{'mimetype'} = $self->get("httpHeader");
WebGUI::ErrorHandler::warn("changed mimetype: " .
$session{'header'}{'mimetype'});
}
# Note, we still process our template below even though it will never
# be displayed if the redirectURL is set. Not sure how important it is
# to do it this way, but it certainly is the least obtrusive to default
# webgui flow. This feature currently requires a patched WebGUI.pm file.
if ($session{'form'}{'redirectURL'}) {
WebGUI::HTTP::setRedirect($session{'form'}{'redirectURL'});
}
$var{'results'} = \@result;
return $self->processTemplate(\%var, "WSClient",$self->get("templateId"));
}
sub _instantiate_soap {
my ($soap, @wobject);
my $self = shift;
# a wsdl file was specified
# we don't use fault handling with wsdls becuase they seem to behave
# differently. Not sure if that is by design.
if ( ($self->get("uri") =~ m/\.wsdl\s*$/i) || ($self->get("uri") =~ m/\.\w*\?wsdl\s*$/i) ) {
WebGUI::ErrorHandler::warn('wsdl=' . $self->get('uri'))
if $self->get('debugMode');
# instantiate SOAP service
$soap = SOAP::Lite->service($self->get('uri'));
# standard uri namespace
} else {
WebGUI::ErrorHandler::warn('uri=' . $self->get('uri'))
if $self->get('debugMode');
# instantiate SOAP service, with fault handling
$soap = new SOAP::Lite
on_fault => sub {
my ($soap, $res) = @_;
die $res->faultstring;
};
$soap->uri($self->get('uri'));
# proxy the call if requested
if ($self->get("proxy") && $soap) {
WebGUI::ErrorHandler::warn('proxy=' . $self->get('proxy'))
if $self->get('debugMode');
$soap->proxy($self->get('proxy'),
options => {compress_threshold => 10000});
}
}
return $soap;
}
1;