File Coverage

blib/lib/WSRF/Lite.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             # ==============================================================================
2             #
3             # Copyright (C) 2000-2008 University of Manchester
4             # WSRF::Lite is free software; you can redistribute it
5             # and/or modify it under the same terms as Perl itself.
6             #
7             # version 0.8.2.7
8             # Author: Mark Mc Keown (mark.mckeown@manchester.ac.uk)
9             #
10             # Stefan Zasada (sjzasada@lycos.co.uk) did most of the work implementing
11             # WS-Security - a big thanks goes to Savas Parastatidis
12             # (http://savas.parastatidis.name/) for helping to get it working with
13             # .NET.
14             #
15             # Contributors: Andrew Porter, Stephen Pickles,
16             # Sven van den Berghe, Jonathan Chin
17             # Jamie Vicary, Bruno Harbulot
18             # Ivan Porro, Ross Nicoll, Luke @ yahoo
19             # Mary Thompson, Alex Peeters, Bjoern A. Zeeb
20             # Glen Fu, John Newman, Doug Claar, Edward Kawas
21             #
22             # Some parts of the this module are taken from SOAP::Lite -
23             # here is the required copyright
24             #
25             # Copyright (C) 2000-2005 Paul Kulchenko (paulclinger@yahoo.com)
26             #
27             #===============================================================================
28              
29             =pod
30              
31             =head1 NAME
32              
33             WSRF::Lite - Implementation of the Web Service Resource Framework
34              
35             =head1 VERSION
36              
37             This document refers to version 0.8.3.1 of WSRF::Lite released Feb, 2011
38              
39             =head1 SYNOPSIS
40              
41             This is an implementation of the Web Service Resource Framework (WSRF),
42             which is built on SOAP::Lite. It provides support for WSRF, WS-Addressing
43             and for digitally signing a SOAP messages using an X.509 certificate
44             according to the OASIS WS-Security standard.
45              
46             =head1 DESCRIPTION
47              
48             WSRF::Lite consists of a number of classes for developing WS-Resources.
49             A WS-Resource is an entity that has a Web service interface defined by
50             the WSRF family of specifications that maintains state between calls
51             to the service.
52              
53             WSRF::Lite provides a number of ways of implementing
54             WS-Resources: one approach uses a process to store the state of the
55             WS-Resource, another approach uses a process to store the state of many
56             WS-Resources and the last approach uses files to store the state of the
57             WS-Resources between calls to the WS-Resource. The different approachs have
58             different benifits, using one process per WS-Resource does not scale very
59             well and isn't very fault tolerant (eg a machine reboot) but is quite
60             easy to develop. The approachs are just examples of how to implement a
61             WS-Resource, it should be possible to use them as a basis to develop
62             tailored solutions for particular applications. For example you could use a
63             relational database to store the state of the WS-Resources.
64              
65             =cut
66              
67             package WSRF::Lite;
68              
69 1     1   21387 use SOAP::Lite;
  0            
  0            
70             use strict;
71              
72             use vars qw{ $VERSION };
73              
74             BEGIN {
75             $VERSION = '0.8.3.1';
76             }
77              
78             # WSRF uses WS-Address headers in the SOAP Header - by default
79             # SOAP::Lite will croak on these so we change the default in
80             # SOAP::Lite. The SOAP spec defines the mustUnderstand attribute -
81             # if an element has this attribute then the service must understand
82             # what to do with this element. See
83             # http://www.w3.org/TR/soap12-part1/#soapmu
84             #
85             # BUG - should ony accept headers we really do understand
86             $SOAP::Constants::DO_NOT_CHECK_MUSTUNDERSTAND = 1;
87              
88             # A singleton class to hold the external socket if there is one.
89             package WSRF::SocketHolder;
90              
91             my $oneTrueSelf;
92              
93             sub instance {
94             unless ( defined $oneTrueSelf ) {
95             my ( $type, $extern_socket ) = @_;
96             my $this = { _socket => $extern_socket };
97             $oneTrueSelf = bless $this, $type;
98             }
99             return $oneTrueSelf;
100             }
101              
102             sub close {
103             my $self = shift;
104             if ( defined $oneTrueSelf ) {
105             my $foo =
106             defined( $ENV{SSL} )
107             ? $self->{_socket}->close( SSL_no_shutdown => 1 )
108             : $self->{_socket}->close;
109             }
110             undef $oneTrueSelf;
111             }
112              
113             #===============================================================================
114             package WSRF::Constants;
115              
116             =pod
117              
118             =head1 WSRF::Constants
119              
120             Defines the set of namespaces used in WSRF::Lite and the directories used to store
121             the named sockets and data files.
122              
123             =over
124              
125             =item $WSRF::Constants::SOCKETS_DIRECTORY
126              
127             Directory to contain the named sockets of the process based WS-Resources.
128              
129             =item $WSRF::Constants::Data
130              
131             Directory used to store files that hold state of WS-Resoures that use file based storage
132              
133             =item $WSRF::Constants::WSA
134              
135             WS-Addressing namespace.
136              
137             =item $WSRF::Constants::WSRL
138              
139             WS-ResourceLifetimes namespace.
140              
141             =item $WSRF::Constants::WSRP
142              
143             WS-ResourceProperties namespace.
144              
145             =item $WSRF::Constants::WSSG
146              
147             WS-ServiceGroup namespace.
148              
149             =item $WSRF::Constants::WSBF
150              
151             WS-BaseFaults namespace.
152              
153             =item $WSRF::Constants::WSU
154              
155             WS-Security untility namespace.
156              
157             =item $WSRF::Constants::WSSE
158              
159             WS-Security extension namespace.
160              
161             =item $WSRF::Constants::WSA_ANON
162              
163             From the WS-Addressing specification, it is used to indicate
164             an anonymous return address. If you are using a request-response protocol like HTTP
165             which uses the same connection for the request and response you use this as the
166             ReplyTo address in SOAP WS-Addressing header of the request.
167              
168             =back
169              
170             =cut
171              
172             #
173             # Where the named Sockets and ResourceProperty files are stored.
174             # User can overide these in the Container script.
175             $WSRF::Constants::SOCKETS_DIRECTORY = "/tmp/wsrf";
176             $WSRF::Constants::Data = $WSRF::Constants::SOCKETS_DIRECTORY . "/data/";
177             $WSRF::Constants::ExternSocket = undef;
178             %WSRF::Constants::ModuleNamespaceMap = ();
179              
180             #The set of namespaces used throughout.
181             #$WSRF::Constants::WSA = 'http://www.w3.org/2005/03/addressing';
182             $WSRF::Constants::WSA = 'http://www.w3.org/2005/08/addressing';
183              
184             #$WSRF::Constants::WSRL = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime';
185             $WSRF::Constants::WSRL = 'http://docs.oasis-open.org/wsrf/rl-2';
186              
187             #$WSRF::Constants::WSRP = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties';
188             $WSRF::Constants::WSRP = 'http://docs.oasis-open.org/wsrf/rp-2';
189              
190             #$WSRF::Constants::WSSG = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ServiceGroup';
191             $WSRF::Constants::WSSG = 'http://docs.oasis-open.org/wsrf/sg-2';
192              
193             #$WSRF::Constants::WSBF = 'http://www.ibm.com/xmlns/stdwip/web-services/WS-BaseFaults';
194             $WSRF::Constants::WSBF = 'http://docs.oasis-open.org/wsrf/bf-2';
195              
196             $WSRF::Constants::WSU =
197             'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
198             $WSRF::Constants::WSSE =
199             'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
200              
201             #$WSRF::Constants::WSA_ANON = $WSRF::Constants::WSA.'/role/anonymous';
202             $WSRF::Constants::WSA_ANON = $WSRF::Constants::WSA . '/anonymous';
203              
204             $WSRF::Constants::DS = 'http://www.w3.org/2000/09/xmldsig#';
205              
206             #===============================================================================
207             # We override SOAP::SOM to store the raw XML from a SOAP message - this class is
208             # used by the WSRF::Deserializer below. SOAP::Lite does not provide you with
209             # access to the raw XML of a SOAP message (It was on the SOAP::Lite TODO list)
210             # - here we override the SOAP::SOM module to provide access to the raw XML -
211             # we override the SOAP::Deserializer which returns the SOAP::SOM object to
212             # make sure that it actually keeps the XML
213              
214             package WSRF::SOM;
215              
216             =pod
217              
218             =head1 WSRF::SOM
219              
220             Extends SOAP::SOM with one extra method "raw_xml".
221              
222             =head2 METHODS
223              
224             =over
225              
226             =item raw_xml
227              
228             Returns the raw XML of a message, useful if you want to parse the message using some
229             other tool than provided with SOAP::Lite:
230              
231             my $xml = $som->raw_xml;
232              
233             =back
234              
235             =cut
236              
237             use strict;
238             use vars qw(@ISA);
239              
240             @ISA = qw(SOAP::SOM);
241              
242             # function to return raw XML
243             sub raw_xml {
244             my $self = shift;
245             return $self->{_xml};
246             }
247              
248             #===============================================================================
249             # We override the SOAP::Serializer to store the raw XML of the SOAP message.
250             # Normally a SOAP::Lite service cannot access the raw XML of a request - this
251             # is sometimes useful for the Service developer who might want to use
252             # XML DOM instead of SOM. The Deserializer returns a WSRF::SOM object - wich
253             # we have defined above.
254             package WSRF::Deserializer;
255              
256             =pod
257              
258             =head1 WSRF::Deserializer
259              
260             Overrides SOAP::Deserializer to return a WSRF::SOM object, which includes the raw XML
261             of the message, from the deserialize method.
262              
263             =head2 METHODS
264              
265             The methods are the same as SOAP::Deserializer.
266              
267             =cut
268              
269             use strict;
270              
271             use vars qw(@ISA);
272              
273             @ISA = qw(SOAP::Deserializer);
274              
275             #This is very similar to the SOAP::Deserializer only a couple of lines are added
276             # Copyright (C) 2000-2005 Paul Kulchenko (paulclinger@yahoo.com)
277             sub deserialize {
278             SOAP::Trace::trace('()');
279             my $self = shift->new;
280              
281             # initialize
282             $self->hrefs( {} );
283             $self->ids( {} );
284              
285             # TBD: find better way to signal parsing errors
286             # This is returning a parsed body, however, if the message was mime
287             # formatted, then the self->ids hash should be populated with mime parts
288             # as will the self->mimeparser->parts array
289             my $parsed =
290             $self->decode( $_[0] ); # TBD: die on possible errors in Parser?
291             # Thought - decode should return an ARRAY which may contain MIME::Entities
292             # then the SOM object that is created and returned from this will know how
293             # to parse them out
294              
295             # Having this code here makes multirefs in the Body work, but multirefs
296             # that reference XML fragments in a MIME part do not work.
297             if ( keys %{ $self->ids() } ) {
298             $self->traverse_ids($parsed);
299             } else {
300             $self->ids($parsed);
301             }
302             $self->decode_object($parsed);
303              
304             # these are the changes from SOAP::Deserializer
305             # otherwise the code is the same. We simply add the raw XML to
306             # the som hash
307             my $som = WSRF::SOM->new($parsed);
308             $som->{'_xml'} = $_[0];
309              
310             # first check if MIME parser has been initialized
311             # simple $self->mimeparser() call doesn't work because of
312             # "lazy initialization" --PK
313             if ( defined $self->{'_mimeparser'} && $self->mimeparser->parts ) {
314              
315             # This seems like an unnecessary copy... does SOAP::SOM have a handle on
316             # the SOAP::Lite->mimeparser instance so that I can skip this?
317             $som->{'_parts'} = $self->mimeparser->parts;
318             }
319             return $som;
320             }
321              
322             #===============================================================================
323             # We override the SOAP::Serializer to add extra namespaces to the SOAP element
324             # - these are namesapace we will use a lot wsrl, wsrp, wsa. These are placed
325             # in any SOAP message we return from the service. The user can use the
326             # prefixs wsrl, wsrp and wsa and not have to worry about defining the
327             # namespaces
328             #
329             # WSRF::WSRFSerializer is were the message is signed - signing is tricky
330             # because we have to create the XML before we sign it, so the process of
331             # signing a SOAP message requires two passes through the serializer. The
332             # first pass (std_envelope) creates the SOAP message, the second actually
333             # signs it. THIS IS NOT EFFICIENT BUT WHO CARES?!
334             package WSRF::WSRFSerializer;
335              
336             =pod
337              
338             =head1 WSRF::WSRFSerializer
339              
340             Overrides SOAP::Serializer. This class extends the SOAP::Serializer class which creates
341             the XML SOAP Enevlope. WSRF::WSRFSerializer overrides the "envelope" method so that it
342             adds the WSRF, WS-Addressing and WS-Security namespaces to the SOAP Envelope, it also
343             where the message signing happens. The XML SOAP message has to be created before it
344             can be signed.
345              
346             =head2 METHODS
347              
348             The methods are the same as SOAP::Serializer, the "envelope" method is overridden to
349             include the extra namespaces and to digitally sign the SOAP message if required.
350              
351             =cut
352              
353             use vars qw(@ISA);
354              
355             @ISA = qw(SOAP::Serializer);
356              
357             # This function is the same as SOAP::Serializer::envelope except that
358             # it adds an extra attribute (wsu:Id="myBody") into the Body element -
359             # this is used by WS-Security to identify the bits of a message that
360             # have been signed.
361             #
362             # We also add extra namespaces for WSRF and WSA into the SOAP Envelope
363             # element so we do not need to declare them in the message itself
364             # Copyright (C) 2000-2005 Paul Kulchenko (paulclinger@yahoo.com)
365             sub old_envelope {
366             SOAP::Trace::trace('()');
367             my $self = shift->new;
368              
369             $self->autotype(0);
370             $self->attr(
371             {
372             'xmlns:wsa' => $WSRF::Constants::WSA,
373             'xmlns:wsrl' => $WSRF::Constants::WSRL,
374             'xmlns:wsrp' => $WSRF::Constants::WSRP,
375             'xmlns:wsu' => $WSRF::Constants::WSU,
376             'xmlns:wsse' => $WSRF::Constants::WSSE
377             }
378             );
379              
380             my $type = shift;
381             my ( @parameters, @header );
382             for (@_) {
383              
384             # Find all the SOAP Headers
385             if ( defined($_) && ref($_) && UNIVERSAL::isa( $_ => 'SOAP::Header' ) )
386             {
387             push( @header, $_ );
388              
389             # Find all the SOAP Message Parts (attachments)
390             } elsif ( defined($_)
391             && ref($_)
392             && $self->context
393             && $self->context->packager->is_supported_part($_) )
394             {
395             $self->context->packager->push_part($_);
396              
397             # Find all the SOAP Body elements
398             } else {
399             push( @parameters, $_ );
400             }
401             }
402             my $header = @header ? SOAP::Data->set_value(@header) : undef;
403             my ( $body, $parameters );
404             if ( $type eq 'method' || $type eq 'response' ) {
405             SOAP::Trace::method(@parameters);
406              
407             my $method = shift(@parameters);
408              
409             # or die "Unspecified method for SOAP call\n";
410              
411             $parameters = @parameters ? SOAP::Data->set_value(@parameters) : undef;
412             if ( !defined($method) ) {
413             } elsif ( UNIVERSAL::isa( $method => 'SOAP::Data' ) ) {
414             $body = $method;
415             } elsif ( $self->use_prefix ) {
416             $body = SOAP::Data->name($method)->uri( $self->uri );
417             } else {
418             $body =
419             SOAP::Data->name($method)->attr( { 'xmlns' => $self->uri } );
420              
421             #$body = SOAP::Data->name($method)->uri($self->uri); # original return before use_prefix
422             }
423              
424             # This is breaking a unit test right now...
425             $body->set_value(
426             SOAP::Utils::encode_data( $parameters ? \$parameters : () ) )
427             if $body;
428             } elsif ( $type eq 'fault' ) {
429             SOAP::Trace::fault(@parameters);
430             $body =
431             SOAP::Data->name(
432             SOAP::Utils::qualify( $self->envprefix => 'Fault' ) )
433              
434             # parameters[1] needs to be escaped - thanks to aka_hct at gmx dot de
435             # commented on 2001/03/28 because of failing in ApacheSOAP
436             # need to find out more about it
437             # -> attr({'xmlns' => ''})
438             ->value(
439             \SOAP::Data->set_value(
440             SOAP::Data->name(
441             faultcode => SOAP::Utils::qualify(
442             $self->envprefix => $parameters[0]
443             )
444             )->type(""),
445             SOAP::Data->name(
446             faultstring => SOAP::Utils::encode_data( $parameters[1] )
447             )->type(""),
448             defined( $parameters[2] )
449             ? SOAP::Data->name(
450             detail => do {
451             my $detail = $parameters[2];
452             ref $detail ? \$detail : $detail;
453             }
454             )
455             : (),
456             defined( $parameters[3] )
457             ? SOAP::Data->name( faultactor => $parameters[3] )->type("")
458             : (),
459             )
460             );
461             } elsif ( $type eq 'freeform' ) {
462             SOAP::Trace::freeform(@parameters);
463             $body = SOAP::Data->set_value(@parameters);
464             } elsif ( !defined($type) ) {
465              
466             # This occurs when the Body is intended to be null. When no method has been
467             # passed in of any kind.
468             } else {
469             die "Wrong type of envelope ($type) for SOAP call\n";
470             }
471              
472             $self->seen( {} ); # reinitialize multiref table
473             # Build the envelope
474             # Right now it is possible for $body to be a SOAP::Data element that has not
475             # XML escaped any values. How do you remedy this?
476             my ($encoded) = $self->encode_object(
477             SOAP::Data->name(
478             SOAP::Utils::qualify( $self->envprefix => 'Envelope' ) =>
479             \SOAP::Data->value(
480             (
481             $header ? SOAP::Data->name(
482             SOAP::Utils::qualify( $self->envprefix => 'Header' ) =>
483             \$header
484             ) : ()
485             ),
486             (
487             $body
488             ? SOAP::Data->name(
489             SOAP::Utils::qualify( $self->envprefix => 'Body' ) =>
490             \$body
491             )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody} } )
492             : SOAP::Data->name(
493             SOAP::Utils::qualify( $self->envprefix => 'Body' )
494             )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody} } )
495             ),
496             )
497             )->attr( $self->attr )
498             );
499             $self->signature( $parameters->signature ) if ref $parameters;
500              
501             # IMHO multirefs should be encoded after Body, but only some
502             # toolkits understand this encoding, so we'll keep them for now (04/15/2001)
503             # as the last element inside the Body
504             # v -------------- subelements of Envelope
505             # vv -------- last of them (Body)
506             # v --- subelements
507             push( @{ $encoded->[2]->[-1]->[2] }, $self->encode_multirefs )
508             if ref $encoded->[2]->[-1]->[2];
509              
510             # Sometimes SOAP::Serializer is invoked statically when there is no context.
511             # So first check to see if a context exists.
512             # TODO - a context needs to be initialized by a constructor?
513             if ( $self->context && $self->context->packager->parts ) {
514              
515             # TODO - this needs to be called! Calling it though wraps the payload twice!
516             # return $self->context->packager->package($self->xmlize($encoded));
517             }
518             return $self->xmlize($encoded);
519             }
520              
521             sub std_envelope {
522             SOAP::Trace::trace('()');
523             my $self = shift->new;
524             my $type = shift;
525              
526             $self->autotype(0);
527             $self->attr(
528             {
529             'xmlns:wsa' => $WSRF::Constants::WSA,
530             'xmlns:wsrl' => $WSRF::Constants::WSRL,
531             'xmlns:wsrp' => $WSRF::Constants::WSRP,
532             'xmlns:wsu' => $WSRF::Constants::WSU,
533             'xmlns:ds' => $WSRF::Constants::DS,
534             'xmlns:wsse' => $WSRF::Constants::WSSE
535             }
536             );
537              
538             my ( @parameters, @header );
539             for (@_) {
540              
541             # Find all the SOAP Headers
542             if ( defined($_) && ref($_) && UNIVERSAL::isa( $_ => 'SOAP::Header' ) )
543             {
544             push( @header, $_ );
545              
546             # Find all the SOAP Message Parts (attachments)
547             } elsif ( defined($_)
548             && ref($_)
549             && $self->context
550             && $self->context->packager->is_supported_part($_) )
551             {
552             $self->context->packager->push_part($_);
553              
554             # Find all the SOAP Body elements
555             } else {
556             push( @parameters, SOAP::Utils::encode_data($_) );
557             }
558             }
559             my $header = @header ? SOAP::Data->set_value(@header) : undef;
560             my ( $body, $parameters );
561             if ( $type eq 'method' || $type eq 'response' ) {
562             SOAP::Trace::method(@parameters);
563              
564             my $method = shift(@parameters);
565              
566             # or die "Unspecified method for SOAP call\n";
567              
568             $parameters = @parameters ? SOAP::Data->set_value(@parameters) : undef;
569             if ( !defined($method) ) {
570             } elsif ( UNIVERSAL::isa( $method => 'SOAP::Data' ) ) {
571             $body = $method;
572             } elsif ( $self->use_default_ns ) {
573             if ( $self->{'_ns_uri'} ) {
574             $body =
575             SOAP::Data->name($method)
576             ->attr( { 'xmlns' => $self->{'_ns_uri'}, } );
577             } else {
578             $body = SOAP::Data->name($method);
579             }
580             } else {
581              
582             # Commented out by Byrne on 1/4/2006 - to address default namespace problems
583             # $body = SOAP::Data->name($method)->uri($self->{'_ns_uri'});
584             # $body = $body->prefix($self->{'_ns_prefix'}) if ($self->{'_ns_prefix'});
585              
586             # Added by Byrne on 1/4/2006 - to avoid the unnecessary creation of a new
587             # namespace
588             # Begin New Code (replaces code commented out above)
589             $body = SOAP::Data->name($method);
590             my $pre = $self->find_prefix( $self->{'_ns_uri'} );
591             $body = $body->prefix($pre) if ( $self->{'_ns_prefix'} );
592              
593             # End new code
594              
595             }
596              
597             # This is breaking a unit test right now...
598             #$body->set_value(SOAP::Utils::encode_data($parameters ? \$parameters : ())) if $body;
599             $body->set_value( $parameters ? \$parameters : () ) if $body;
600             } elsif ( $type eq 'fault' ) {
601             SOAP::Trace::fault(@parameters);
602             $body =
603             SOAP::Data->name(
604             SOAP::Utils::qualify( $self->envprefix => 'Fault' ) )
605              
606             # parameters[1] needs to be escaped - thanks to aka_hct at gmx dot de
607             # commented on 2001/03/28 because of failing in ApacheSOAP
608             # need to find out more about it
609             # -> attr({'xmlns' => ''})
610             ->value(
611             \SOAP::Data->set_value(
612             SOAP::Data->name(
613             faultcode => SOAP::Utils::qualify(
614             $self->envprefix => $parameters[0]
615             )
616             )->type(""),
617             SOAP::Data->name(
618             faultstring => SOAP::Utils::encode_data( $parameters[1] )
619             )->type(""),
620             defined( $parameters[2] )
621             ? SOAP::Data->name(
622             detail => do {
623             my $detail = $parameters[2];
624             ref $detail ? \$detail : $detail;
625             }
626             )
627             : (),
628             defined( $parameters[3] )
629             ? SOAP::Data->name( faultactor => $parameters[3] )->type("")
630             : (),
631             )
632             );
633             } elsif ( $type eq 'freeform' ) {
634             SOAP::Trace::freeform(@parameters);
635             $body = SOAP::Data->set_value(@parameters);
636             } elsif ( !defined($type) ) {
637              
638             # This occurs when the Body is intended to be null. When no method has been
639             # passed in of any kind.
640             } else {
641             die "Wrong type of envelope ($type) for SOAP call\n";
642             }
643              
644             $self->seen( {} ); # reinitialize multiref table
645             # Build the envelope
646             # Right now it is possible for $body to be a SOAP::Data element that has not
647             # XML escaped any values. How do you remedy this?
648             my ($encoded) = $self->encode_object(
649             SOAP::Data->name(
650             SOAP::Utils::qualify( $self->envprefix => 'Envelope' ) =>
651             \SOAP::Data->value(
652             (
653             $header ? SOAP::Data->name(
654             SOAP::Utils::qualify( $self->envprefix => 'Header' ) =>
655             \$header
656             ) : ()
657             ),
658             (
659             $body
660             ? SOAP::Data->name(
661             SOAP::Utils::qualify( $self->envprefix => 'Body' ) =>
662             \$body
663             )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody} } )
664             : SOAP::Data->name(
665             SOAP::Utils::qualify( $self->envprefix => 'Body' )
666             )->attr( { 'wsu:Id' => $WSRF::WSS::ID{myBody} } )
667             ),
668             )
669             )->attr( $self->attr )
670             );
671             $self->signature( $parameters->signature ) if ref $parameters;
672              
673             # IMHO multirefs should be encoded after Body, but only some
674             # toolkits understand this encoding, so we'll keep them for now (04/15/2001)
675             # as the last element inside the Body
676             # v -------------- subelements of Envelope
677             # vv -------- last of them (Body)
678             # v --- subelements
679             push( @{ $encoded->[2]->[-1]->[2] }, $self->encode_multirefs )
680             if ref $encoded->[2]->[-1]->[2];
681              
682             # Sometimes SOAP::Serializer is invoked statically when there is no context.
683             # So first check to see if a context exists.
684             # TODO - a context needs to be initialized by a constructor?
685             if ( $self->context && $self->context->packager->parts ) {
686              
687             # TODO - this needs to be called! Calling it though wraps the payload twice!
688             # return $self->context->packager->package($self->xmlize($encoded));
689             }
690             return $self->xmlize($encoded);
691             }
692              
693             # This function is called whenever a SOAP message is created using the
694             # WSRF::Serializer. First it calls std_envelope to create the SOAP message,
695             # then it takes this message and signs the bits of the message that should
696             # be signed and adds the extra signing information into the message
697             sub envelope {
698             my $self = shift @_;
699              
700             my ($dummy, $method, $params,$orig_header) = @_;
701             #create an envelope - this returns raw XML
702             my $envelope = $self->std_envelope(@_);
703              
704             #if the user has defined these env then he wants the envlope signed -
705             #we take the envelope in the above step and do the necessary
706             if ( defined( $ENV{WSS_SIGN} ) ) {
707              
708             #call the function to sign the envlope - returns the Header and Body
709             #as raw XML
710             my ( $header, $Body ) = WSRF::WSS::sign($envelope);
711              
712             #returns the body and header as XMl - the header does not have its top
713             #and tail ie. the and are missing so we
714             #add them
715             my ($encoded) = $self->encode_object(
716             SOAP::Data->name(
717             SOAP::Utils::qualify( $self->envprefix => 'Envelope' ) =>
718             \SOAP::Data->value(
719             SOAP::Data->name(
720             SOAP::Utils::qualify( $self->envprefix => 'Header' ) =>
721             ($orig_header ?
722             \SOAP::Data->value( $orig_header, SOAP::Data->value($header)->type('xml') )
723             :
724             \SOAP::Data->value($header)->type('xml')
725             )
726             ),
727             SOAP::Data->value($Body)->type('xml')
728             )
729             )->attr( $self->attr )
730             );
731              
732             #$encoded is a SOAP::data - we convert it to XML
733             $envelope = $self->xmlize($encoded);
734             }
735              
736             return $envelope;
737             }
738              
739             #===============================================================================
740             # Take a SOAP::Data object and serialise it - if we are given a SOAP::SOM or
741             # SOAP::Data object and we want to get simple XML without all the SOAP stuff
742             # added we use this class. Useful if the user wants to use DOM instead of
743             # SOM to handle the object.
744             #
745             # This is useful if we have a SOAP::Data or SOAP::SOM object which we want to
746             # convert to XML (e.g. to write to a file) without all the SOAP crap.
747             # Other Perl packages will do this for you (convert a Perl object to XML)
748             # but I want to reuse the SOAP::Lite stuff.
749             #
750             package WSRF::SimpleSerializer;
751              
752             =pod
753              
754             =head1 WSRF::SimpleSerializer
755              
756             Overrides SOAP::Serializer. This is helper class that is based in SOAP::Serializer,
757             it will serialize a SOAP::Data object into XML but without adding the SOAP namespaces
758             etc. It is useful if you want to extra some simple XML from a SOM object, retrieve
759             a SOAP::Data object from the SOM then serialize it to simple XML.
760              
761             my $serializer = WSRF::SimpleSerializer->new();
762             my $xml = $seriaizer->serialize( $som->dataof('/Envelope/Body/[1]') );
763              
764             =head2 METHODS
765              
766             All methods are the same as SOAP::Serializer except "serialize".
767              
768             =over
769              
770             =item serialize
771              
772             This method from SOAP::Serializer is overridden so that it does not add the SOAP namepaces
773             to the XML or set the types of the elements in the XML.
774              
775             sub serialize {
776             my $self = shift @_;
777             $self->autotype(0);
778             $self->namespaces({});
779             $self->encoding(undef);
780             $self->SUPER::serialize(@_);
781             }
782              
783             =back
784              
785             =cut
786              
787             use strict;
788             use vars qw(@ISA);
789              
790             @ISA = qw(SOAP::Serializer); # derived from the SOAP::Serializer
791              
792             sub typecast { return; }
793              
794             #we override the serialize funtion, switching of lots of stuff
795             sub serialize {
796             my $self = shift @_;
797             $self->autotype(0);
798             $self->namespaces( {} );
799             $self->encoding(undef);
800             $self->SUPER::serialize(@_);
801             }
802              
803             #===============================================================================
804             # The Container that handles all the connections for us.
805             #
806             # All incoming messages arrive at the handle function -
807             # in previous versions of WSRF::Lite function that was
808             # way too big. Now we have a hash which allows use to
809             # map messages to functions depending on the destination
810             # URI. This makes it easy to add handlers for messages.
811             #
812             # BUG - should be Object Orientated
813             #
814             package WSRF::Container;
815              
816             =pod
817              
818             =head1 WSRF::Container
819              
820             WSRF::Container handles incoming messages and dispatchs them to the appropriate
821             WS-Resource.
822              
823             =head2 METHODS
824              
825             =over
826              
827             =item handle
828              
829             Takes a HTTP Request object and dispatchs it to the appropriate WS-Resource,
830             handle returns a HTTP Response object from the WS-Resource which should be
831             returned to the client.
832              
833             =back
834              
835             =cut
836              
837             use IO::Socket;
838             use HTTP::Daemon;
839             use HTTP::Status;
840             use HTTP::Response;
841              
842             # This hash maps incoming messages to functions - the mapping is done
843             # using the RequestURI in the HTTP Header. It should be very easy to
844             # add a custom handler!
845             # The key in this hash is used in a regular expression - it is matched
846             # to the start of the RequestURI - eg
847             # http://vermont.mvc.mcc.ac.uk/WSRF/foobar -> WSRF
848             # (/WSRF/foobar is the RequestURI)
849             %WSRF::Container::HandlerMap = (
850             'WSRF' => \&WSRF::Container::WSRFHandler,
851             'Session' => \&WSRF::Container::SessionHandler,
852             'MultiSession' => \&WSRF::Container::MultiSessionHandler
853             );
854              
855             # All messages should pass through this handle function - $r is a
856             # HTTP::Request Object
857             sub handle {
858             my ( $r, $socket ) = @_;
859              
860             #need to record if this process has an open socket with the world
861             #- if we fork we might need to close it
862             $WSRF::Constants::ExternSocket = WSRF::SocketHolder->instance($socket);
863              
864             if ( !$r ) {
865             print STDERR "$$ WSRF::Container HTTP::Request not defined!";
866             return;
867             }
868              
869             my $Path = $r->uri->path;
870             if ( $Path =~ m/\.{2,}/og ) {
871             print STDERR
872             "$$ WSRF::Container Path $Path contains unacceptable charactors.\n";
873             my $fail = new HTTP::Response(RC_NOT_FOUND);
874             $fail->header( 'Content-Type' => 'text/xml' );
875             $fail->content("Path $Path contains unacceptable charactors.\n");
876             return $fail;
877             }
878              
879             my ($response);
880              
881             #walk through the hash until we find a handler for this function - we put
882             #the key between / and / and do a reg expression match
883             my $found = undef;
884             LINE: foreach my $key ( keys %WSRF::Container::HandlerMap ) {
885             if ( $Path =~ m/^\/$key\// ) {
886             $found = "TRUE";
887             print STDERR "$$ WSRF::Container Using $key Handler\n";
888             $response = $WSRF::Container::HandlerMap{$key}->($r);
889             last LINE;
890             }
891             }
892              
893             #no handler found - return a 404 HTTP error message
894             if ( !$found ) {
895             $response = HTTP::Response->new(404);
896             }
897              
898             return $response;
899             }
900              
901             # handles messages with URI http://blah.com/WSRF/
902             # this maps to WS-Resources that use a process to manage the
903             # state of a WS-Resource, one process per WS-Resource. This
904             # functions sends the message down a UNIX socket to the process
905             sub WSRFHandler {
906             my $request = shift @_;
907              
908             #Only Handle GET and POST
909             return HTTP::Response->new(RC_FORBIDDEN)
910             if ( $request->method ne 'POST'
911             && $request->method ne 'GET'
912             && $request->method ne 'DELETE'
913             && $request->method ne 'PUT' );
914              
915             print STDERR "$$ WSRFHandler called\n";
916             my $Path = $request->uri->path;
917              
918             #strip extra '/' at start of URL
919             $Path =~ s/^\/+//o;
920              
921             #remeber the Path - we will put this in our responses so clients
922             #will know who sent them the message - part of WS-Addressing
923             $ENV{FROM} = $ENV{URL} . $Path;
924              
925             #split up Path part of URL - we multiplex on the first part (the base)
926             #the module name is the last part
927             my @PathArray = split( /\//, $Path );
928             my $ID = pop @PathArray;
929             my $base = $PathArray[0];
930             my $ModuleName = pop @PathArray;
931             print "$$ ModuleName= $ModuleName\n";
932             my $Directory = join '/', @PathArray;
933              
934             #this is the absolute path now
935             $Directory = $ENV{WSRF_MODULES} . "/" . $Directory;
936             print STDERR "Directory= $Directory\n";
937              
938             $Path = $ENV{WSRF_MODULES} . "/" . $Path;
939              
940             #check the ID is safe - we do not accept dots,
941             #all paths will be relative to $ENV{WRF_MODULES}
942             #only allow alphanumeric, underscore and hyphen
943             if ( $ID !~ m/^([-\w]+)$/ && $ID !~ m/^$ModuleName\.(xsl|js|css|svg)$/ ) {
944             print STDERR "$$ Bad ID $ID\n";
945             my $fail = new HTTP::Response(RC_BAD_REQUEST);
946             $fail->header( 'Content-Type' => 'text/xml' );
947             $fail->content(
948             SOAP::Serializer->fault(
949             'Bad WS-Resource Identifier',
950             "WS-Resource identifier contains bad charactors"
951             )
952             );
953              
954             return $fail;
955             }
956              
957             my ($PUT);
958             if ( $request->method eq 'PUT' ) {
959             $PUT = 1;
960              
961             my $To = $ENV{URL};
962             chop $To;
963             $To .= $request->uri;
964             my $header =
965             SOAP::Header->value( "" . $To . "" )->type('xml');
966             my $xml = $request->content;
967              
968             print STDERR "$$ Attempt to PUT\n";
969              
970             $xml =~ s/^<\?xml[\s\w\.\-].*\?>\n?//o;
971             print STDERR "$$ >>>xml>>>\n$xml\n<<
972              
973             my $data =
974             SOAP::Data->name('PutResourcePropertyDocument')->prefix('wsrp')
975             ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } )
976             ->value( \SOAP::Data->value($xml)->type('xml') );
977              
978             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
979             print "$$ >>>envelope>>>\n$envelope\n<<
980             $request = HTTP::Request->new();
981             $request->method('POST');
982             $request->header( "SOAPAction" =>
983             "$WSRF::Constants::WSRP/PutResourcePropertyDocument" );
984             $request->header( "Content-Length" => length $envelope );
985             $request->content($envelope);
986             }
987              
988             print "$$ ID= $ID\n";
989             my ($GET);
990             if ( $request->method eq 'GET' ) {
991              
992             #does the client just want the WSDL/XSL/CSS for service
993             if ( $request->uri->query eq 'WSDL' ) {
994             my $resp = GetWSDL($request);
995             return $resp;
996             } elsif ( $ID =~ m/^$ModuleName\.(xsl|css|js|svg)$/ )
997              
998             #looking for xsl or css or js
999             {
1000             print "$$ Getting $ID file\n";
1001             my $resp = HTTP::Response->new();
1002             my $file = $Directory . "/" . $ID;
1003             print "$$ File to open is $file\n";
1004             if ( !( -f $file ) || !( -r $file ) ) {
1005             $resp->code(404);
1006             return $resp;
1007             }
1008             open FILE, "< $file" or die "$$ Could not open $file";
1009             my $xsl = join "", ;
1010             close FILE or die "Could not close $file file";
1011             $resp->header( 'Content-Type' => 'text/xml' )
1012             if ( $ID =~ m/\.xsl$/ );
1013             $resp->header( 'Content-Type' => 'text/css' )
1014             if ( $ID =~ m/\.css$/ );
1015             $resp->header( 'Content-Type' => 'text/javascript' )
1016             if ( $ID =~ m/\.js$/ );
1017             $resp->header( 'Content-Type' => 'text/xml' )
1018             if ( $ID =~ m/\.svg$/ );
1019              
1020             $resp->content($xsl);
1021             return $resp;
1022             }
1023              
1024             #wants ResourceProperties
1025             $GET = 1;
1026             my $data =
1027             SOAP::Data->name('GetResourcePropertyDocument')->prefix('wsrp')
1028             ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } );
1029             my $To = $ENV{URL};
1030             chop $To;
1031             $To .= $request->uri;
1032             my $header =
1033             SOAP::Header->value( "" . $To . "" )->type('xml');
1034             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1035             $request = HTTP::Request->new();
1036             $request->method('POST');
1037             $request->header( "SOAPAction" =>
1038             "$WSRF::Constants::WSRP/GetResourcePropertyDocument" );
1039             $request->header( "Content-Length" => length $envelope );
1040             $request->content($envelope);
1041             }
1042              
1043             if ( $request->method eq 'DELETE' ) {
1044             my $data =
1045             SOAP::Data->name('Destroy')->prefix('wsrl')
1046             ->attr( { 'xmlns:wsrl' => $WSRF::Constants::WSRL } );
1047             my $To = $ENV{URL};
1048             chop $To;
1049             $To .= $request->uri;
1050             my $header =
1051             SOAP::Header->value( "" . $To . "" )->type('xml');
1052             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1053             $request = HTTP::Request->new();
1054             $request->method('POST');
1055             $request->header( "SOAPAction" => "$WSRF::Constants::WSRL/Destroy" );
1056             $request->header( "Content-Length" => length $envelope );
1057             $request->content($envelope);
1058             }
1059              
1060             my $rend = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $ID;
1061              
1062             #check that the Socket exists for the requested Grid Service
1063             if ( !-S $rend ) {
1064             print STDERR "$$ UNIX Socket $rend does not exist\n";
1065             my $fail = new HTTP::Response(RC_NOT_FOUND);
1066             $fail->header( 'Content-Type' => 'text/xml' );
1067             $fail->content(
1068             SOAP::Serializer->fault(
1069             'No such WS-Resource type',
1070             "Check Endpoint of service"
1071             )
1072             );
1073              
1074             return $fail;
1075             }
1076              
1077             print STDERR "$$ $Path Child $$ Starting Processing\n";
1078             print STDERR "$$ Client Rendezvous $rend\n";
1079              
1080             #open a socket to the GS
1081             my $MyFH = IO::Socket::UNIX->new(
1082             Peer => "$rend",
1083             Type => SOCK_STREAM,
1084             Timeout => 10
1085             )
1086             or die SOAP::Fault->faultcode("Container Fault")
1087             ->faultstring("Container Failure - Socket problem $!");
1088             print STDERR "$$ Client Socket $MyFH\n";
1089              
1090             #if using SSL add the extra information to the HTTP request
1091             # we stick it into the HTTP Header
1092             if ( defined( $ENV{SSL_CLIENT_DN} ) ) {
1093             $request->header( 'Client-SSL-Cert-Subject' => "$ENV{SSL_CLIENT_DN}" );
1094             $request->header(
1095             'Client-SSL-Cert-Issuer' => "$ENV{SSL_CLIENT_ISSUER}" );
1096             }
1097              
1098             #send down socket and wait for response
1099             my $out = print $MyFH ( $request->as_string() );
1100              
1101             if ( !defined($out) ) { print STDERR "$$ Could not write to $MyFH\n" }
1102              
1103             #read the response from the Socket and turn it into a
1104             #HTTP::Response
1105             my $resp = WSRF::Daemon::ResponseHandler($MyFH);
1106             $MyFH->close;
1107             print STDERR "$$ $Path Processing Finished\n";
1108              
1109             # print STDERR "$$ >>>out>>>\n".$resp->content."\n<<
1110              
1111             if ( $GET || $PUT ) #Original Request was a GET
1112             {
1113             $resp =
1114             WSRF::Container::getProperties( $resp, $Directory, $ModuleName );
1115             $resp->header( "Pragma" => "no-cache" );
1116             $resp->header(
1117             "Cache-Control" => "no-cache, max-age=1, must-revalidate" );
1118             }
1119             return $resp;
1120             }
1121              
1122             # This function handles messages that have a URI like
1123             # http://blah.com/Session/stuff
1124             # Session WS-Resources store their state in a DB/filesystem etc...
1125             # this function loads the function that loads the code to access
1126             # the state and process the message
1127             sub SessionHandler {
1128             my $request = shift @_;
1129             print STDERR "$$ SessionHandler called\n";
1130              
1131             #Only Handle GET and POST
1132             return HTTP::Response->new(RC_FORBIDDEN)
1133             if ( $request->method ne 'POST'
1134             && $request->method ne 'GET'
1135             && $request->method ne 'DELETE'
1136             && $request->method ne 'PUT' );
1137              
1138             my $Path = $request->uri->path;
1139              
1140             #strip extra '/' at start of URL
1141             $Path =~ s/^\/+//o;
1142              
1143             #remeber the Path - we will put this in our responses so clients
1144             #will know who sent them the message - part of WS-Addressing
1145             $ENV{FROM} = $ENV{URL} . $Path;
1146              
1147             #split up Path part of URL - we multiplex on the first part (the base)
1148             #the module name is the last part
1149             my @PathArray = split( /\//, $Path );
1150             my $ID = pop @PathArray;
1151             my ($module);
1152             if ( $ID =~ /\d+-?d*/o
1153             || $ID =~ /^\w+\.(js|xsl|css|svg)$/ ) #a resource identifier
1154             {
1155             $module = pop @PathArray;
1156             } else {
1157             $module = $ID;
1158             }
1159             $ENV{ID} = $ID;
1160              
1161             my $base = $PathArray[0];
1162             my $RelativeDirectory = join '/', @PathArray;
1163              
1164             #this is the absolute path now
1165              
1166             my $Directory = $ENV{WSRF_MODULES} . "/" . $RelativeDirectory;
1167             print STDERR "$$ Directory to modules $Directory\n";
1168              
1169             my $tmpPath = $Directory . '/' . $module . ".pm";
1170             print STDERR "$$ Path to module $tmpPath\n";
1171             if ( !-f $tmpPath ) {
1172             print STDERR "$$ ERROR $tmpPath no such file\n";
1173             my $fail = new HTTP::Response(RC_OK);
1174             $fail->header( 'Content-Type' => 'text/xml' );
1175              
1176             #$fail->content("GS::$Path No Such service\n");
1177             $fail->content(
1178             SOAP::Serializer->fault(
1179             'No Service', "Check Endpoint of Service"
1180             )
1181             );
1182             return $fail;
1183             }
1184              
1185             my ($PUT);
1186             if ( $request->method eq 'PUT' ) {
1187             $PUT = 1;
1188              
1189             my $To = $ENV{URL};
1190             chop $To;
1191             $To .= $request->uri;
1192             my $header =
1193             SOAP::Header->value( "" . $To . "" )->type('xml');
1194             my $xml = $request->content;
1195              
1196             print STDERR "$$ Attempt to PUT\n";
1197              
1198             $xml =~ s/^<\?xml[\s\w\.\-].*\?>\n?//o;
1199             print STDERR "$$ >>>xml>>>\n$xml\n<<
1200              
1201             my $data =
1202             SOAP::Data->name('PutResourcePropertyDocument')->prefix('wsrp')
1203             ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } )
1204             ->value( \SOAP::Data->value($xml)->type('xml') );
1205              
1206             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1207             print "$$ >>>envelope>>>\n$envelope\n<<
1208             $request = HTTP::Request->new();
1209             $request->method('POST');
1210             $request->header( "SOAPAction" =>
1211             "$WSRF::Constants::WSRP/PutResourcePropertyDocument" );
1212             $request->header( "Content-Length" => length $envelope );
1213             $request->content($envelope);
1214             }
1215              
1216             my ($GET);
1217             if ( $request->method eq 'GET' ) {
1218              
1219             #does the client just want the WSDL for service
1220             if ( $request->uri->query eq 'WSDL' ) {
1221             my $resp = GetWSDL($request);
1222             return $resp;
1223             } elsif ( $ID =~ m/^$module\.(xsl|css|js|svg)$/ )
1224              
1225             #looking for xsl or css or js
1226             {
1227             print "$$ Getting $ID file\n";
1228             my $resp = HTTP::Response->new();
1229             my $file = $Directory . "/" . $ID;
1230             print "$$ File to open is $file\n";
1231             if ( !( -f $file ) || !( -r $file ) ) {
1232             $resp->code(404);
1233             return $resp;
1234             }
1235             print "$$ File to open is $file\n";
1236             open FILE, "< $file" or die "$$ Could not open $file";
1237             my $xsl = join "", ;
1238             close FILE or die "Could not close WSDL file";
1239             $resp->header( 'Content-Type' => 'text/xml' )
1240             if ( $ID =~ m/\.xsl$/ );
1241             $resp->header( 'Content-Type' => 'text/css' )
1242             if ( $ID =~ m/\.css$/ );
1243             $resp->header( 'Content-Type' => 'text/javascript' )
1244             if ( $ID =~ m/\.js$/ );
1245             $resp->header( 'Content-Type' => 'text/xml' )
1246             if ( $ID =~ m/\.svg$/ );
1247              
1248             $resp->content($xsl);
1249             return $resp;
1250             }
1251              
1252             $GET = 1;
1253             my $data =
1254             SOAP::Data->name('GetResourcePropertyDocument')->prefix('wsrp')
1255             ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } );
1256             my $To = $ENV{URL};
1257             chop $To;
1258             $To .= $request->uri;
1259             my $header =
1260             SOAP::Header->value( "" . $To . "" )->type('xml');
1261             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1262             $request = HTTP::Request->new();
1263             $request->method('POST');
1264             $request->header( "SOAPAction" =>
1265             "$WSRF::Constants::WSRP/GetResourcePropertyDocument" );
1266             $request->header( "Content-Length" => length $envelope );
1267             $request->content($envelope);
1268             }
1269              
1270             if ( $request->method eq 'DELETE' ) {
1271             my $data =
1272             SOAP::Data->name('Destroy')->prefix('wsrl')
1273             ->attr( { 'xmlns:wsrl' => $WSRF::Constants::WSRL } );
1274             my $To = $ENV{URL};
1275             chop $To;
1276             $To .= $request->uri;
1277             my $header =
1278             SOAP::Header->value( "" . $To . "" )->type('xml');
1279             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1280             $request = HTTP::Request->new();
1281             $request->method('POST');
1282             $request->header( "SOAPAction" => "$WSRF::Constants::WSRL/Destroy" );
1283             $request->header( "Content-Length" => length $envelope );
1284             $request->content($envelope);
1285             }
1286              
1287             print STDERR "$$ Dispatch path $Directory\n";
1288             my %namespacemap = (
1289             $WSRF::Constants::WSRL => "$module",
1290             $WSRF::Constants::WSRP => "$module",
1291             $WSRF::Constants::WSSG => "$module"
1292             );
1293             %namespacemap = ( %namespacemap, %WSRF::Constants::ModuleNamespaceMap );
1294              
1295             #this loads the module to handle this function, the module
1296             #will retrieve the state for the WS-Resource from a DB or
1297             #some other stable storage, process the message and return the
1298             #state to the stable storage
1299             my $resp =
1300             WSRF::Session->dispatch_to($Directory)->dispatch_with( \%namespacemap )
1301             ->serializer( WSRF::WSRFSerializer->new )
1302             ->deserializer( WSRF::Deserializer->new )->handle($request);
1303              
1304             print STDERR "$$ >>>out>>>\n" . $resp->content . "\n<<
1305             if ( $GET || $PUT ) #Original Request was a GET
1306             {
1307             $resp = WSRF::Container::getProperties( $resp, $Directory, $module );
1308             }
1309              
1310             return $resp;
1311             }
1312              
1313             sub getProperties {
1314             my $resp = shift @_;
1315             my $Dir = shift @_;
1316             my $Module = shift @_;
1317             my $xml = $resp->content;
1318             eval { require XML::LibXML };
1319             if ( !$@ ) #we have XML::LibXML, so we can strip the SOAP stuff
1320             {
1321             #my $xpath = ' 1322             # . $WSRF::Constants::WSRP
1323             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsrp:ResourceProperties]';
1324             my $xpath = '(//. | //@* | //namespace::*)[ancestor-or-self::wsrp:ResourceProperties]';
1325            
1326             my $canon = '' . "\n";
1327             $canon = $canon
1328             . '
1329             . $Module
1330             . '.xsl"?>' . "\n"
1331             if ( -f $Dir . "/$Module.xsl" && -r $Dir . "/$Module.xsl" );
1332             my $parser = XML::LibXML->new();
1333             my $doc = $parser->parse_string($xml);
1334             $canon .= $doc->toStringEC14N( 0, $xpath, [''] );
1335             $resp->header( "Content-Length" => length $canon );
1336             $resp->content($canon);
1337             }
1338             return $resp;
1339             }
1340              
1341             # This fuction handles message with URIs like
1342             # http://blah.com/MultiSession/foe
1343             # WS-Resources for this use a single process to store the state of multiple
1344             # WS-Resources. The function passes the message onto the process that handles
1345             # messages for all the WS-Resources of a particular type - if the process
1346             # has not been created ie if this is the first call to this type of
1347             # WS-Resource then this function will create the process
1348             sub MultiSessionHandler {
1349             my $request = shift @_;
1350             print STDERR "$$ MultiSessionHandler called\n";
1351              
1352             #Only Handle GET and POST
1353             return HTTP::Response->new(RC_FORBIDDEN)
1354             if ( $request->method ne 'POST'
1355             && $request->method ne 'GET'
1356             && $request->method ne 'DELETE'
1357             && $request->method ne 'PUT' );
1358              
1359             my $Path = $request->uri->path;
1360              
1361             #strip extra '/' at start of URL
1362             $Path =~ s/^\/+//o;
1363              
1364             #remeber the Path - we will put this in our responses so clients
1365             #will know who sent them the message - part of WS-Addressing
1366             $ENV{FROM} = $ENV{URL} . $Path;
1367              
1368             #split up Path part of URL - we multiplex on the first part (the base)
1369             #the module name is the last part
1370             my @PathArray = split( /\//, $Path );
1371             my $ID = pop @PathArray;
1372             my ($module);
1373              
1374             if ( $ID =~ /\d+-?d*/o
1375             || $ID =~ /^\w+\.(xsl|js|css|svg)$/o ) #a resource identifier
1376             {
1377             $module = pop @PathArray;
1378             } else {
1379             $module = $ID;
1380             }
1381             $ENV{ID} = $ID;
1382             my $base = $PathArray[0];
1383             my $RelativeDirectory = join '/', @PathArray;
1384              
1385             #this is the absolute path now
1386             my $Directory = $ENV{WSRF_MODULES} . "/" . $RelativeDirectory;
1387              
1388             #check the message actually maps to a module
1389             my $tmpPath = $Directory . '/' . $module . ".pm";
1390             print STDERR "$$ Path to module $tmpPath\n";
1391             if ( !-f $tmpPath ) {
1392             print STDERR "$$ ERROR:: $tmpPath No Such File\n";
1393             my $fail = new HTTP::Response(RC_OK);
1394             $fail->header( 'Content-Type' => 'text/xml' );
1395             $fail->content(
1396             SOAP::Serializer->fault(
1397             'No Service', "Check Endpoint of Service"
1398             )
1399             );
1400             return $fail;
1401             }
1402              
1403             my ($PUT);
1404             if ( $request->method eq 'PUT' ) {
1405             $PUT = 1;
1406              
1407             my $To = $ENV{URL};
1408             chop $To;
1409             $To .= $request->uri;
1410             my $header =
1411             SOAP::Header->value( "" . $To . "" )->type('xml');
1412             my $xml = $request->content;
1413              
1414             print STDERR "$$ Attempt to PUT\n";
1415              
1416             $xml =~ s/^<\?xml[\s\w\.\-].*\?>\n?//o;
1417             print STDERR "$$ >>>xml>>>\n$xml\n<<
1418              
1419             my $data =
1420             SOAP::Data->name('PutResourcePropertyDocument')->prefix('wsrp')
1421             ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } )
1422             ->value( \SOAP::Data->value($xml)->type('xml') );
1423              
1424             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1425             print "$$ >>>envelope>>>\n$envelope\n<<
1426             $request = HTTP::Request->new();
1427             $request->method('POST');
1428             $request->header( "SOAPAction" =>
1429             "$WSRF::Constants::WSRP/PutResourcePropertyDocument" );
1430             $request->header( "Content-Length" => length $envelope );
1431             $request->content($envelope);
1432             }
1433              
1434             my ($GET);
1435             if ( $request->method eq 'GET' ) {
1436              
1437             #does the client just want the WSDL for service
1438             if ( $request->uri->query eq 'WSDL' ) {
1439             my $resp = GetWSDL($request);
1440             return $resp;
1441             } elsif ( $ID =~ m/^$module\.(xsl|css|js|svg)$/ )
1442              
1443             #looking for xsl or css or js
1444             {
1445             print "$$ Getting $ID file\n";
1446             my $resp = HTTP::Response->new();
1447             my $file = $Directory . "/" . $ID;
1448             print "$$ File to open is $file\n";
1449             if ( !( -f $file ) || !( -r $file ) ) {
1450             $resp->code(404);
1451             return $resp;
1452             }
1453             open FILE, "< $file" or die "$$ Could not open $file";
1454             my $xsl = join "", ;
1455             close FILE or die "Could not close $file file";
1456             $resp->header( 'Content-Type' => 'text/xml' )
1457             if ( $ID =~ m/\.xsl$/ );
1458             $resp->header( 'Content-Type' => 'text/css' )
1459             if ( $ID =~ m/\.css$/ );
1460             $resp->header( 'Content-Type' => 'text/javascript' )
1461             if ( $ID =~ m/\.js$/ );
1462             $resp->header( 'Content-Type' => 'text/xml' )
1463             if ( $ID =~ m/\.svg$/ );
1464              
1465             $resp->content($xsl);
1466             return $resp;
1467             }
1468              
1469             $GET = 1;
1470             my $data =
1471             SOAP::Data->name('GetResourcePropertyDocument')->prefix('wsrp')
1472             ->attr( { 'xmlns:wsrp' => $WSRF::Constants::WSRP } );
1473             my $To = $ENV{URL};
1474             chop $To;
1475             $To .= $request->uri;
1476             my $header =
1477             SOAP::Header->value( "" . $To . "" )->type('xml');
1478             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1479             $request = HTTP::Request->new();
1480             $request->method('POST');
1481             $request->header( "SOAPAction" =>
1482             "$WSRF::Constants::WSRP/GetResourcePropertyDocument" );
1483             $request->header( "Content-Length" => length $envelope );
1484             $request->content($envelope);
1485             }
1486              
1487             if ( $request->method eq 'DELETE' ) {
1488             my $data =
1489             SOAP::Data->name('Destroy')->prefix('wsrl')
1490             ->attr( { 'xmlns:wsrl' => $WSRF::Constants::WSRL } );
1491             my $To = $ENV{URL};
1492             chop $To;
1493             $To .= $request->uri;
1494             my $header =
1495             SOAP::Header->value( "" . $To . "" )->type('xml');
1496             my $envelope = WSRF::WSRFSerializer->new()->freeform( $header, $data );
1497             $request = HTTP::Request->new();
1498             $request->method('POST');
1499             $request->header( "SOAPAction" => "$WSRF::Constants::WSRL/Destroy" );
1500             $request->header( "Content-Length" => length $envelope );
1501             $request->content($envelope);
1502             }
1503              
1504             #check if a process to handle this message has been created
1505             my $SockPath = $WSRF::Constants::SOCKETS_DIRECTORY . '/' . $module;
1506             my ($resp);
1507             if ( !-S $SockPath ) {
1508              
1509             #create the file and fork the process
1510             print STDERR "$$ Creating a new Service $module\n";
1511             my $service = WSRF::Resource->new(
1512             module => $module,
1513             path => $RelativeDirectory,
1514             ID => $module
1515             );
1516             print STDERR "$$ Calling handle() on service\n";
1517             $service->handle("");
1518             print STDERR "$$ Connecting to Socket $SockPath\n";
1519             my $MyFH = IO::Socket::UNIX->new(
1520             Peer => $SockPath,
1521             Type => SOCK_STREAM,
1522             Timeout => 10
1523             )
1524             or die SOAP::Fault->faultcode("Container Fault")
1525             ->faultstring("Container Failure - Socket problem $!");
1526              
1527             #if using SSL add the extra information to the HTTP request
1528             if ( defined( $ENV{SSL_CLIENT_DN} ) ) {
1529             $request->header(
1530             'Client-SSL-Cert-Subject' => "$ENV{SSL_CLIENT_DN}" );
1531             $request->header(
1532             'Client-SSL-Cert-Issuer' => "$ENV{SSL_CLIENT_ISSUER}" );
1533             }
1534              
1535             #print "Ingoing HTTP>>>\n".$r->as_string()."\n<<
1536             my $out = print $MyFH ( $request->as_string() );
1537             if ( !defined($out) ) {
1538             print STDERR "$$ ERROR could not write to $MyFH\n";
1539             }
1540              
1541             #read the response from the Socket and turn it into a
1542             #HTTP::Response
1543             $resp = WSRF::Daemon::ResponseHandler($MyFH);
1544             $MyFH->close;
1545             print STDERR "$$ $Path Processing Finished\n";
1546             } else #no process to handle this message - we need to create one
1547             {
1548              
1549             #check the socket is up - send SOAP to socket
1550             my $MyFH = IO::Socket::UNIX->new(
1551             Peer => $SockPath,
1552             Type => SOCK_STREAM,
1553             Timeout => 10
1554             );
1555             if ( !$MyFH ) {
1556              
1557             #create the file and fork the process
1558             my $service = WSRF::Resource->new(
1559             module => $module,
1560             path => $RelativeDirectory,
1561             ID => $module
1562             );
1563             $service->handle();
1564              
1565             $MyFH = IO::Socket::UNIX->new(
1566             Peer => $SockPath,
1567             Type => SOCK_STREAM,
1568             Timeout => 10
1569             )
1570             or die SOAP::Fault->faultcode("Container Fault")
1571             ->faultstring("Container Failure - Socket problem $!");
1572             }
1573              
1574             #if using SSL add the extra information to the HTTP request
1575             if ( defined( $ENV{SSL_CLIENT_DN} ) ) {
1576             $request->header(
1577             'Client-SSL-Cert-Subject' => "$ENV{SSL_CLIENT_DN}" );
1578             $request->header(
1579             'Client-SSL-Cert-Issuer' => "$ENV{SSL_CLIENT_ISSUER}" );
1580             }
1581              
1582             my $out = print $MyFH ( $request->as_string() );
1583             if ( !defined($out) ) { print STDERR "ERROR\n" }
1584              
1585             #read the response from the Socket and turn it into a
1586             #HTTP::Response
1587             $resp = WSRF::Daemon::ResponseHandler($MyFH);
1588             $MyFH->close;
1589             print STDERR "$$ $Path Processing Finished\n";
1590             }
1591              
1592             # print STDERR "$$ >>>out>>>\n".$resp->content."\n<<
1593             if ( $GET || $PUT ) #Original Request was a GET
1594             {
1595             $resp = WSRF::Container::getProperties( $resp, $Directory, $module );
1596             }
1597              
1598             return $resp;
1599             }
1600              
1601             sub GetWSDL {
1602             my ($request) = @_;
1603              
1604             #get the path from the HTTP::Request
1605             my $uri = $request->uri;
1606             my $path = $request->uri->path;
1607             $path =~ s/^\/+//o;
1608             my $endpoint = $ENV{URL} . $path;
1609              
1610             #strip extra '/' at start of URL
1611             #$path =~ s/^\/+//o;
1612              
1613             #we only allow certain types of Path
1614             #alphanumeric, hypen, and forward-slash
1615             #BUG - this pattern is too restrictive
1616             if ( $path =~ /^([-\/\w]+)$/ ) {
1617             $path = $1;
1618             } else { #Bad Path
1619             return HTTP::Response->new(RC_FORBIDDEN);
1620             }
1621              
1622             my $LongPATH = $ENV{WSRF_MODULES} . "/" . $path . ".WSDL";
1623              
1624             # print STDERR "WSRF::Container::GetWSDL LongPATH=\"$LongPATH\"\n";
1625              
1626             #BUG - this could be done with reg-ex
1627             #split up path
1628             my @patharray = split( /\//, $path );
1629              
1630             #sometimes the path will have an ID at the end - pop it of
1631             pop @patharray;
1632              
1633             #rebuild path
1634             $path = join '/', @patharray;
1635             my $ShortPATH = $ENV{WSRF_MODULES} . "/" . $path . ".WSDL";
1636              
1637             # print STDERR "WSRF::Container::GetWSDL ShortPATH=\"$ShortPATH\"\n";
1638              
1639             # resp will be a HTTP::Response object
1640             # ReturnWSDL can throw exceptions, so we catch them
1641             my ($resp);
1642              
1643             #check if I can read the file
1644             if ( -r $LongPATH ) {
1645             eval { $resp = WSRF::WSDL::ReturnWSDL( $LongPATH, $endpoint ); };
1646             if ($@) {
1647             print STDERR
1648             "$$ WSRF::Container::GetWSDL could not retrieve WSDL from $LongPATH";
1649             $resp = HTTP::Response->new(RC_INTERNAL_SERVER_ERROR);
1650             }
1651             } elsif ( -r $ShortPATH ) {
1652             eval { $resp = WSRF::WSDL::ReturnWSDL( $ShortPATH, $endpoint ); };
1653             if ($@) {
1654             print STDERR
1655             "$$ WSRF::Container::GetWSDL could not retrieve WSDL from $ShortPATH";
1656             $resp = HTTP::Response->new(RC_INTERNAL_SERVER_ERROR);
1657             }
1658             } else {
1659             $resp = HTTP::Response->new(RC_NOT_FOUND);
1660             }
1661              
1662             return $resp;
1663             }
1664              
1665             #===============================================================================
1666             # WS_Address
1667             #
1668             # A class for holding and handling WS-Addressing EPRs
1669             #
1670             package WSRF::WS_Address;
1671              
1672             =pod
1673              
1674             =head1 WSRF::WS_Address
1675              
1676             Class to provide support for WS-Addressing
1677              
1678             =head2 METHODS
1679              
1680             =over
1681              
1682             =item new
1683              
1684             Creates a new WSRF::WS_Address object, takes either a SOM object or raw XML that
1685             contains a WS-Addressing Endpoint Reference and creates a WSRF::WS_Addressing
1686             object.
1687              
1688             =item from_envelope
1689              
1690             Creates a new WSRF::WS_Address object from a SOM representation of a SOAP Envelope
1691             that contains a WS-Addressing Endpoint Reference.
1692              
1693             =item MessageID
1694              
1695             If the WSRF::WS_Address is used to send a message to a service to client this function
1696             is used to create a unique identifier for the message. The identifier goes into
1697             the WS-Addressing SOAP Header MessageID.
1698              
1699             =item XML
1700              
1701             Returns the WS-Addressing Endpoint Reference as a string.
1702              
1703             =item serializeReferenceParameters
1704              
1705             Outputs the ReferenceParameters of the WS-Addressing Endpoint Reference.
1706              
1707             =back
1708              
1709             =cut
1710              
1711             sub new {
1712             my ( $self, $stuff ) = @_;
1713              
1714             my ( $address, $ref_params, $meta_data, $XML );
1715             if ( defined($stuff) ) {
1716              
1717             # we accept either a SOM or XML
1718             my $som =
1719             UNIVERSAL::isa( $stuff => 'SOAP::SOM' )
1720             ? $stuff
1721             : SOAP::Deserializer->new->deserialize($stuff);
1722              
1723             # $XML = WSRF::SimpleSerializer->new->serialize( $som->dataof("//{$WSRF::Constants::WSA}EndpointReference"));
1724              
1725             $address = $som->valueof("//{$WSRF::Constants::WSA}Address");
1726              
1727             #print STDERR "address= $address\n";
1728              
1729             if ( $som->match("//{$WSRF::Constants::WSA}ReferenceParameters") ) {
1730             my $i = 1;
1731             while (
1732             $som->match(
1733             "//{$WSRF::Constants::WSA}ReferenceParameters/[$i]")
1734             )
1735             {
1736             $ref_params .= WSRF::SimpleSerializer->new->serialize(
1737             $som->dataof(
1738             "//{$WSRF::Constants::WSA}ReferenceParameters/[$i]")
1739             );
1740             $i++;
1741             }
1742             }
1743              
1744             if ( $som->match("//{$WSRF::Constants::WSA}Metadata") ) {
1745             my $i = 1;
1746             while ( $som->match("//{$WSRF::Constants::WSA}Metadata/[$i]") ) {
1747             $meta_data .=
1748             WSRF::SimpleSerializer->new->serialize(
1749             $som->dataof("//{$WSRF::Constants::WSA}Metadata/[$i]") );
1750             $i++;
1751             }
1752             }
1753              
1754             }
1755              
1756             bless {
1757             _Address => $address,
1758             _ReferenceParameters => $ref_params,
1759             _Metadata => $meta_data,
1760             _XML => $XML
1761             }, $self;
1762              
1763             }
1764              
1765             sub from_envelope {
1766             my ( $self, $stuff ) = @_;
1767              
1768             return $self unless defined $stuff;
1769              
1770             my ( $address, $ref_params, $meta_data, $XML );
1771             my $som =
1772             UNIVERSAL::isa( $stuff => 'SOAP::SOM' )
1773             ? $stuff
1774             : SOAP::Deserializer->new->deserialize($stuff);
1775              
1776             $address =
1777             $som->match("//Body//EndpointReference/{$WSRF::Constants::WSA}Address")
1778             ? $som->valueof(
1779             "//Body//EndpointReference/{$WSRF::Constants::WSA}Address")
1780             : die
1781             "WS_Address::from_envlope No wsa:EndpointReference in Envelope Body\n";
1782              
1783             # print STDERR "address= $address\n";
1784              
1785             if (
1786             $som->match(
1787             "//Body//EndpointReference/{$WSRF::Constants::WSA}ReferenceParameters" )
1788             )
1789             {
1790             my $i = 1;
1791             while (
1792             $som->match( "//Body//EndpointReference/{$WSRF::Constants::WSA}ReferenceParameters/[$i]"
1793             )
1794             )
1795             {
1796             $ref_params .= WSRF::SimpleSerializer->new->serialize(
1797             $som->dataof(
1798             "//Body//EndpointReference/{$WSRF::Constants::WSA}ReferenceParameters/[$i]"
1799             )
1800             );
1801             $i++;
1802             }
1803             }
1804              
1805             if (
1806             $som->match(
1807             "//Body//EndpointReference/{$WSRF::Constants::WSA}Metadata")
1808             )
1809             {
1810             my $i = 1;
1811             while (
1812             $som->match(
1813             "//Body//EndpointReference{$WSRF::Constants::WSA}Metadata/[$i]")
1814             )
1815             {
1816             $meta_data .= WSRF::SimpleSerializer->new->serialize(
1817             $som->dataof(
1818             "//Body//EndpointRefernce/{$WSRF::Constants::WSA}Metadata/[$i]"
1819             )
1820             );
1821             $i++;
1822             }
1823             }
1824              
1825             bless {
1826             _Address => $address,
1827             _ReferenceParameters => $ref_params,
1828             _Metadata => $meta_data,
1829             _XML => $XML
1830             }, $self;
1831             }
1832              
1833             sub BEGIN {
1834             no strict 'refs';
1835              
1836             for my $method (qw(Address ReferenceParameters Metadata )) {
1837             my $field = '_' . $method;
1838             *$method = sub {
1839             my $self = shift;
1840             @_
1841             ? ( $self->{$field} = shift, return $self )
1842             : return $self->{$field};
1843             }
1844             }
1845             }
1846              
1847             sub MessageID {
1848             return join '', 'urn:www.sve.man.ac.uk-', int( rand 100000000000 ) + 1,
1849             gmtime;
1850             }
1851              
1852             sub XML {
1853             my $self = shift;
1854              
1855             if ( !defined $self->{_XML} ) {
1856             my $XML = '';
1857             $XML .= " ";
1858             $XML .= '' . $self->{_Address} . '';
1859             $XML .=
1860             $self->{_ReferenceParameters} ? $self->{_ReferenceParameters} : '';
1861             $XML .= $self->{_Metadata} ? $self->{_Metadata} : '';
1862             $XML .= '';
1863             $self->{_XML} = $XML;
1864             }
1865              
1866             return $self->{_XML};
1867             }
1868              
1869             sub serializeReferenceParameters {
1870             my $self = shift;
1871              
1872             if ( !defined( $self->{_ReferenceParameters} ) ) {
1873             return undef;
1874             }
1875              
1876             #need to wrap the ReferenceParameters to parse
1877             my $som =
1878             SOAP::Deserializer->new->deserialize(
1879             '<_foo>' . $self->{_ReferenceParameters} . '' );
1880              
1881             my $ans = "";
1882             my $i = 1;
1883             while ( $som->match("/[1]/[$i]") ) {
1884             my $data = $som->dataof("/[1]/[$i]");
1885             my %attr = %{ $data->attr };
1886             $attr{'wsa:isReferenceParameter'} = 'true';
1887             $data->attr( \%attr );
1888             $ans .= WSRF::SimpleSerializer->new->serialize($data);
1889             $i++;
1890             }
1891              
1892             return $ans;
1893              
1894             }
1895              
1896             #===============================================================================
1897             # WS-BaseFaults
1898             #
1899             # This function allows you to return a WS-BaseFault.
1900             # Simply call die_with_Fault to case your service to
1901             # through an exception.
1902             #
1903             # The function takes hash with the following:
1904             # OriginatorReference (where did the fault originally originate)
1905             # ErrorCode (some code number)
1906             # dialect (?)
1907             # Description (a description of the fault)
1908             # FaultCause (?)
1909             # For details check out the BasFault spec.
1910             #
1911             # I am not sure when you should throw a SOAP fault or a BaseFault
1912              
1913             package WSRF::BaseFaults;
1914              
1915             =pod
1916              
1917             =head1 WSRF::BaseFaults
1918              
1919             Class to support the WSRF BaseFaults specification
1920              
1921             =head2 METHODS
1922              
1923             =over
1924              
1925             =item die_with_Fault
1926              
1927             To return a WSRF BaseFault call die_with_Fault. die_with_Fault creates a SOAP fault
1928             then dies.
1929            
1930             die_with_Fault(
1931             OriginatorReference => $EPR,
1932             ErrorCode => $errorcode,
1933             dialect => $dialect,
1934             Description => $Description,
1935             FaultCause => $FaultCause
1936             );
1937            
1938             OriginatorReference is the WS-Addressing Endpoint Reference of the WS-Resource that the
1939             fault orignially came from. ErrorCode allows the WS-Resource to pass an error code
1940             back to the client. dialect is the dialect that the error code belongs to. Description
1941             provides a description of the fault and FaultCause provides the reason for the fault.
1942            
1943             =back
1944              
1945             =cut
1946              
1947             sub die_with_Fault {
1948             my %args = @_;
1949              
1950             my $fault = "";
1951             $fault .=
1952             ""
1953             . WSRF::Time::ConvertEpochTimeToString(time)
1954             . "";
1955              
1956             if ( defined( $args{OriginatorReference} ) ) {
1957             $fault .=
1958             ""
1959             . $args{OriginatorReference}
1960             . "";
1961             }
1962              
1963             #has the client defined an error code & dialect
1964             if ( defined( $args{ErrorCode} ) ) {
1965             if ( defined( $args{dialect} ) ) {
1966             $fault .=
1967             "
1968             . $args{dialect} . "\">"
1969             . $args{ErrorCode}
1970             . "";
1971             } else {
1972             $fault .=
1973             "" . $args{ErrorCode} . "";
1974             }
1975             }
1976              
1977             #has the client defined a Description
1978             if ( defined( $args{Description} ) ) {
1979             $fault .=
1980             "" . $args{Description} . "";
1981             }
1982              
1983             #has the client defined a BaseCause
1984             if ( defined( $args{FaultCause} ) ) {
1985             $fault .=
1986             "" . $args{FaultCause} . "";
1987             }
1988              
1989             $fault .= "";
1990              
1991             die SOAP::Fault->faultdetail($fault);
1992             }
1993              
1994             #===============================================================================
1995             # For WSRF services that are Session based - the process that calls
1996             # this function does all the work - it loads the module, does the operation
1997             # and returns the result.
1998             #
1999             package WSRF::Session;
2000              
2001             use SOAP::Transport::HTTP;
2002              
2003             use vars qw(@ISA);
2004              
2005             @ISA = qw(SOAP::Transport::HTTP::Server);
2006              
2007             sub DESTROY { SOAP::Trace::objects('()') }
2008              
2009             # constructor for the WSRF::Deamon object
2010             sub new {
2011             my $self = shift;
2012              
2013             unless ( ref $self ) {
2014             my $class = ref($self) || $self;
2015             $self = $class->SUPER::new(@_);
2016             SOAP::Trace::objects('()');
2017             }
2018             return $self;
2019             }
2020              
2021             sub handle {
2022             my $self = shift->new;
2023             $self->request( shift @_ );
2024             $self->SUPER::handle;
2025             return $self->response;
2026             }
2027              
2028             #===============================================================================
2029             # Similar to the SOAP::Transport::Daemon module except it listens to a UNIX
2030             # Domain Socket rather than an INET port
2031             #
2032             package WSRF::Daemon;
2033              
2034             use vars qw(@ISA);
2035              
2036             use HTTP::Status;
2037             use SOAP::Transport::HTTP;
2038              
2039             @ISA = qw(SOAP::Transport::HTTP::Server);
2040              
2041             sub DESTROY { SOAP::Trace::objects('()') }
2042              
2043             # constructor for the WSRF::Deamon object
2044             sub new {
2045             my $self = shift;
2046              
2047             unless ( ref $self ) {
2048             my $class = ref($self) || $self;
2049             $self = $class->SUPER::new(@_);
2050             SOAP::Trace::objects('()');
2051             }
2052             return $self;
2053             }
2054              
2055             # takes a socket and handles the info coming out of
2056             # it, passes it to the SOAP handler and then returns
2057             # the answer.
2058             sub handle {
2059             my $self = shift->new;
2060             my $Hdle = shift;
2061              
2062             while ( my $new_c = $Hdle->accept ) {
2063             my $req = $self->Requesthandler($new_c);
2064              
2065             #print "CHILD START::\n",$req->as_string, "CHILD END\n";
2066             $self->request($req);
2067             $self->SUPER::handle;
2068             my $resp = $self->response;
2069              
2070             #print "Return>>>\n".$resp->as_string."\n<<
2071             print $new_c ( $resp->as_string );
2072             }
2073             close($Hdle);
2074             }
2075              
2076             # A function that takes a HTTP message from a socket $Handle
2077             # and converts it to a HTTP::Request object
2078             # This HTTP handler is not very sophisticated but we know the
2079             # message has already been parsed in the pipeline
2080             sub Requesthandler {
2081             my ( $self, $Handle ) = @_;
2082             my $request = HTTP::Request->new();
2083             chomp( my $method = <$Handle> );
2084             my ( $Met, $URI, @blah ) = split( / /, $method );
2085             $request->method($Met);
2086             $request->uri($URI);
2087             my $SIZE = 0;
2088             LINE: while ( my $line = <$Handle> ) {
2089             last LINE if $line eq "\n";
2090             my ( $TAG, $VAL ) = split( /: /, $line, 2 );
2091             if ( $TAG eq "Content-Length" ) {
2092             $SIZE = $VAL;
2093             } elsif ( $TAG eq 'Client-SSL-Cert-Subject' ) {
2094             $ENV{SSL_CLIENT_DN} = $VAL;
2095             } elsif ( $TAG eq 'Client-SSL-Cert-Issuer' ) {
2096             $ENV{SSL_CLIENT_ISSUER} = $VAL;
2097             }
2098             $request->header( $TAG, $VAL );
2099             }
2100             $request->remove_header( 'TE', 'Connection', 'SOAPAction' );
2101             my $content = "";
2102              
2103             if ( $SIZE != 0 ) {
2104              
2105             FULL: while ( my $line = <$Handle> ) {
2106             $content .= $line;
2107             last FULL if length($content) >= $SIZE;
2108             }
2109             $request->content($content);
2110             }
2111              
2112             return $request;
2113             }
2114              
2115             #parses a HTTP message that comes from a Socket called $Handler
2116             #and returns a HTTP::Response object.
2117             #not much error checking but we know the response should be
2118             #good since we created it.
2119             sub ResponseHandler {
2120             my ($Handler) = @_;
2121             my $SIZE = 0;
2122             my $resp = HTTP::Response->new(RC_OK);
2123             chomp( my $result = <$Handler> );
2124              
2125             # $resp->message($result);
2126             LINE: while ( my $line = <$Handler> ) {
2127             last LINE if $line eq "\n";
2128             my ( $TAG, $VAL ) = split( /:/, $line, 2 );
2129             my $headers .= $TAG . " " . $VAL;
2130             if ( $TAG eq "Content-Length" ) {
2131             $SIZE = $VAL;
2132             }
2133             $resp->header( $TAG, $VAL );
2134             }
2135             my $content = "";
2136             FULL: while ( my $line = <$Handler> ) {
2137             $content .= $line;
2138             last FULL if length($content) >= $SIZE;
2139             }
2140             $resp->content($content);
2141             return $resp;
2142             }
2143              
2144             #===============================================================================
2145             # This class takes a WSDL file and changes the endpoint to match the
2146             # proper endpoint of the service
2147             #
2148             # BUG(FIXED) - "soap:address" is hardcoded, problem with XML::DOM not
2149             # understanding namespaces - FIXED
2150              
2151             package WSRF::WSDL;
2152              
2153             use XML::DOM;
2154             use HTTP::Status;
2155              
2156             sub ReturnWSDL {
2157             my ( $FILEPATH, $endpoint ) = @_;
2158              
2159             # print STDERR "WSDL File Path = $FILEPATH\n";
2160              
2161             if ( !-r $FILEPATH ) {
2162             print STDERR "ERROR WSDL file does not exist\n";
2163             return HTTP::Response->new(RC_NOT_FOUND);
2164             }
2165              
2166             #open file and read contents
2167             #print "Creating Response Object\n";
2168             #if we cannot open file we do NOT throw a SOAP fault
2169             #because we are not answering a SOAP request but a HTTP
2170             #GET request for the WSDL. This exception should be caught
2171             #by however has called this function.
2172             open FILE, "< $FILEPATH" or die "Could not open WSDL file";
2173              
2174             #read file
2175             my $wsdl = join "", ;
2176              
2177             #close file
2178             close FILE or die "Could not close WSDL file";
2179              
2180             #take a copy of the WSDL
2181             my $soap = $wsdl;
2182              
2183             #get the prefix for the http://schemas.xmlsoap.org/wsdl/soap/
2184             #namespace - hacky because XML::DOM does not like namespaces
2185             $soap =~ s/="http:\/\/schemas\.xmlsoap\.org\/wsdl\/soap\/"(.|\n)*//o;
2186             $soap =~ s/(.|\n)*xmlns://o;
2187              
2188             # print STDERR "Soap Namespace= ".$soap."\n";
2189              
2190             my $parser = new XML::DOM::Parser;
2191              
2192             # we used to just parse the file but the above hack screwed that
2193             # up - we just parse the string.
2194             # my $doc = $parser->parsefile($FILEPATH);
2195             my $doc = $parser->parse($wsdl);
2196             my $node = $doc->getElementsByTagName( $soap . ":address" );
2197              
2198             if ( !defined $node->item(0) ) {
2199             print STDERR "$$ ERROR in WSDL file - no " . $soap
2200             . ":address element\n";
2201             return HTTP::Response->new(RC_INTERNAL_SERVER_ERROR);
2202             }
2203              
2204             #These methods can throw exceptions - please catch them
2205             $node->item(0)->getAttributeNode("location")->setValue();
2206             $node->item(0)->getAttributeNode("location")->setValue($endpoint);
2207              
2208             my $ans = $doc->toString;
2209             $doc->dispose;
2210              
2211             my $resp = HTTP::Response->new(RC_OK);
2212             $resp->header( 'Content-Type' => 'text/xml' );
2213             $resp->content($ans);
2214             return $resp;
2215             }
2216              
2217             #===============================================================================
2218             #
2219             # Some helper functions that have been bundled together
2220             #
2221             package WSRF::GSutil;
2222              
2223             use IO::Socket;
2224              
2225             # function to generate a unique handle for the resource.
2226             # BUG - the name is misleading, GSH is a hangover from OGSI
2227             sub CalGSH_ID {
2228             my $num = int( rand 100000 ) + 1;
2229             my $gsh_id = join( '', gmtime ) . $num;
2230             return $gsh_id;
2231              
2232             }
2233              
2234             # create a WS-Address
2235             # BUG - we die without throwing proper SOAP faults
2236             # function takes a HASH with the following
2237             # path = relative path to module directory (relative to $ENV{WSRF_MODULES})
2238             # module = name of module file
2239             # ID = the WS-Resource identifier (can be created with CalGSH_ID above)
2240             sub createWSAddress {
2241             my %args = @_;
2242              
2243             my $URL = $ENV{'URL'};
2244             my $path = $args{path} || die "createWSAddress:: No Module Path\n";
2245             my $module = $args{module} || die "createWSAddress:: No Module\n";
2246             my $ID = $args{ID} || die "createWSAddress:: No ID\n";
2247              
2248             #strip .pm from module name if it is there
2249             $module =~ s/\.pm$//o;
2250              
2251             #strip leading /
2252             $path =~ s/^\/+//o;
2253              
2254             #strip trailing /
2255             $path =~ s/\/+$//o;
2256              
2257             #actual endpoint of service
2258             my $endpoint = $ENV{'URL'} . $path . '/' . $module . '/' . $ID;
2259              
2260             #here we create the WS-Addressing string
2261             my $response =
2262             "";
2263             $response .= "" . $endpoint . "";
2264             $response .= "";
2265              
2266             return $response;
2267             }
2268              
2269             # send some SOAP down the UNIX socket to the Resource, returns a SOM object
2270             sub SendSOAPToSocket {
2271             my ( $SocketAddress, $URI, $method, @params ) = @_;
2272              
2273             #print "SendSOAPToSocket: SocketAddress= $SocketAddress\n";
2274             #print "SendSOAPToSocket: URI= $URI\n";
2275             #print "SendSOAPToSocket: method= $method\n";
2276             #foreach my $param ( @params )
2277             #{
2278             # print "SendSOAPToSocket: params= $param\n";
2279             #}
2280              
2281             #create a SOAP message
2282             my $my_soap =
2283             SOAP::Lite->serializer->uri($URI)->envelope( method => $method, @params );
2284              
2285             #print "SendSOAPToSocket: my_soap= \n".$my_soap."\n";
2286              
2287             #create a HTTP message and put the SOAP into it
2288             my $request = HTTP::Request->new();
2289             $request->method('POST');
2290             $request->uri($URI);
2291             $request->push_header( 'Content_Length' => length($my_soap) );
2292             $request->push_header( 'Content-Type' => 'text/xml; charset=utf-8' );
2293             $request->content($my_soap);
2294              
2295             #BUG - have we actually checked the socket exists?
2296             #open the sockect
2297             my $rendev = $SocketAddress;
2298             my $MyFH = IO::Socket::UNIX->new(
2299             Peer => "$rendev",
2300             Type => SOCK_STREAM,
2301             Timeout => 10
2302             )
2303             or die SOAP::Fault->faultcode("Container Fault")
2304             ->faultstring("Container Failure - Socket problem $!");
2305              
2306             #print "SendSOAPToSocket sending \n".$request->as_string()."\n to $rendev\n";
2307             #send HTTP request with SOAP messgae down sockect
2308             my $out = print $MyFH ( $request->as_string() )
2309             or die SOAP::Fault->faultcode("Container Fault")
2310             ->faultstring("Container Failure - Socket problem $!");
2311              
2312             if ( !defined($out) ) {
2313             print STDERR
2314             "$$ ERROR - WSRF::GSutil::SendSOAPToSocket did not get response from Socket\n";
2315             die SOAP::Fault->faultcode("Container Fault")
2316             ->faultstring("Container Failure - Socket problem");
2317             }
2318              
2319             #resp is a HTTP::Response Object
2320             my $resp = WSRF::Daemon::ResponseHandler($MyFH);
2321              
2322             #$som is a WSRF::SOM object
2323             my $som = WSRF::Deserializer->deserialize( $resp->content );
2324              
2325             return $som;
2326             }
2327              
2328             #===============================================================================
2329             # Some functions to handle time - convert to/from epoch time/W3C time.
2330             # To handle times and compare them we convert all times in W3C format to
2331             # seconds since the epoch (ie. the number of seconds since 1970)
2332             #
2333             # This module provides some helper classes for doing this
2334             #
2335             package WSRF::Time;
2336              
2337             =pod
2338              
2339             =head1 WSRF::Time
2340              
2341             WSRF::Time provides two helper sub routines for converting a W3C time
2342             to seconds since the Epoch and vice versa.
2343              
2344             =head2 METHODS
2345              
2346             =over
2347              
2348             =item ConvertStringToEpochTime
2349              
2350             Converts a W3C date time string to the number of seconds since the UNIX Epoch.
2351              
2352             =item ConvertEpochTimeToString
2353              
2354             Converts a time in seconds since the UNIX Epoch to a W3C date time string.
2355              
2356             =back
2357              
2358             =cut
2359              
2360             =head2 VARIABLES
2361              
2362             =over
2363              
2364             =item EXPIRES_IN
2365              
2366             You can specify how long until an item expires with $WSRF::TIME::EXPIRES_IN. This variable defaults to 60 seconds.
2367              
2368             =back
2369              
2370             =cut
2371              
2372              
2373             use DateTime::Format::W3CDTF;
2374             use DateTime::Format::Epoch;
2375              
2376             # THE EXPIRES_IN variable, rather than hard code 60*60 seconds
2377             $WSRF::TIME::EXPIRES_IN = 60;
2378              
2379             # convert XML format Time string to time in seconds since epoch
2380             sub ConvertStringToEpochTime {
2381             my ($StringTime) = @_;
2382              
2383             #print "StringTime = $StringTime\n";
2384             #$f object used to convert W3CDTF TimeString to DateTime object
2385             my $f = DateTime::Format::W3CDTF->new;
2386              
2387             #$formatter used to convert DateTime object to seconds from epoch
2388             #we use the unix epoch here
2389             my $dt = DateTime->new( year => '1970', month => '1', day => '1' );
2390             my $formatter = DateTime::Format::Epoch->new( epoch => $dt );
2391              
2392             #convert $StringTime to a DateTime object
2393             #This will throw an exception if StringTime is not in the correct W3C format
2394             #BUG(fixed) with DateTime::Format::W3CDTF - does not
2395             #like subseconds - should patch DateTime::Format::W3CDTF
2396             #strip of the crap that DateTime::Format::W3CDTF does not understand
2397             $StringTime =~ s/\.\d+//;
2398              
2399             my $DateTimeObject = $f->parse_datetime($StringTime);
2400              
2401             #calc time in sec from epoch of $DateTimeObject
2402             my $EpochTime = $formatter->format_datetime($DateTimeObject);
2403              
2404             return $EpochTime;
2405             }
2406              
2407             # convert time in secs since Epoch to suitable XML format string
2408             sub ConvertEpochTimeToString {
2409             my ($EpochTime) = @_;
2410              
2411             #if no input time use now
2412             if ( !defined($EpochTime) ) {
2413             $EpochTime = time;
2414             }
2415              
2416             #use formatter to convert epoch time to W3CDTF TimeString
2417             my $dt = DateTime->new( year => 1970, month => 1, day => 1 );
2418             my $formatter = DateTime::Format::Epoch->new( epoch => $dt );
2419              
2420             my $DateTimeObject = $formatter->parse_datetime($EpochTime);
2421              
2422             my $f = DateTime::Format::W3CDTF->new;
2423              
2424             my $TimeString = $f->format_datetime($DateTimeObject);
2425              
2426             return $TimeString;
2427             }
2428              
2429             #===============================================================================
2430             # Class that allows us to create a new WSRF reource - uses a process to hold
2431             # the state of the resource. The handle function actually forks the process
2432             # to manage and hold the state of the Resource.
2433             #
2434             package WSRF::Resource;
2435              
2436             =pod
2437              
2438             =head1 WSRF::Resource
2439              
2440             A process based WS-Resource. The state of the WS-Resource is held in a
2441             process, the WSRF::Lite Container talks to the WS-Resource via a named UNIX
2442             socket.
2443              
2444             =head2 METHODS
2445              
2446             =over
2447              
2448             =item new
2449              
2450             Creates a new WSRF::Resource.
2451              
2452             my $resource = WSRF::Resource->new(
2453             module => Counter,
2454             path => /WSRF/Counter/Counter.pm,
2455             ID => M4325324563456,
2456             namespace => Counter
2457             );
2458              
2459             B is the name of the module that implements the WS-Resource,
2460             B is the path to the module relative to $ENV{WSRF_MODULES},
2461             B is the identifier for your WS-Resource, it will used as part of
2462             the URI in the WS-Addressing EPR. If you do not include the B one
2463             will be assigned for you. B is the namespace of the WSDL
2464             port for any non WSRF operations the WS-Resource supports, if no namespace
2465             is provided the name of the module will be used
2466              
2467             =item handle
2468              
2469             This subroutine should be called after B. It forks the process
2470             that is the WS-Resource. Anything passed to B is sent to the
2471             B method of the WS-Resource after it is created. The WS-Addressing
2472             EPR of the WS-Resource is available to the WS-Resource through $ENV{WSA}.
2473             B returns the WSRF identifier for the WS-Resource, this is used
2474             to form the URI used in the WS-Addressing EPR.
2475            
2476             =item ID
2477              
2478             ID returns the WSRF identifier for the WS-Resource.
2479              
2480             =back
2481              
2482             =cut
2483              
2484             use IO::Socket;
2485              
2486             use vars qw($AUTOLOAD);
2487              
2488             # new takes a HASH with
2489             # module - name of module
2490             # path - relative path to module (relative to $ENV{WSRF_MODULES}
2491             # ID - idnetifier for resource (if non is provided then it is calc'd
2492             # for you)
2493             # namepsace - for your service
2494             sub new {
2495             my ( $class, %args ) = @_;
2496              
2497             bless {
2498             _module => $args{module} || die("missing module name\n"),
2499             _path => $args{path} || die("missing module path\n"),
2500             _ID => $args{ID} || WSRF::GSutil::CalGSH_ID(),
2501             _namespace => $args{namespace}
2502             || ""
2503              
2504             }, $class;
2505             }
2506              
2507             sub ID {
2508             my ($self) = @_;
2509             return $self->{_ID};
2510             }
2511              
2512             # function that forks the process that manages the Resource - after
2513             # forking the init function is called on the Service. Allows user to
2514             # put an init funtion into their module which they know will be
2515             # called when the service is first created.
2516             sub handle {
2517             my ( $self, @Params ) = @_;
2518              
2519             my $ModulePath = $self->{_path};
2520             my $resourceID = $self->{_ID};
2521             my $ModuleName = $self->{_module};
2522             my $Namespace = $self->{_namespace};
2523              
2524             #strip .pm from end of module if is there
2525             $ModuleName =~ s/\.pm$//o;
2526              
2527             #print "handle Namespace = $Namespace\n";
2528             #$SIG{CHLD} = 'IGNORE';
2529              
2530             #my $URL = $ENV{'URL'};
2531             #chop $URL;
2532             my $location = $ENV{'URL'} . "$ModulePath";
2533              
2534             #fork the service off here
2535             if ( my $pid = fork ) {
2536              
2537             #parent process
2538             } elsif ( defined $pid ) { #child
2539             $SIG{ALRM} = sub { die "Alarm went off\n"; };
2540              
2541             #There may be an open connection to the world - need to close it
2542             if ( defined($WSRF::Constants::ExternSocket) ) {
2543             $WSRF::Constants::ExternSocket->close;
2544             undef $WSRF::Constants::ExternSocket;
2545             }
2546              
2547             #Store the WSA addres in a ENV variable so the
2548             #service can know its own EPR
2549             $ENV{WSA} =
2550             WSRF::GSutil::createWSAddress(
2551             module => $ModuleName,
2552             path => $ModulePath,
2553             ID => $resourceID
2554             );
2555              
2556             #the address of the socket were this resource is going to live
2557             my $rendivous = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $resourceID;
2558              
2559             #remove any file that is already there...
2560             if ( -e $rendivous ) {
2561             unlink "$rendivous"
2562             or die SOAP::Fault->faultcode("Container Fault")
2563             ->faultstring("Container Failure - Could not remove file");
2564             }
2565              
2566             print STDERR "$$ Created $resourceID rendezvous:: $rendivous\n";
2567             my $Handle = IO::Socket::UNIX->new(
2568             Local => "$rendivous",
2569             Type => SOCK_STREAM,
2570             Listen => SOMAXCONN
2571             )
2572             or die SOAP::Fault->faultcode("Container Fault")
2573             ->faultstring("Container Failure - Socket problem $!");
2574             print STDERR "$$ $resourceID Socket: $Handle\n";
2575              
2576             # redirect stderr/stdout to log directory
2577             open( STDOUT, "> " . $ENV{WSRF_MODULES} . "/logs/$resourceID.log" )
2578             or print STDERR "$$ WARNING: Could not open log file "
2579             . $ENV{WSRF_MODULES}
2580             . "/logs/$resourceID.log in WSRF::Resource::handle\n";
2581             open( STDERR, ">&STDOUT" );
2582              
2583             #my %namespaces = { 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceLifetime'
2584             # => "$ModuleName",
2585             # 'http://www.ibm.com/xmlns/stdwip/web-services/WS-ResourceProperties'
2586             # => "$ModuleName"
2587             # };
2588              
2589             #if ($Namespace ne "" )
2590             #{
2591             # $namespaces{$Namespace} = $ModuleName;
2592             #}
2593              
2594             #print "handle set $Namespace = ".$namespaces{$Namespace}."\n";
2595              
2596             #create a new service
2597              
2598             # BUG - if Namespace is not set
2599             # Now start the Resource in the process we have just created.
2600             %WSRF::WSRP::ResourceProperties = ();
2601             %WSRF::WSRP::PropertyNamespaceMap = ();
2602             %WSRF::WSRP::NotDeletable = ();
2603             %WSRF::WSRP::NotModifiable = ();
2604             %WSRF::WSRP::NotInsert = ();
2605             %WSRF::WSRP::Private = ();
2606              
2607             my $daemon =
2608             WSRF::Daemon->new()->serializer( WSRF::WSRFSerializer->new )
2609             ->deserializer( WSRF::Deserializer->new )
2610             ->dispatch_to( "$ENV{WSRF_MODULES}" . "/"
2611             . "$ModulePath" )->dispatch_with(
2612             {
2613             $WSRF::Constants::WSRL => "$ModuleName",
2614             $WSRF::Constants::WSRP => "$ModuleName",
2615             $WSRF::Constants::WSSG => "$ModuleName",
2616             $Namespace => $ModuleName
2617             }
2618             );
2619              
2620             #use eval to handle any time out
2621             eval { $daemon->handle($Handle); };
2622             print STDERR
2623             "$$ WSRF::Resource::handle caught exception: $@ - if it is \"Alarm went off\" then the WS-Resource's lifetime has expired";
2624             unlink($rendivous)
2625             or print STDERR
2626             "$$ WARNING: Could not remove $rendivous in WSRF::Resource::handle\n";
2627             print STDERR "$$ Resource Shutting Down\n";
2628              
2629             exit; #should never get here!!
2630             } else { #problem forking
2631             print STDERR
2632             "$$ ERROR: Could perform fork it start Resource in WSRF::Resource::handle\n";
2633             return "FAILURE";
2634             }
2635              
2636             #Parent Process Takes Over Here.
2637             # by default the factory will call init on the service it just
2638             # created - select is called to allow the child time to set up socket
2639             my $rend = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $resourceID;
2640              
2641             #sleep for 0.2 seconds
2642             select( undef, undef, undef, 0.2 );
2643              
2644             #resp from SendSOAPToSocket is a WSRF::SOM object - here we call init method
2645             my $resp =
2646             WSRF::GSutil::SendSOAPToSocket( $rend, $ModuleName, "init", @Params );
2647              
2648             #Check for a fault from the init method
2649             if ( $resp->fault ) {
2650             print STDERR "$$ ERROR: SOAP fault from init: "
2651             . $resp->faultstring
2652             . "\n in WSRF::Resource::handle\n";
2653             }
2654              
2655             return ( $resourceID, $resp );
2656             }
2657              
2658             # Once a WSRF::Resource is created with new and started using handle
2659             # method we can call operations on the Service using AUTOLOAD
2660             sub AUTOLOAD {
2661             my ( $self, @params ) = @_;
2662              
2663             #strip class name from method name (Conway p56)
2664             $AUTOLOAD =~ s/.*:://;
2665              
2666             my $rend = $WSRF::Constants::SOCKETS_DIRECTORY . "/" . $self->ID();
2667              
2668             if ( $AUTOLOAD eq "DESTROY" ) {
2669              
2670             # print STDERR "Attempt to DESTROY ".$self->ID()."\n";
2671             return;
2672             }
2673              
2674             #$resp is WSRF::SOM object
2675             my $resp =
2676             WSRF::GSutil::SendSOAPToSocket( $rend, $self->{_module}, $AUTOLOAD,
2677             @params );
2678              
2679             return $resp;
2680             }
2681              
2682             #===============================================================================
2683             # This is the module that provides file locking for us - when an object of this
2684             # class is created a lock file is created. The lock file is automatically
2685             # removed when the object is destroyed. We could use fcntl to do this - I
2686             # decided to actually create lock files so a user could manually create and
2687             # remove lock files themselves.
2688             #
2689             # This`works by creating/checking for/removing a directory
2690             #
2691             # BUG - This is not very sophistcated. We use this class in WSRF::File
2692              
2693             =pod
2694              
2695             =head1 WSRF::FileLock
2696              
2697             Simple class to provide file locking. It is possible to use fcntl to
2698             do file locking but some file systems don't support it. WSRF::FileLock is
2699             used to by the file based WS-Resources in WSRF::Lite to prevent concurrent
2700             access to the WS-Resource by more than one client.
2701              
2702             =head2 METHODS
2703              
2704             =over
2705              
2706             =item new
2707              
2708             B takes a name and tries to create a directory with that name,
2709             if there is already a directory with that name it will sleep for half
2710             a second and retry. When the directory is created a new WSRF::FileLock
2711             object is returned, then the object goes out of scope the directory is
2712             removed.
2713              
2714             my $lock = WSRF::FileLock->new($somefilelocation);
2715              
2716             =back
2717            
2718             =cut
2719              
2720             package WSRF::FileLock;
2721              
2722             #Provides a simple locking tool -
2723              
2724             sub new {
2725             my ( $self, $file ) = @_;
2726              
2727             #$file is the name of the directory to make - the lock
2728             until ( mkdir $file ) {
2729             select( undef, undef, undef, 0.5 );
2730             print STDERR "$$ Lock on $file\n";
2731             }
2732              
2733             bless { _file => $file }, $self;
2734             }
2735              
2736             sub DESTROY {
2737             my ($self) = @_;
2738             print STDERR "$$ Removing Lock File ";
2739             print STDERR $self->{_file} . "\n";
2740             if ( -d $self->{_file} ) {
2741             rmdir $self->{_file}
2742             or die SOAP::Fault->faultcode("Container Fault")
2743             ->faultstring( "Could not remove lock file " . $self->{_file} );
2744             }
2745             print STDERR "$$ Lock file " . $self->{_file} . " removed\n";
2746             }
2747              
2748             #===============================================================================
2749             # This module supports writing all the resource properties of a Resource to a
2750             # file. Allows the state of the resource to be stored in a file between calls
2751             # to the Resource. Relies on the Serialisers provided by SOAP::Lite to do the
2752             # work
2753             #
2754             # We could use other Perl modules to do this (eg. the Dumper module) - I
2755             # decided to reuse stuff from SOAP::Lite
2756             #
2757             package WSRF::File;
2758             use Storable qw(lock_store lock_nstore lock_retrieve);
2759             use Safe;
2760              
2761             =pod
2762              
2763             =head1 WSRF::File
2764              
2765             This class provides support for serializing the state of a WS-Resource to
2766             a file.
2767              
2768             =head2 METHODS
2769              
2770             =over
2771              
2772             =item new
2773              
2774             Takes a WSRF::SOM envelope, gets the ID of the WS-Resource and then loads
2775             the properties of the WS-Resource into the WSRF::WSRP::ResourceProperties
2776             hash. B locks the WS-Resource so that no other client can access
2777             the WS-Resource while this clients request is being processed. When the
2778             WSRF::File object runs out of scope and is destroyed the lock is removed.
2779              
2780             =item ID
2781              
2782             Returns the WSRF::Lite indentifier of the WS-Resource.
2783              
2784             =item path
2785              
2786             Filename of the file that holds the state of the WS-Resource.
2787              
2788            
2789             =item toFile
2790              
2791             Serializes the WSRF::WSRP::ResourceProperties hash back to the file. If the
2792             properties of the WS-Resource have been modified this should be called before
2793             the WSRF::File object goes out of scope.
2794              
2795             =back
2796              
2797             =cut
2798              
2799             # this is made a private function - Resources use files to store their state
2800             # inherit this module along the way, we do not want remote clients to be
2801             # able to invoke this function so we make it private. (SOAP::Lite will not
2802             # allow you to invoke private functions in a module remotely)
2803             # This function takes a SOM object and puts the data from the SOM object
2804             # into the ResourceProperty HASH of the Resource, the resource developer
2805             # only has to program using the hash.
2806             #
2807             my $Insert = sub {
2808             my ($b) = @_;
2809              
2810             #get the name of the property
2811             my $name = $b->dataof()->name;
2812              
2813             #print "insert name= ".$name."\n";
2814              
2815             #check there is no user defined function
2816             #for inserting this property
2817             if ( defined( $WSRF::WSRP::InsertMap{$name} ) ) {
2818             $WSRF::WSRP::InsertMap{$name}->($b);
2819             return;
2820             }
2821              
2822             #get the value of the property
2823             my $value = $b->dataof()->value;
2824              
2825             #print "insert $name value= $value\n";
2826              
2827             #check the property actually exists
2828             if ( defined( $WSRF::WSRP::ResourceProperties{$name} ) ) {
2829              
2830             #check the type of the property (scalar|array)
2831             my $type = ref( $WSRF::WSRP::ResourceProperties{$name} );
2832             if ( $type eq "" ) #scalar
2833             {
2834             $WSRF::WSRP::ResourceProperties{$name} = $value;
2835             } elsif ( $type eq "ARRAY" ) #array
2836             {
2837              
2838             #add property to array
2839             push( @{ $WSRF::WSRP::ResourceProperties{$name} }, $value );
2840             } elsif ( $type ne "CODE" ) {
2841             print STDERR
2842             "$$ ERROR: Property $name is a $type, only ARRAY,SCALAR and CODE are supported in WSRF::File::Insert\n";
2843             }
2844             } else {
2845             print STDERR
2846             "$$ ERROR: Attempting to load property from file that has not been declared in WSRF::File::Insert\n";
2847             }
2848              
2849             return;
2850             };
2851              
2852             # Takes a SOAP::SOM envelope, gets the ID of the Resource and then loads the
2853             # properties into the WSRF::WSRP::ResouceProperties hash for the service. Uses
2854             # the Insert function to load the properties into the hash. Also creates a
2855             # lock file - lock file is removed in the DESTROY operation when the
2856             # WSRF::File object is destroyed
2857             #
2858             sub new {
2859             my ( $class, $envelope ) = @_;
2860              
2861             my $address = $envelope->headerof("//{$WSRF::Constants::WSA}To");
2862             if ( defined $address ) {
2863             $address = $envelope->headerof("//{$WSRF::Constants::WSA}To")->value;
2864             } else {
2865             print STDERR "ERROR: No ResourceID in the SOAP Header\n";
2866             die SOAP::Fault->faultcode("No WS-Resource Identifier")
2867             ->faultstring("No WS-Resource identifier in SOAP Header");
2868             }
2869              
2870             my @PathArray = split( /\//, $address );
2871             my $ID = pop @PathArray;
2872              
2873             #my $ID = $ENV{ID};
2874              
2875             #check the ID is safe - we do not accept dots,
2876             #all paths will be relative to $ENV{WRF_MODULES}
2877             #only allow alphanumeric, underscore and hyphen
2878             if ( $ID =~ /^([-\w]+)$/ ) {
2879             $ID = $1;
2880             } else {
2881             print STDERR "$$ WSRF::File ERROR: Bad $ID for WS-Resource\n";
2882             die SOAP::Fault->faultcode("Badly formed WS-Resource Identifier")
2883             ->faultstring("Badly formed WS-Resource Identifier: $ID");
2884             }
2885              
2886             my $ID_clipped = $ID;
2887              
2888             #ID can be of the form 1341-4565, we use this form to all multiple
2889             #WS-Resources to share the same state, the state is in the file
2890             #1341 - we use this with ServiceGroup/ServiceGroupEntry
2891             $ID_clipped =~ s/-\w*//o;
2892              
2893             my $path = $WSRF::Constants::Data . $ID_clipped;
2894              
2895             if ( !( -e $path ) ) {
2896             print STDERR "$$ ERROR: No Resource $path\n";
2897             die SOAP::Fault->faultcode("No WS-Resource")
2898             ->faultstring("No WS-Resource with Identifer $ID");
2899             }
2900              
2901             #The address of the lock file
2902             my $lock = $path . ".lock";
2903              
2904             #Acquire a lock for the file
2905             my $Lock = WSRF::FileLock->new($lock);
2906              
2907             # open FILE, "$path" or die SOAP::Fault->faultcode("Container Failure")
2908             # ->faultstring("Container Failure: Could not open WS-Resource file");
2909             # #read the XML from the file
2910             # my $XML = join "", ;
2911              
2912             # close FILE or die SOAP::Fault->faultcode("Container Failure")
2913             # ->faultstring("Container Failure: Could not close WS-Resource file");
2914              
2915             # convert the XML into a SOM object. (the SOM object will still allow access
2916             # to the raw XML)
2917             # my $som = WSRF::Deserializer->deserialize($XML);
2918              
2919             #iterate through the ResourceProperties and call insert for each one
2920             # my $k = 1;
2921             # while( $som->match("//ResourceProperties/[$k]") )
2922             # {
2923             #print "SOM name= ".$som->dataof("//ResourceProperties/[$k]")->name()."\n";
2924             # $Insert->( $som->match("//ResourceProperties/[$k]") );
2925             # $k++;
2926             # }
2927              
2928             # my $safe = new Safe;
2929             # $safe->permit(qw(:default require));
2930             # local $Storable::Eval = sub { $safe->reval($_[0]) };
2931             my $hashref = Storable::lock_retrieve($path);
2932              
2933             # print "Thawing...\n";
2934             # foreach my $key (keys %$hashref)
2935             # {
2936             # $WSRF::WSRP::ResourceProperties{$key} = $hashref->{$key};
2937             # print $key.": ".$hashref->{$key}."\n";
2938             # }
2939             #print "CurrentTime = ".${$hashref->{CurrentTime}}."\n";
2940              
2941             %WSRF::WSRP::ResourceProperties =
2942             ( %WSRF::WSRP::ResourceProperties, %{ $hashref->{Properties} } );
2943              
2944             %WSRF::WSRP::Private = ( %WSRF::WSRP::Private, %{ $hashref->{Private} } );
2945              
2946             #check that the resource is still alive - if TT time is not
2947             #set then TT is infinity
2948             if ( defined( $WSRF::WSRP::ResourceProperties{'TerminationTime'} )
2949             && ( $WSRF::WSRP::ResourceProperties{'TerminationTime'} ne "" ) )
2950             {
2951             if (
2952             WSRF::Time::ConvertStringToEpochTime(
2953             $WSRF::WSRP::ResourceProperties{'TerminationTime'}
2954             ) < time
2955             )
2956             {
2957             print STDERR "$$ Resource $ID expired\n";
2958             unlink $path
2959             or die SOAP::Fault->faultcode("Container Failure")
2960             ->faultstring("Container Failure: Could not remove file");
2961             rmdir $lock
2962             or die SOAP::Fault->faultcode("Container Failure")
2963             ->faultstring("Container Failure: Could not remove lock file");
2964             die SOAP::Fault->faultcode("No such Resource")
2965             ->faultstring("No such Resource $ID - Lifetime expired");
2966             }
2967             }
2968              
2969             bless {
2970             _ID => $ID,
2971             _path => $path,
2972             _lock => $Lock
2973             }, $class;
2974             }
2975              
2976             sub ID {
2977             my ($self) = @_;
2978             return $self->{_ID};
2979             }
2980              
2981             sub path {
2982             my ($self) = @_;
2983             return $self->{_path};
2984             }
2985              
2986             # Send the ResourceProperties to a file
2987             sub toFile {
2988             my $class = shift;
2989              
2990             my $filename =
2991             ref($class)
2992             ? $class->{_path}
2993             : $WSRF::Constants::Data . $class;
2994              
2995             # open FILE, ">$filename" or die SOAP::Fault->faultcode("Container Failure")
2996             # ->faultstring("Container Failure: Could open file");
2997              
2998             # print ">>>>AFTER>>>>\n".WSRF::WSRP::xmlizeProperties()."\n<<<<<<<<<<<<\n\n";
2999              
3000             # print FILE WSRF::WSRP::xmlizeProperties();
3001              
3002             # close FILE or die SOAP::Fault->faultcode("Container Failure")
3003             # ->faultstring("Container Failure: Could close file");
3004             # my $safe = new Safe;
3005             # $safe->permit(qw(:default require));
3006             # local $Storable::Eval = sub { $safe->reval($_[0]) };
3007             # local $Storable::Deparse = 1;
3008              
3009             my %tmpPrivate = (%WSRF::WSRP::Private);
3010              
3011             #should use map?
3012             foreach my $key ( keys %tmpPrivate ) {
3013             if ( ref( $tmpPrivate{$key} ) eq "CODE" ) {
3014             delete $tmpPrivate{$key};
3015             }
3016             }
3017              
3018             #take a copy of the ResourceProperties to copy to file
3019             my %tmphash = (%WSRF::WSRP::ResourceProperties);
3020             foreach my $key ( keys %tmphash ) {
3021             if ( ref( $tmphash{$key} ) eq "CODE" ) {
3022             delete $tmphash{$key};
3023             }
3024             }
3025              
3026             my %tmpStore = ( Properties => \%tmphash, Private => \%tmpPrivate );
3027              
3028             local $Storable::forgive_me = "TRUE";
3029             lock_store \%tmpStore, $filename;
3030              
3031             return;
3032             }
3033              
3034             sub unlock {
3035             my ($self) = @_;
3036             my $Lock = $self->{_lock};
3037             $Lock->DESTROY();
3038             }
3039              
3040             #===============================================================================
3041             # header function creates a SOAP::Header that should be included
3042             # in the response to the client. Handles the WS-Address stuff.
3043             # Takes the original envelope and creates a Header from it -
3044             # the second paramter will be stuffed into the Header so must
3045             # be XML
3046             #
3047             # BUG This should be better automated - probably in the SOAP serializer,
3048             # not sure how because we need to remember the MessageID
3049             package WSRF::Header;
3050              
3051             =pod
3052              
3053             =head1 WSRF::Header
3054              
3055             WSRF::Header provides one helper routine B
3056              
3057             =head2 METHODS
3058              
3059             =over
3060              
3061             =item header
3062              
3063             This subroutine takes a WSRF::SOM envelope and creates the appropriate
3064             SOAP Headers for the response including the required WS-Addressing SOAP
3065             headers.
3066            
3067            
3068             sub foo {
3069             my $envelope = pop @_;
3070            
3071             return WSRF::Header::header($envelope);
3072             }
3073            
3074             =back
3075              
3076             =cut
3077              
3078             sub header {
3079             my ( $envelope, $anythingelse ) = @_;
3080              
3081             #To create the wsa:Action we must find the operation name
3082             #and its namespace
3083             my $data = $envelope->match('/Envelope/Body/[1]')->dataof;
3084             my $method = $data->name;
3085             my $uri = $data->uri;
3086             my $Action = $uri . "/" . $method . "Response";
3087             my $myHeader = "" . $Action . "";
3088              
3089             #We only use "anonoymous" for wsa:To
3090             $myHeader .= "$WSRF::Constants::WSA_ANON";
3091              
3092             #We use our endpoint to create the wsa:From - the endpoint
3093             #is an ENV variable
3094             if ( $envelope->match("/Envelope/Header/{$WSRF::Constants::WSA}To") ) {
3095             my $from =
3096             $envelope->valueof("/Envelope/Header/{$WSRF::Constants::WSA}To");
3097             $myHeader .=
3098             "$from";
3099             }
3100              
3101             $myHeader .=
3102             ""
3103             . WSRF::WS_Address::MessageID()
3104             . "";
3105              
3106             #check for wsa:MessageID in envelope - if it is set use it to
3107             #create a wsa:RelatesTo element
3108             my $messageID = $envelope->headerof("//{$WSRF::Constants::WSA}MessageID");
3109             if ( defined $messageID ) {
3110             $messageID =
3111             $envelope->headerof("//{$WSRF::Constants::WSA}MessageID")->value;
3112             $myHeader .=
3113             ""
3114             . $messageID
3115             . "";
3116             }
3117              
3118             #append anything else the user has given us
3119             $myHeader .= $anythingelse;
3120              
3121             #create the SOAP::Header object and return to client
3122             return SOAP::Header->value($myHeader)->type('xml');
3123             }
3124              
3125             #===============================================================================
3126             # Base class for the process based WSRF services - a Service can inherit from
3127             # this class to pick up GetResourceProperty, GetMultiResourceProperties and
3128             # SetResourceProperty operations.
3129              
3130             package WSRF::WSRP;
3131              
3132             =pod
3133              
3134             =head1 WSRF::WSRP
3135              
3136             Provides support for WSRF ResourceProperties, the properties of the WS-Resource
3137             are stored in a hash called %WSRF::WSRP::ResourceProperties.
3138              
3139             =head2 METHODS
3140              
3141             =over
3142              
3143             =item xmlizeProperties
3144              
3145             =item GetResourcePropertyDocument
3146              
3147             =item GetResourceProperty
3148              
3149             =item GetMultipleResourceProperties
3150              
3151             =item SetResourceProperties
3152              
3153             =item InsertResourceProperties
3154              
3155             =item UpdateResourceProperties
3156              
3157             =item DeleteResourceProperties
3158              
3159             =back
3160              
3161             =cut
3162              
3163             use vars qw(@ISA);
3164              
3165             # we inherit this to gain access to the envelope - see SOAP::Lite
3166             @ISA = qw(SOAP::Server::Parameters);
3167              
3168             # Hash to store resource properties - we make this effectively
3169             # a globe variable
3170             %WSRF::WSRP::ResourceProperties = ();
3171              
3172             # Hash stores the prefix for the resource property
3173             # eg CurrentTime will use the prefix wsrl, the
3174             # map between tthe prefix and the namespace is
3175             # elsewhere
3176             %WSRF::WSRP::PropertyNamespaceMap = ();
3177              
3178             # Hash that maps a property and the fuction that
3179             # should be called when aan attempt is made to
3180             # insert that property. Simple properties are
3181             # handled by default.
3182             %WSRF::WSRP::InsertMap = ();
3183              
3184             # Hash that maps property to function that should
3185             # be used to delete it - simple properties are
3186             # handled by default
3187             %WSRF::WSRP::DeleteMap = ();
3188              
3189             # Hash to define which properties can be "nil" - by
3190             # default properties can not be nil.
3191             %WSRF::WSRP::Nillable = ();
3192              
3193             # Hash to define which properties cannot be Deleted
3194             %WSRF::WSRP::NotDeletable = ();
3195              
3196             # Hash to define which properties cannot be changed
3197             %WSRF::WSRP::NotModifiable = ();
3198              
3199             # Hash to define which properties cannot be inserted
3200             %WSRF::WSRP::NotInsert = ();
3201              
3202             # serach for a resource property - this is used by getResourceProperty
3203             # and getMultipleResourceProperties. Takes the ID of the resource
3204             # and the name of the rsource.
3205             #
3206             # BUG - we do not handle namespaces of property!!
3207             sub searchResourceProperty {
3208             my $longsearch = shift @_;
3209              
3210             #dump the namespace of property
3211             my ( $junk, $search );
3212             if ( $longsearch =~ m/:/ ) {
3213             ( $junk, $search ) = split /:/, $longsearch;
3214             } else {
3215             $search = $longsearch;
3216             }
3217              
3218             #default result!!
3219             my $ans = "";
3220              
3221             #print "Printing keys\n";
3222             #foreach my $key ( keys %WSRF::WSRP::ResourceProperties)
3223             #{
3224             # print " key= <$key>\n";
3225             #}
3226              
3227             #Check Resource property exists, if it does it can either
3228             #be a simple scalar, an array or a function.
3229             if ( defined( $WSRF::WSRP::ResourceProperties{$search} ) ) {
3230              
3231             #get type of property
3232             my $type = ref( $WSRF::WSRP::ResourceProperties{$search} );
3233             if ( $type eq "" ) # if scalar
3234             {
3235              
3236             #check if property set
3237             if ( $WSRF::WSRP::ResourceProperties{$search} ne "" ) {
3238             $ans .= "<"
3239             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3240             . ":$search ";
3241              
3242             #do we need to add a namespace for this property
3243             my $ns =
3244             defined(
3245             $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
3246             ? " xmlns:"
3247             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
3248             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
3249             . "\">"
3250             : ">";
3251             $ans .= $ns
3252             . $WSRF::WSRP::ResourceProperties{$search} . "
3253             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3254             . ":$search>";
3255             }
3256              
3257             #property NOT set - is it nillable?
3258             elsif ( $WSRF::WSRP::ResourceProperties{$search} eq ""
3259             && defined( $WSRF::WSRP::Nillable{$search} ) )
3260             {
3261             $ans .= "<"
3262             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3263             . ":$search";
3264             my $ns =
3265             defined(
3266             $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
3267             ? " xmlns:"
3268             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
3269             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
3270             . "\""
3271             : " ";
3272             $ans .= $ns . " xsi:nil=\"true\"/>";
3273             }
3274             }
3275              
3276             #property is array of things
3277             elsif ( $type eq "ARRAY" ) {
3278              
3279             #check array is not empty - and property is nillable
3280             if ( !@{ $WSRF::WSRP::ResourceProperties{$search} }
3281             && defined( $WSRF::WSRP::Nillable{$search} ) )
3282             {
3283             $ans .= "<"
3284             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3285             . ":$search";
3286             my $ns =
3287             defined(
3288             $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
3289             ? " xmlns:"
3290             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
3291             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
3292             . "\""
3293             : " ";
3294             $ans .= $ns . " xsi:nil=\"true\"/>";
3295             }
3296              
3297             #loop over array building result
3298             else {
3299             foreach
3300             my $entry ( @{ $WSRF::WSRP::ResourceProperties{$search} } )
3301             {
3302             $ans .= "<"
3303             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3304             . ":$search";
3305              
3306             #do we need to add a namespace for this property
3307             my $ns =
3308             defined( $WSRF::WSRP::PropertyNamespaceMap->{$search}
3309             {namespace} )
3310             ? " xmlns:"
3311             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3312             . "=\""
3313             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace}
3314             . "\">"
3315             : ">";
3316             $ans .=
3317             $ns . $entry . "
3318             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3319             . ":$search>";
3320             }
3321             }
3322             }
3323              
3324             #property is a subroutine - call it to get result
3325             #example of this is CurrentTime
3326             elsif ( $type eq "CODE" ) {
3327             $ans .= $WSRF::WSRP::ResourceProperties{$search}->();
3328             }
3329              
3330             #Some type we do not understand yet eg. Hash - attempt to serialize it anyway
3331             else {
3332             my $serializer = WSRF::SimpleSerializer->new();
3333             $ans .= "<"
3334             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3335             . ":$search";
3336              
3337             #do we need to add a namespace for this property
3338             my $ns =
3339             defined( $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
3340             ? " xmlns:"
3341             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
3342             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} . "\">"
3343             : ">";
3344              
3345             $ans .= $ns
3346             . $serializer->serialize(
3347             $WSRF::WSRP::ResourceProperties{$search} )
3348             . "
3349             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
3350             . ":$search>";
3351              
3352             # die SOAP::Fault->faultcode("WSRF::Lite Failure")
3353             # ->faultstring("Could not understand type: $type");
3354             }
3355              
3356             }
3357              
3358             return $ans;
3359             }
3360              
3361             # This creates XML with all the ResourceProperties in it - we can then
3362             # use the XPath query from queryResourceProperty on it.
3363             # BUG (FIXED(?) But we have not written queryResourceProperty yet - its a
3364             # bad idea anyway so lets not worry about it.
3365             #
3366             sub xmlizeProperties {
3367              
3368             #my $ans = "";
3369             my $ans =
3370             "
3371             . " xmlns:wsrp=\"$WSRF::Constants::WSRP\" "
3372             . " xmlns:wsrl=\"$WSRF::Constants::WSRL\" "
3373             . " xmlns:wssg=\"$WSRF::Constants::WSSG\" "
3374             . " xmlns:wsa=\"$WSRF::Constants::WSA\" "
3375             . " xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\" "
3376             . " xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">";
3377              
3378             foreach my $key ( keys %WSRF::WSRP::ResourceProperties ) {
3379             $ans .= searchResourceProperty($key);
3380             }
3381              
3382             $ans .= "";
3383              
3384             return $ans;
3385             }
3386              
3387             sub GetResourcePropertyDocument {
3388             my $envelope = pop @_;
3389             my $xml = xmlizeProperties();
3390             return WSRF::Header::header($envelope),
3391             SOAP::Data->value($xml)->type('xml');
3392             }
3393              
3394             # delete property
3395             # BUG we do not handle namespaces
3396             my $mydelete = sub {
3397             my ($name) = @_;
3398              
3399             #strip namespace
3400             $name =~ s/\w*://o;
3401              
3402             # #check for user defined delete function for this property
3403             if ( defined( $WSRF::WSRP::DeleteMap{$name} ) ) {
3404             $WSRF::WSRP::DeleteMap{$name}->();
3405             return;
3406             }
3407              
3408             #check we are allowed to delete this function
3409             # if( defined( $WSRF::WSRP::NotDeletable{$name} ) )
3410             # {
3411             # die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
3412             # ->faultstring("Could not delete $name");
3413             # }
3414              
3415             #check property exists
3416             if ( defined( $WSRF::WSRP::ResourceProperties{$name} ) ) {
3417              
3418             #check type either array or scalar
3419             my $type = ref( $WSRF::WSRP::ResourceProperties{$name} );
3420             if ( $type eq "" ) #scalar
3421             {
3422             $WSRF::WSRP::ResourceProperties{$name} = "";
3423             } elsif ( $type eq "ARRAY" ) # array
3424             {
3425              
3426             #set contents to nothing
3427             @{ $WSRF::WSRP::ResourceProperties{$name} } = ();
3428             } else {
3429             die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
3430             ->faultstring("Could not delete $name");
3431             }
3432             } else {
3433             die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
3434             ->faultstring("No ResourceProperty: $name");
3435             }
3436             return;
3437             };
3438              
3439             # insert property - this function is used by the Insert and Update
3440             # in the SetResourceProperty operation. This operation takes
3441             # the ID of the resource and a SOAP::SOM object that has been set
3442             # at the property that should be inserted
3443             # Only one property can be inserted at a time using the function -
3444             # SetResourceProperty of course loops over it
3445             my $insert = sub {
3446             my ($b) = @_;
3447              
3448             #get the name of the property
3449             my $name = $b->dataof()->name;
3450              
3451             # #check there is no user defined function
3452             # #for inserting this property
3453             if ( defined( $WSRF::WSRP::InsertMap{$name} ) ) {
3454             $WSRF::WSRP::InsertMap{$name}->($b);
3455             return;
3456             }
3457              
3458             #check this property can be changed
3459             # if( defined( $WSRF::WSRP::NotModifiable{$name} ))
3460             # {
3461             # die SOAP::Fault->faultcode("setResourceproperty: Insert Failure")
3462             # ->faultstring("Could not insert $name");
3463             # }
3464              
3465             #get the value of the property
3466             my $value = $b->dataof()->value;
3467              
3468             #check the property actually exists
3469             if ( defined( $WSRF::WSRP::ResourceProperties{$name} ) ) {
3470              
3471             #check the type of the property (scalar|array)
3472             my $type = ref( $WSRF::WSRP::ResourceProperties{$name} );
3473             if ( $type eq "" ) #scalar
3474             {
3475             $WSRF::WSRP::ResourceProperties{$name} = $value;
3476             } elsif ( $type eq "ARRAY" ) #array
3477             {
3478              
3479             #add property to array
3480             push( @{ $WSRF::WSRP::ResourceProperties{$name} }, $value );
3481             } else #perhaps subroutine?
3482             {
3483             die SOAP::Fault->faultcode("setResourceproperty: Insert Failure")
3484             ->faultstring("Could not insert $name");
3485             }
3486             } else {
3487             die SOAP::Fault->faultcode(
3488             "setResourceproperty: No such ResourceProperty")
3489             ->faultstring("$name is not a ResourceProperty of this WS-Resource");
3490             }
3491             return;
3492             };
3493              
3494             # we provide an init method in case the service writer does bother - this
3495             # will be called whenever the WS-Resource is created
3496             sub init { return; }
3497              
3498             # wsrp GetResourceProperty
3499             sub GetResourceProperty {
3500             my $envelope = pop @_;
3501              
3502             #print "XML>>>\n".xmlizeProperties()."\n<<
3503              
3504             #search through envelope to the GetResourceProperty bit
3505             #and get the resource property name
3506             my $search = $envelope->valueof('//GetResourceProperty/');
3507              
3508             #print "GetResourceProperty = $search\n";
3509             my $ans = searchResourceProperty($search);
3510              
3511             #print "GetResourceProperty Ans= $ans\n";
3512              
3513             return WSRF::Header::header($envelope),
3514             SOAP::Data->value($ans)->type('xml');
3515             }
3516              
3517             # wsrp GetMultipleResourceProperties
3518             sub GetMultipleResourceProperties {
3519             my $envelope = pop @_;
3520              
3521             my $ans = ""; #we will just cat the answers together
3522              
3523             # print "XML>>>\n".$xmlizeProperties->($ID)."\n<<
3524              
3525             #loop over each ResourceProperty request
3526             foreach my $search ( $envelope->valueof('//ResourceProperty/') ) {
3527             $ans .= searchResourceProperty($search);
3528             }
3529              
3530             return WSRF::Header::header($envelope),
3531             SOAP::Data->value($ans)->type('xml');
3532              
3533             }
3534              
3535             # wsrp SetResourceProperties - the client can request that properties
3536             # are inserted, updated and deleted in the one operation. The commands
3537             # must happen in the order they come in the request, all stop when we
3538             # hit a problem
3539             sub SetResourceProperties {
3540              
3541             #get the envelope
3542             my $som = pop @_;
3543              
3544             #the base point of all our searchs.
3545             my $base = "//SetResourceProperties";
3546              
3547             #find the start of commands - should think
3548             #of this as an array of arries - that is why we have [$jj]/[$kk]
3549             if ( $som->match($base) ) {
3550             my $jj = 1;
3551              
3552             #now we loop over commands - $jj records our postion
3553             while ( $som->dataof("$base/[$jj]") ) {
3554              
3555             #get the command name
3556             my $Function = $som->dataof("$base/[$jj]")->name();
3557             if ( $Function eq "Insert" ) #an Insert
3558             {
3559             my $kk = 1;
3560              
3561             #loop over the things that have to be inserted
3562             while ( $som->match("$base/[$jj]/[$kk]") ) {
3563              
3564             #print "Inserting ".$som->dataof("$base/[$jj]/[$kk]")->name()."\n";
3565             #insert the thing - note we pass a SOM object becasue the
3566             if (
3567             !defined(
3568             $WSRF::WSRP::NotInsert{ $som->dataof(
3569             "$base/[$jj]/[$kk]")->name() }
3570             )
3571             )
3572             {
3573             $insert->( $som->match("$base/[$jj]/[$kk]") );
3574             } #thing could be pretty complex.
3575              
3576             $kk++;
3577             }
3578             } elsif ( $Function eq "Update" ) #an Update
3579             {
3580             my $kk = 1;
3581             my %tmpHash = ();
3582              
3583             #loop over things to Update - an update is a Delete followed
3584             #by an Insert in a single atomic operation
3585             while ( $som->match("$base/[$jj]/[$kk]") ) {
3586              
3587             #get name of thing we are updating
3588             my $name = $som->dataof("$base/[$jj]/[$kk]")->name();
3589              
3590             #print "Updating $name\n";
3591             #check we have not deleted it before else delete before inserting
3592             if ( !defined( $WSRF::WSRP::NotModifiable{$name} ) ) {
3593             if ( !defined( $tmpHash{$name} ) ) {
3594             $mydelete->($name);
3595             $tmpHash{$name} = 1;
3596             }
3597              
3598             #insert value
3599             $insert->( $som->match("$base/[$jj]/[$kk]") );
3600             }
3601             $kk++;
3602             }
3603             } elsif ( $Function eq "Delete" ) #a Delete
3604             {
3605              
3606             #the property to delete is actually an attribute
3607             #in the delete element
3608             my $propname =
3609             $som->dataof("$base/[$jj]")->attr->{'resourceProperty'};
3610              
3611             #print "Delete $propname\n";
3612             #delete property
3613             if ( !defined( $WSRF::WSRP::NotDeletable{$propname} ) ) {
3614             $mydelete->($propname);
3615             }
3616             } else { #something other than Insert|Update|Delete
3617             die SOAP::Fault->faultcode(
3618             "setResourceproperty: Unkown operation")
3619             ->faultstring("$Function not supported - only Insert,Update and Delete are supported"
3620             );
3621             }
3622             $jj++;
3623             }
3624             }
3625              
3626             return WSRF::Header::header($som);
3627             }
3628              
3629             sub InsertResourceProperties {
3630             my $som = pop @_;
3631             my $base = "//InsertResourceProperties";
3632             if ( $som->match($base) ) {
3633             my $kk = 1;
3634             while ( $som->match("$base/[1]/[$kk]") ) {
3635             my $name = $som->dataof("$base/[1]/[$kk]")->name();
3636             print "Inserting $name\n";
3637              
3638             #insert the thing - note we pass a SOM object becasue the
3639             #thing could be pretty complex.
3640             if ( !defined( $WSRF::WSRP::NotInsert{$name} ) ) {
3641             $insert->( $som->match("$base/[1]/[$kk]") );
3642             } else {
3643             die "InvalidInsertResourcePropertiesRequestContent\n";
3644             }
3645             $kk++;
3646             }
3647             }
3648             return WSRF::Header::header($som);
3649             }
3650              
3651             sub UpdateResourceProperties {
3652             my $som = pop @_;
3653             my $base = "//UpdateResourceProperties";
3654             if ( $som->match($base) ) {
3655             my $kk = 1;
3656             my %tmpHash = ();
3657             while ( $som->match("$base/[1]/[$kk]") ) {
3658              
3659             #get name of thing we are updating
3660             my $name = $som->dataof("$base/[1]/[$kk]")->name();
3661             print "Updating $name\n";
3662             if ( !defined( $WSRF::WSRP::NotModifiable{$name} ) ) {
3663              
3664             #check we have not deleted it before else delete before inserting
3665             if ( !defined( $tmpHash{$name} ) ) {
3666             $mydelete->($name);
3667             $tmpHash{$name} = 1;
3668             }
3669              
3670             #insert value
3671             $insert->( $som->match("$base/[1]/[$kk]") );
3672             } else {
3673             die "InvalidUpdateResourcePropertiesRequestContent\n";
3674             }
3675             $kk++;
3676             }
3677             }
3678              
3679             return WSRF::Header::header($som);
3680             }
3681              
3682             sub DeleteResourceProperties {
3683             my $som = pop @_;
3684             my $base = "//DeleteResourceProperties";
3685             if ( $som->match($base) ) {
3686             my $kk = 1;
3687             while ( $som->match("$base/[$kk]") ) {
3688             print "Into Loop inner...\n";
3689              
3690             #the property to delete is actually an attribute
3691             #in the delete element
3692             my $propname =
3693             $som->dataof("$base/[$kk]")->attr->{'ResourceProperty'};
3694             $propname =~ s/\w*://o;
3695              
3696             #delete property
3697             if ( !defined( $WSRF::WSRP::NotDeletable{$propname} ) ) {
3698             $mydelete->($propname);
3699             } else {
3700             die "InvalidDeleteResourcePropertiesRequestContent\n";
3701             }
3702             $kk++;
3703             }
3704             }
3705              
3706             return WSRF::Header::header($som);
3707             }
3708              
3709             #===============================================================================
3710             # The WSRL class, inherits from the WSRF::WSRP class and adds Destroy
3711             # and SetTerminationTime operations. Adds the resource properties
3712             # required wsrl:TerminationTime and wsrl:CurrentTime
3713             #
3714             package WSRF::WSRL;
3715              
3716             =pod
3717              
3718             =head1 WSRF::WSRL
3719              
3720             Provides support for WS-ResourceLifetimes. WS-ResourceLifetime defines
3721             a standard mechanism for controlling the lifetime of a WS-Resource. It
3722             adds the ResourceProperty I to the set of ResourceProerties
3723             of the WS-Resource, the I cannot be changed through the
3724             WS-ResourceProperties - it can only be modified using the WS-ResourceLifetime
3725             B operation.
3726              
3727             =head2 METHODS
3728              
3729             =over
3730              
3731             =item Destroy
3732              
3733             =item SetTerminationTime
3734              
3735             =back
3736              
3737             =cut
3738              
3739             use vars qw(@ISA);
3740              
3741             @ISA = qw(WSRF::WSRP);
3742              
3743             sub init {
3744             my $self = shift @_;
3745              
3746             # Add TerminationTime as a resource property -
3747             # initalise to nothing (ie. set TT to infinity)
3748             $WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";
3749              
3750             # belongs to RsourceLiftetime namespace - defined
3751             # elsewhere to be wsrl
3752             $WSRF::WSRP::PropertyNamespaceMap->{TerminationTime}{prefix} = "wsrl";
3753              
3754             # the TerminationTime can be nil.
3755             $WSRF::WSRP::Nillable{TerminationTime} = 1;
3756             $WSRF::WSRP::NotModifiable{TerminationTime} = 1;
3757              
3758             # add resource property CurrentTime - in this
3759             # case a subroutine that returns the current
3760             # time in the correct format
3761             $WSRF::WSRP::ResourceProperties{'CurrentTime'} = sub {
3762             return ""
3763             . WSRF::Time::ConvertEpochTimeToString()
3764             . "";
3765             };
3766             $WSRF::WSRP::PropertyNamespaceMap->{CurrentTime}{prefix} = "wsrl";
3767              
3768             # By default if a resource property is a subroutine
3769             # then you cannot change it or delete it - however
3770             # for completeness we set the following
3771             $WSRF::WSRP::NotDeletable{CurrentTime} = 1;
3772             $WSRF::WSRP::NotModifiable{CurrentTime} = 1;
3773             $WSRF::WSRP::NotInsert{CurrentTime} = 1;
3774              
3775             $self->SUPER::init();
3776              
3777             }
3778              
3779             sub Destroy {
3780              
3781             #set alarm to 1, gives us time to return a result
3782             #before we die
3783             alarm(1);
3784              
3785             #return nothing except a SOAP HEADER
3786             return WSRF::Header::header( pop @_ );
3787             }
3788              
3789             # wsrl SetTerminationTime - if you want to make a max limit your Resource
3790             # you should override this function in your module.
3791             sub SetTerminationTime {
3792             my $envelope = pop @_;
3793             shift @_; #the first paramter is always the class of the object
3794             my $time = shift @_; #the new TerminationTime
3795              
3796             #check for null time - allowed by wsrl, means TT is infinity
3797             if ( $time eq "" ) {
3798             $WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";
3799              
3800             #disable alarm
3801             alarm;
3802             my $ans =
3803             ""
3804             . ""
3805             . WSRF::Time::ConvertEpochTimeToString()
3806             . "";
3807              
3808             return WSRF::Header::header($envelope),
3809             SOAP::Data->value($ans)->type('xml');
3810             }
3811              
3812             #BUG this is handled by WSRF::Time::ConvertStringToEpochTime now - should
3813             #BUG be removed from here
3814             $time =~ s/\.\d+//;
3815              
3816             #print "Setting TerminationTime to: $time\n";
3817             #test time is good - this will die if the string is faulty, causing
3818             #a SOAP fault to be sent to the cli
3819             #ent
3820             DateTime::Format::W3CDTF->new->parse_datetime($time);
3821              
3822             my $SecsToLive = WSRF::Time::ConvertStringToEpochTime($time);
3823              
3824             if ( $SecsToLive < time ) # TT is sometime in the past, die now
3825             {
3826              
3827             #give us time to reply - then die
3828             alarm 1;
3829             } else {
3830              
3831             #reset the alarm, this is were you can set a max TT.
3832             alarm( $SecsToLive - time );
3833             }
3834              
3835             #reset TerminationTime
3836             $WSRF::WSRP::ResourceProperties{'TerminationTime'} = $time;
3837              
3838             my $result = "$time";
3839             $result .=
3840             ""
3841             . WSRF::Time::ConvertEpochTimeToString()
3842             . "";
3843              
3844             return WSRF::Header::header($envelope),
3845             SOAP::Data->value($result)->type('xml');
3846             }
3847              
3848             #===============================================================================
3849             # If the Service inherits from this class then the ResourceProperties are
3850             # stored in a file between calls.
3851             #
3852             package WSRF::FileBasedResourceProperties;
3853              
3854             =pod
3855              
3856             =head1 WSRF::FileBasedResourceProperties
3857              
3858             If a WS-Resource module inherits from this class then its ResourceProperties
3859             will be stored in a file.
3860              
3861             =head2 METHODS
3862              
3863             =over
3864              
3865             =item GetResourceProperty
3866              
3867             =item GetMultipleResourceProperties
3868              
3869             =item SetResourceProperties
3870              
3871             =item InsertResourceProperties
3872              
3873             =item UpdateResourceProperties
3874              
3875             =item DeleteResourceProperties
3876              
3877             =item GetResourcePropertyDocument
3878              
3879             =back
3880              
3881             =cut
3882              
3883             use vars qw(@ISA);
3884              
3885             @ISA = qw(WSRF::WSRP);
3886              
3887             # Load the ResourceProperties from the file into the ResourceProperties hash
3888             # then call the super operation.
3889             sub GetResourceProperty {
3890             my $self = shift @_;
3891             my $envelope = pop @_;
3892             my $lock = WSRF::File->new($envelope);
3893              
3894             #print "TT= ".$WSRF::WSRP::ResourceProperties{TerminationTime}."\n";
3895             #print "calling SUPER::GetResourceProperty\n";
3896             my @resp = $self->SUPER::GetResourceProperty($envelope);
3897             $lock->toFile();
3898             return @resp;
3899             }
3900              
3901             # Load the ResourceProperties from the file into the ResourceProperties hash
3902             # then call the super operation.
3903             sub GetMultipleResourceProperties {
3904             my $self = shift @_;
3905             my $envelope = pop @_;
3906             my $lock = WSRF::File->new($envelope);
3907             my @resp = $self->SUPER::GetMultipleResourceProperties($envelope);
3908             $lock->toFile();
3909             return @resp;
3910             }
3911              
3912             # Load the ResourceProperties from the file into the ResourceProperties hash
3913             # then call the super operation.
3914             sub SetResourceProperties {
3915             my $self = shift @_;
3916             my $envelope = pop @_;
3917             my $lock = WSRF::File->new($envelope);
3918             my @resp = $self->SUPER::SetResourceProperties($envelope);
3919             $lock->toFile();
3920             return @resp;
3921             }
3922              
3923             # Load the ResourceProperties from the file into the ResourceProperties hash
3924             # then call the super operation.
3925             sub InsertResourceProperties {
3926             my $self = shift @_;
3927             my $envelope = pop @_;
3928             my $lock = WSRF::File->new($envelope);
3929             my @resp = $self->SUPER::InsertResourceProperties($envelope);
3930             $lock->toFile();
3931             return @resp;
3932             }
3933              
3934             # Load the ResourceProperties from the file into the ResourceProperties hash
3935             # then call the super operation.
3936             sub UpdateResourceProperties {
3937             my $self = shift @_;
3938             my $envelope = pop @_;
3939             my $lock = WSRF::File->new($envelope);
3940             my @resp = $self->SUPER::UpdateResourceProperties($envelope);
3941             $lock->toFile();
3942             return @resp;
3943             }
3944              
3945             # Load the ResourceProperties from the file into the ResourceProperties hash
3946             # then call the super operation.
3947             sub DeleteResourceProperties {
3948             my $self = shift @_;
3949             my $envelope = pop @_;
3950             my $lock = WSRF::File->new($envelope);
3951             my @resp = $self->SUPER::DeleteResourceProperties($envelope);
3952             $lock->toFile();
3953             return @resp;
3954             }
3955              
3956             # Load the ResourceProperties from the file into the ResourceProperties hash
3957             # then call the super operation.
3958             sub GetResourcePropertyDocument {
3959             my $self = shift @_;
3960             my $envelope = pop @_;
3961             my $lock = WSRF::File->new($envelope);
3962             my @resp = $self->SUPER::GetResourcePropertyDocument($envelope);
3963             $lock->toFile();
3964             return @resp;
3965             }
3966              
3967             #=============================================================================
3968             # Inherits from WSRF::FileBasedResourceProperties, adds the WSRL operations
3969             # to the Service. Again all the ResourceProperties are stored in a file
3970             # between calls - the name of the file is the same as the Resource ID
3971             #
3972              
3973             package WSRF::FileBasedResourceLifetimes;
3974              
3975             =pod
3976              
3977             =head1 WSRF::FileBasedResourceLifetimes
3978              
3979             If a WS-Resource wants to store its state in a file and wants to support
3980             WS-ResourceLifetimes it should inherit from this class.
3981             WSRF::FileBasedResourceLifetimes inherits from
3982             WSRF::FileBasedResourceProperties.
3983              
3984             =head2 METHODS
3985              
3986             =over
3987              
3988             =item Destroy
3989              
3990             =item SetTerminationTime
3991              
3992             =back
3993              
3994             =cut
3995              
3996             use vars qw(@ISA);
3997              
3998             @ISA = qw(WSRF::FileBasedResourceProperties);
3999              
4000             #Add TerminationTime as a reource property -
4001             #initalise to nothing (infinity)
4002             $WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";
4003              
4004             #belongs to RsourceLiftetime namespace - defined
4005             #elsewhere to be wsrl
4006             $WSRF::WSRP::PropertyNamespaceMap->{TerminationTime}{prefix} = "wsrl";
4007              
4008             #the TerminationTime can be nil
4009             $WSRF::WSRP::Nillable{TerminationTime} = 1;
4010             $WSRF::WSRP::NotModifiable{TerminationTime} = 1;
4011              
4012             #add resource property CurrentTime - in this
4013             #case a subroutine that returns the current
4014             #time in the correct format
4015             $WSRF::WSRP::ResourceProperties{'CurrentTime'} = sub {
4016             return ""
4017             . WSRF::Time::ConvertEpochTimeToString()
4018             . "";
4019             };
4020             $WSRF::WSRP::PropertyNamespaceMap->{CurrentTime}{prefix} = "wsrl";
4021              
4022             #By default if a resource property is a subroutine
4023             #then you cannot change it or delete it - however
4024             #for completeness we set the following
4025             $WSRF::WSRP::NotDeletable{CurrentTime} = 1;
4026             $WSRF::WSRP::NotModifiable{CurrentTime} = 1;
4027              
4028             # remove the file with the resource properties in it.
4029             sub Destroy {
4030             my $envelope = pop @_;
4031             my $lock = WSRF::File->new($envelope);
4032             my $file = $WSRF::Constants::Data . $lock->ID();
4033             unlink $file
4034             or die SOAP::Fault->faultcode("Container Failure")
4035             ->faultstring("Container Failure: could not remove file");
4036             return WSRF::Header::header($envelope);
4037             }
4038              
4039             # load the properties from the file into the hash then
4040             # set the termination time and store back to the file.
4041             sub SetTerminationTime {
4042             my $envelope = pop @_;
4043             my $lock = WSRF::File->new($envelope);
4044             shift @_; #the first paramter is always the class of the object
4045             my $time = shift @_; #the new TerminationTime
4046              
4047             #check for null time - allowed by wsrl
4048             my ($ans);
4049             if ( $time eq "" ) {
4050             $WSRF::WSRP::ResourceProperties{'TerminationTime'} = "";
4051              
4052             my $ans =
4053             ""
4054             . ""
4055             . WSRF::Time::ConvertEpochTimeToString(time)
4056             . "";
4057             } else {
4058              
4059             #BUG - this is done in ConvertEpochTimeToString now so we can drop it
4060             $time =~ s/\.\d+//;
4061              
4062             #print "Setting TerminationTime to: $time\n";
4063              
4064             #test time is good - this will die if the string is faulty, causing
4065             #a SOAP fault to be sent to the client
4066             DateTime::Format::W3CDTF->new->parse_datetime($time);
4067              
4068             #reset TerminationTime
4069             $WSRF::WSRP::ResourceProperties{'TerminationTime'} = $time;
4070              
4071             $ans = "$time";
4072             $ans .=
4073             ""
4074             . WSRF::Time::ConvertEpochTimeToString()
4075             . "";
4076             }
4077              
4078             $lock->toFile();
4079             return WSRF::Header::header($envelope),
4080             SOAP::Data->value($ans)->type('xml');
4081             }
4082              
4083             #===============================================================================
4084             # In this case a single process acts on behave of a number of
4085             # Resources - the resource properties are all held in a hash - the
4086             # ID of the resource is used as the key to the hash. The Container
4087             # talks to the process through a named UNIX socket - the name of the
4088             # socket is the same as the name of the module.
4089             #
4090             package WSRF::MultiResourceProperties;
4091              
4092             =pod
4093              
4094             =head1 WSRF::MultiResourceProperties
4095              
4096             In this case a single process acts on behave of a number of
4097             WS-Resources. The I are all held in a hash - the
4098             WSRF::Lite identifier of the WS-Resource is used as the key to the hash.
4099             The WSRF::Lite I talks to the process through a named UNIX socket
4100             - the name of the socket is the same as the name of the module.
4101             The WS-Resource module should inherit this class
4102              
4103             =head2 METHODS
4104              
4105             =over
4106              
4107             =item GetResourcePropertyDocument
4108              
4109             =item GetResourceProperty
4110              
4111             =item GetMultipleResourceProperties
4112              
4113             =item SetResourceProperties
4114              
4115             =item InsertResourceProperties
4116              
4117             =item UpdateResourceProperties
4118              
4119             =item DeleteResourceProperties
4120              
4121             =back
4122              
4123             =cut
4124              
4125             use vars qw(@ISA);
4126              
4127             #we inherit this to gain access to the envelope - see SOAP::Lite
4128             @ISA = qw(SOAP::Server::Parameters);
4129              
4130             # For this example all Resources are managed by one process,
4131             # a hash holds an entry for each resource, the same hash
4132             # also holds all the resource properties for each resource
4133              
4134             #Hash to store each resource and its properties
4135             %WSRF::MultiResourceProperties::ResourceProperties = ();
4136              
4137             # Hash stores the prefix for the resource property
4138             # eg CurrentTime will use the prefix wsrl, the
4139             # map between tthe prefix and the namespace is
4140             # elsewhere
4141             %WSRF::MultiResourceProperties::PropertyNamespaceMap = ();
4142              
4143             # Hash that maps a property and the fuction that
4144             # should be called when aan attempt is made to
4145             # insert that property. Simple properties are
4146             # handled by default.
4147             %WSRF::MultiResourceProperties::InsertMap = ();
4148              
4149             # Hash that maps property to function that should
4150             # be used to delete it - simple properties are
4151             # handled by default
4152             %WSRF::MultiResourceProperties::DeleteMap = ();
4153              
4154             # Hash to define which properties can be "nil" - by
4155             # default properties can not be nil.
4156             %WSRF::MultiResourceProperties::Nillable = ();
4157              
4158             # Hash to define which properties cannot be Deleted
4159             %WSRF::MultiResourceProperties::NotDeletable = ();
4160              
4161             # Hash to define which properties cannot be changed
4162             %WSRF::MultiResourceProperties::NotModifiable = ();
4163              
4164             %WSRF::MultiResourceProperties::NotInsert = ();
4165              
4166             # get the Resource ID from the envelope - check that it is in the
4167             # hash and check the termination time for the resource.
4168             # BUG - should we check the TT for all resources and do Garbag Collection
4169             # pro-actively
4170             sub getID {
4171             my $envelope = shift;
4172              
4173             #print STDERR "Calling getID...\n";
4174             #search for ResourceID in Header
4175             my $ID = $envelope->headerof("//{$WSRF::Constants::WSA}To");
4176             if ( defined $ID ) {
4177             $ID = $envelope->headerof("//{$WSRF::Constants::WSA}To")->value;
4178             } else {
4179             die SOAP::Fault->faultcode('No WS-Resource Identifier')
4180             ->faultstring('No Resource Identifier in SOAP Header');
4181             }
4182              
4183             my @PathArray = split( /\//, $ID );
4184             $ID = pop @PathArray;
4185              
4186             #print STDERR "ID => $ID\n";
4187              
4188             #check the Resource actually exists or die
4189             if ( !defined( $WSRF::MultiResourceProperties::ResourceProperties->{$ID} ) )
4190             {
4191             die SOAP::Fault->faultcode('No WS-Resource')
4192             ->faultstring("No Resource with Identifier $ID");
4193             }
4194              
4195             #check that the resource is still alive - if TT time is not
4196             #set then TT is infinity
4197             foreach
4198             my $key ( keys %{$WSRF::MultiResourceProperties::ResourceProperties} )
4199             {
4200             if (
4201             defined(
4202             $WSRF::MultiResourceProperties::ResourceProperties->{$key}
4203             {'TerminationTime'}
4204             )
4205             && ( $WSRF::MultiResourceProperties::ResourceProperties->{$key}
4206             {'TerminationTime'} ne "" )
4207             )
4208             {
4209             if (
4210             WSRF::Time::ConvertStringToEpochTime(
4211             $WSRF::MultiResourceProperties::ResourceProperties->{$key}
4212             {'TerminationTime'}
4213             ) < time
4214             )
4215             {
4216             print STDERR "MultiResourceProperties Resource $key Expired\n";
4217             delete
4218             $WSRF::MultiResourceProperties::ResourceProperties->{$key};
4219             }
4220             }
4221             }
4222              
4223             #check the Resource actually exists or die
4224             if ( !defined( $WSRF::MultiResourceProperties::ResourceProperties->{$ID} ) )
4225             {
4226             die SOAP::Fault->faultcode('No WS-Resource')
4227             ->faultstring("No Resource with Identifier $ID");
4228             }
4229              
4230             #could set as ENV variable?
4231             return $ID;
4232             }
4233              
4234             # serach for a resource property - this is used by getResourceProperty
4235             # and getMultipleResourceProperties. Takes the ID of the resource
4236             # and the name of the rsource.
4237             # BUG - we do not handle namespaces of peroperty!!
4238             my $MultisearchResourceProperty = sub {
4239             my %args = @_;
4240             my $ID = $args{ID};
4241             my $longsearch = $args{property};
4242              
4243             #dump the namespace of property
4244             my ( $junk, $search );
4245             if ( $longsearch =~ m/:/ ) {
4246             ( $junk, $search ) = split /:/, $longsearch;
4247             } else {
4248             $search = $longsearch;
4249             }
4250              
4251             #default result!!
4252             my $ans = "";
4253              
4254             #Check Resource property exists, if it does it can either
4255             #be a simple scalar, an array or a function.
4256             if (
4257             defined( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$search}
4258             )
4259             )
4260             {
4261              
4262             #get type of property
4263             my $type =
4264             ref( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4265             {$search} );
4266             if ( $type eq "" ) # if scalar
4267             {
4268              
4269             #check if property set
4270             if ( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4271             {$search} ne "" )
4272             {
4273             $ans .= "<"
4274             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4275             ->{$search}{prefix} . ":$search ";
4276              
4277             #do we need to add a namespace for this property
4278             my $ns =
4279             defined( $WSRF::MultiResourceProperties::PropertyNamespaceMap
4280             ->{$search}{namespace} )
4281             ? " xmlns:"
4282             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4283             ->{$search}{prefix} . "=\""
4284             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4285             ->{$search}{namespace} . "\">"
4286             : ">";
4287             $ans .= $ns
4288             . $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4289             {$search} . "
4290             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4291             ->{$search}{prefix} . ":$search>";
4292             }
4293              
4294             #property NOT set - is it nillable?
4295             elsif ( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4296             {$search} eq ""
4297             && defined( $WSRF::MultiResourceProperties::Nillable{$search} )
4298             )
4299             {
4300             $ans .= "<"
4301             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4302             ->{$search}{prefix} . ":$search";
4303             my $ns =
4304             defined( $WSRF::MultiResourceProperties::PropertyNamespaceMap
4305             ->{$search}{namespace} )
4306             ? " xmlns:"
4307             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4308             ->{$search}{prefix} . "=\""
4309             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4310             ->{$search}{namespace} . "\""
4311             : " ";
4312             $ans .= $ns . " xsi:nil=\"true\"/>";
4313             }
4314             }
4315              
4316             #property is array of things
4317             elsif ( $type eq "ARRAY" ) {
4318              
4319             #check array is not empty - and property is nillable
4320             if (
4321             !@{
4322             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4323             {$search}
4324             }
4325             && defined( $WSRF::MultiResourceProperties::Nillable{$search} )
4326             )
4327             {
4328             $ans .= "<"
4329             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4330             ->{$search}{prefix} . ":$search";
4331             my $ns =
4332             defined( $WSRF::MultiResourceProperties::PropertyNamespaceMap
4333             ->{$search}{namespace} )
4334             ? " xmlns:"
4335             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4336             ->{$search}{prefix} . "=\""
4337             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4338             ->{$search}{namespace} . "\""
4339             : " ";
4340             $ans .= $ns . " xsi:nil=\"true\"/>";
4341             }
4342              
4343             #loop over array building result
4344             else {
4345             foreach my $entry (
4346             @{
4347             $WSRF::MultiResourceProperties::ResourceProperties
4348             ->{$ID}{$search}
4349             }
4350             )
4351             {
4352             $ans .= "<"
4353             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4354             ->{$search}{prefix} . ":$search";
4355              
4356             #do we need to add a namespace for this property
4357             my $ns =
4358             defined(
4359             $WSRF::MultiResourceProperties::PropertyNamespaceMap
4360             ->{$search}{namespace} )
4361             ? " xmlns:"
4362             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4363             ->{$search}{prefix} . "=\""
4364             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4365             ->{$search}{namespace} . "\">"
4366             : ">";
4367             $ans .=
4368             $ns . $entry . "
4369             . $WSRF::MultiResourceProperties::PropertyNamespaceMap
4370             ->{$search}{prefix} . ":$search>";
4371             }
4372             }
4373             }
4374              
4375             #property is a subroutine - call it to get result
4376             #example of this is CurrentTime
4377             elsif ( $type eq "CODE" ) {
4378             $ans .=
4379             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$search}
4380             ->();
4381             }
4382              
4383             #Some type we do not understand yet eg. Hash
4384             else {
4385              
4386             my $serializer = WSRF::SimpleSerializer->new();
4387             $ans .= "<"
4388             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
4389             . ":$search";
4390              
4391             #do we need to add a namespace for this property
4392             my $ns =
4393             defined( $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} )
4394             ? " xmlns:"
4395             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix} . "=\""
4396             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{namespace} . "\">"
4397             : ">";
4398              
4399             $ans .= $ns
4400             . $serializer->serialize(
4401             $WSRF::WSRP::ResourceProperties->{$ID}{$search} )
4402             . "
4403             . $WSRF::WSRP::PropertyNamespaceMap->{$search}{prefix}
4404             . ":$search>";
4405              
4406             #die "Do not understand type\n";
4407             }
4408              
4409             }
4410              
4411             return $ans;
4412             };
4413              
4414             # This creates XML with all the ResourceProperties in it - we can then
4415             # use the XPath query from queryResourceProperty on it.
4416             # BUG - we have not written queryResourceProperty
4417             my $xmlizeProperties = sub {
4418             my $ID = shift @_;
4419              
4420             if ( !defined($ID) || $ID eq "" ) {
4421             die "Attempt to call xmlizeProperties without ID\n";
4422             }
4423              
4424             #print "$$ MultiSession xmlizeProperties called for $ID\n";
4425              
4426             #my $ans = "";
4427             my $ans =
4428             "
4429             . " xmlns:wsrp=\"$WSRF::Constants::WSRP\" "
4430             . " xmlns:wsrl=\"$WSRF::Constants::WSRL\" "
4431             . " xmlns:wsa=\"$WSRF::Constants::WSA\" >";
4432              
4433             foreach my $key (
4434             keys %{ $WSRF::MultiResourceProperties::ResourceProperties->{$ID} } )
4435             {
4436             $ans .= $MultisearchResourceProperty->( ID => $ID, property => $key );
4437             }
4438              
4439             $ans .= "";
4440              
4441             return $ans;
4442             };
4443              
4444             sub GetResourcePropertyDocument {
4445             my $envelope = pop @_;
4446             my $ID = getID($envelope);
4447             print "$$ Called GetResourcePropertyDocument, ID= $ID\n";
4448             my $xml = $xmlizeProperties->($ID);
4449             return WSRF::Header::header($envelope),
4450             SOAP::Data->value($xml)->type('xml');
4451             }
4452              
4453             # insert property - this function is used by the Insert and Update
4454             # in the SetResourceProperty operation. This operation takes
4455             # the ID of the resource and a SOAP::SOM object that has been set
4456             # at the property that should be inserted
4457             # Only one property can be inserted at a time using the function -
4458             # SetResourceProperty of course loops over it
4459             my $Multiinsert = sub {
4460             my %args = @_;
4461             my $ID = $args{ID};
4462             my $b = $args{som};
4463              
4464             #get the name of the property
4465             my $name = $b->dataof()->name;
4466              
4467             #check there is no user defined function
4468             #for inserting this property
4469             if ( defined( $WSRF::MultiResourceProperties::InsertMap{$name} ) ) {
4470             $WSRF::MultiResourceProperties::InsertMap{$name}->( $ID, $b );
4471             return;
4472             }
4473              
4474             #check this property can be changed
4475             # if( defined( $WSRF::MultiResourceProperties::NotModifiable{$name} ))
4476             # {
4477             # die SOAP::Fault->faultcode("setResourceproperty: Failure")
4478             # ->faultstring("Could not modify $name");
4479             # }
4480              
4481             #get the value of the property
4482             my $value = $b->dataof()->value;
4483              
4484             #check the property actually exists
4485             if (
4486             defined( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name}
4487             )
4488             )
4489             {
4490              
4491             #check the type of the property (scalar|array)
4492             my $type =
4493             ref(
4494             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} );
4495             if ( $type eq "" ) #scalar
4496             {
4497             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} =
4498             $value;
4499             } elsif ( $type eq "ARRAY" ) #array
4500             {
4501              
4502             #add property to array
4503             push(
4504             @{
4505             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4506             {$name}
4507             },
4508             $value
4509             );
4510             } else #perhaps subroutine?
4511             {
4512             die SOAP::Fault->faultcode("setResourceproperty: Failure")
4513             ->faultstring("Could not modify $name");
4514             }
4515             } else {
4516             die SOAP::Fault->faultcode("No such WS-Resource")
4517             ->faultstring("No such WS-Resource with identifier $ID");
4518             }
4519             return;
4520             };
4521              
4522             # delete property
4523             # BUG we do not handle namespaces
4524             my $Multimydelete = sub {
4525             my %args = @_;
4526             my $ID = $args{ID};
4527             my $name = $args{property};
4528              
4529             #strip namespace
4530             $name =~ s/\w*://;
4531              
4532             #check for user defined delete function for this property
4533             if ( defined( $WSRF::MultiResourceProperties::DeleteMap{$name} ) ) {
4534             $WSRF::MultiResourceProperties::DeleteMap{$name}->($ID);
4535             return;
4536             }
4537              
4538             #check we are allowed to delete this function
4539             # if( defined( $WSRF::MultiResourceProperties::NotDeletable{$name} ) )
4540             # {
4541             # die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
4542             # ->faultstring("Could not delete $name");
4543             # }
4544              
4545             #check property exists
4546             if (
4547             defined( $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name}
4548             )
4549             )
4550             {
4551              
4552             #check type either array or scalar
4553             my $type =
4554             ref(
4555             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} );
4556             if ( $type eq "" ) #scalar
4557             {
4558             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}{$name} =
4559             "";
4560             } elsif ( $type eq "ARRAY" ) # array
4561             {
4562              
4563             #set contents to nothing
4564             @{ $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4565             {$name} } = ();
4566             } else {
4567             die SOAP::Fault->faultcode("setResourceproperty: Delete Failure")
4568             ->faultstring("Could not delete $name");
4569             }
4570             } else {
4571             die SOAP::Fault->faultcode("No such WS-Resource")
4572             ->faultstring("No WS-Resource with identifier $ID");
4573             }
4574             return;
4575             };
4576              
4577             # provide a default init - incase the service developer doesn't bother
4578             sub init { return; }
4579              
4580             # wsrp GetResourceProperty
4581             sub GetResourceProperty {
4582             my $envelope = pop @_;
4583             my $ID = getID($envelope);
4584              
4585             #search through envelope to the GetResourceProperty bit
4586             #and get the resource property name
4587             my $search = $envelope->valueof('//GetResourceProperty/');
4588              
4589             my $ans = $MultisearchResourceProperty->( ID => $ID,
4590             property => $search );
4591              
4592             return WSRF::Header::header($envelope),
4593             SOAP::Data->value($ans)->type('xml');
4594             }
4595              
4596             # wsrp GetMultipleResourceProperties
4597             sub GetMultipleResourceProperties {
4598             my $envelope = pop @_;
4599             my $ID = getID($envelope);
4600              
4601             my $ans = ""; #we will just cat the answers together
4602              
4603             # print "XML>>>\n".$xmlizeProperties->($ID)."\n<<
4604              
4605             #loop over each ResourceProperty request
4606             foreach my $search ( $envelope->valueof('//ResourceProperty/') ) {
4607             $ans .= $MultisearchResourceProperty->( ID => $ID,
4608             property => $search );
4609             }
4610              
4611             return WSRF::Header::header($envelope),
4612             SOAP::Data->value($ans)->type('xml');
4613              
4614             }
4615              
4616             # wsrp SetResourceProperties - the client can request that properties
4617             # are inserted, updated and deleted in the one operation. The commands
4618             # must happen in the order they come in the request, all stop when we
4619             # hit a problem
4620             sub SetResourceProperties {
4621              
4622             #get the envelope
4623             my $som = pop @_;
4624             my $ID = getID($som);
4625              
4626             #the base point of all our searchs.
4627             my $base = "//SetResourceProperties";
4628              
4629             #find the start of commands - should think
4630             #of this as an array of arries - that is why we have [$jj]/[$kk]
4631             if ( $som->match($base) ) {
4632             my $jj = 1;
4633              
4634             #now we loop over commands - $jj records our postion
4635             while ( $som->dataof("$base/[$jj]") ) {
4636              
4637             #get the command name
4638             my $Function = $som->dataof("$base/[$jj]")->name();
4639             if ( $Function eq "Insert" ) #an Insert
4640             {
4641             my $kk = 1;
4642              
4643             #loop over the things that have to be inserted
4644             while ( $som->match("$base/[$jj]/[$kk]") ) {
4645              
4646             #print "Inserting ".$som->dataof("$base/[$jj]/[$kk]")->name()."\n";
4647             #insert the thing - note we pass a SOM object becasue the
4648             #thing could be pretty complex.
4649             if (
4650             !defined(
4651             $WSRF::MultiResourceProperties::NotInsert{ $som
4652             ->dataof("$base/[$jj]/[$kk]")->name() }
4653             )
4654             )
4655             {
4656             $Multiinsert->( ID => $ID,
4657             som => $som->match("$base/[$jj]/[$kk]") );
4658             }
4659             $kk++;
4660             }
4661             } elsif ( $Function eq "Update" ) #an Update
4662             {
4663             my $kk = 1;
4664             my %tmpHash = ();
4665              
4666             #loop over things to Update - an update is a Delete followed
4667             #by an Insert in a single atomic operation
4668             while ( $som->match("$base/[$jj]/[$kk]") ) {
4669              
4670             #get name of thing we are updating
4671             my $name = $som->dataof("$base/[$jj]/[$kk]")->name();
4672              
4673             #print "Updating $name\n";
4674             #check we have not deleted it before else delete before inserting
4675             if (
4676             !defined( $WSRF::MultiResourceProperties::NotModifiable{$name}
4677             )
4678             )
4679             {
4680             if ( !defined( $tmpHash{$name} ) ) {
4681             $Multimydelete->( ID => $ID,
4682             property => $name );
4683             $tmpHash{$name} = 1;
4684             }
4685              
4686             #insert value
4687             $Multiinsert->( ID => $ID,
4688             som => $som->match("$base/[$jj]/[$kk]") );
4689             }
4690             $kk++;
4691             }
4692             } elsif ( $Function eq "Delete" ) #a Delete
4693             {
4694              
4695             #the property to delete is actually an attribute
4696             #in the delete element
4697             my $propname =
4698             $som->dataof("$base/[$jj]")->attr->{'resourceProperty'};
4699              
4700             #print "Delete $propname\n";
4701             #delete property
4702             if (
4703             !defined( $WSRF::MultiResourceProperties::NotDeletable{$propname}
4704             )
4705             )
4706             {
4707             $Multimydelete->( ID => $ID,
4708             property => $propname );
4709             }
4710             } else { #something other than Insert|Update|Delete
4711             die SOAP::Fault->faultcode("setResourceproperty: Failure")
4712             ->faultstring("setResourceProperty does not support $Function: only Insert, Update and Delete are supported"
4713             );
4714             }
4715             $jj++;
4716             }
4717             }
4718              
4719             return WSRF::Header::header($som);
4720             }
4721              
4722             sub InsertResourceProperties {
4723             my $som = pop @_;
4724             my $ID = getID($som);
4725             my $base = "//InsertResourceProperties";
4726             if ( $som->match($base) ) {
4727             my $kk = 1;
4728             while ( $som->match("$base/[1]/[$kk]") ) {
4729             my $name = $som->dataof("$base/[1]/[$kk]")->name();
4730             print "Inserting $name\n";
4731              
4732             #insert the thing - note we pass a SOM object becasue the
4733             #thing could be pretty complex.
4734             if ( !defined( $WSRF::MultiResourceProperties::NotInsert{$name} ) )
4735             {
4736             $Multiinsert->( ID => $ID,
4737             som => $som->match("$base/[1]/[$kk]") );
4738             } else {
4739             die "InvalidInsertResourcePropertiesRequestContent\n";
4740             }
4741             $kk++;
4742             }
4743             }
4744             return WSRF::Header::header($som);
4745             }
4746              
4747             sub UpdateResourceProperties {
4748             my $som = pop @_;
4749             my $ID = getID($som);
4750             my $base = "//UpdateResourceProperties";
4751             if ( $som->match($base) ) {
4752             my $kk = 1;
4753             my %tmpHash = ();
4754             while ( $som->match("$base/[1]/[$kk]") ) {
4755              
4756             #get name of thing we are updating
4757             my $name = $som->dataof("$base/[1]/[$kk]")->name();
4758             print "Updating $name\n";
4759             if (
4760             !defined( $WSRF::MultiResourceProperties::NotModifiable{$name}
4761             )
4762             )
4763             {
4764              
4765             #check we have not deleted it before else delete before inserting
4766             if ( !defined( $tmpHash{$name} ) ) {
4767             $Multimydelete->( ID => $ID,
4768             property => $name );
4769             $tmpHash{$name} = 1;
4770             }
4771              
4772             #insert value
4773             $Multiinsert->( ID => $ID,
4774             som => $som->match("$base/[1]/[$kk]") );
4775             } else {
4776             die "InvalidUpdateResourcePropertiesRequestContent\n";
4777             }
4778             $kk++;
4779             }
4780             }
4781              
4782             return WSRF::Header::header($som);
4783             }
4784              
4785             sub DeleteResourceProperties {
4786             my $som = pop @_;
4787             my $ID = getID($som);
4788             my $base = "//DeleteResourceProperties";
4789             if ( $som->match($base) ) {
4790             my $kk = 1;
4791             while ( $som->match("$base/[$kk]") ) {
4792             print "Into Loop inner...\n";
4793              
4794             #the property to delete is actually an attribute
4795             #in the delete element
4796             my $propname =
4797             $som->dataof("$base/[$kk]")->attr->{'ResourceProperty'};
4798             $propname =~ s/\w*://o;
4799              
4800             #delete property
4801             if (
4802             !defined( $WSRF::MultiResourceProperties::NotDeletable{$propname}
4803             )
4804             )
4805             {
4806             $Multimydelete->( ID => $ID,
4807             property => $propname );
4808             } else {
4809             die "InvalidDeleteResourcePropertiesRequestContent\n";
4810             }
4811             $kk++;
4812             }
4813             }
4814              
4815             return WSRF::Header::header($som);
4816             }
4817              
4818             #===============================================================================
4819             # The extension to WSRF::MultiResourceProperties that supports WSRL - adding
4820             # the operations Destroy and SetTerminationTime
4821             #
4822             package WSRF::MultiResourceLifetimes;
4823              
4824             =pod
4825              
4826             =head1 WSRF::MultiResourceLifetimes
4827              
4828             Extends WSRF::MultiResourceProperties to add support for WS-ResourceLifetime.
4829              
4830             =head2 METHODS
4831              
4832             =over
4833              
4834             =item Destroy
4835              
4836             =item SetTerminationTime
4837              
4838             =back
4839              
4840             =cut
4841              
4842             use vars qw(@ISA);
4843              
4844             @ISA = qw(WSRF::MultiResourceProperties);
4845              
4846             # wsrl Destroy
4847             sub Destroy {
4848             my $envelope = pop @_;
4849             my $ID = WSRF::MultiResourceProperties::getID($envelope);
4850              
4851             delete $WSRF::MultiResourceProperties::ResourceProperties->{$ID};
4852              
4853             #return nothing except a SOAP HEADER
4854             return WSRF::Header::header($envelope);
4855             }
4856              
4857             # wsrl SetTerminationTime
4858             sub SetTerminationTime {
4859             my $envelope = pop @_;
4860             shift @_; #the first paramter is always the class of the object
4861             my $time = shift @_; #the new TerminationTime
4862             my $ID = WSRF::MultiResourceProperties::getID($envelope);
4863              
4864             #check for null time - allowed by wsrl
4865             if ( $time eq "" ) {
4866             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4867             {'TerminationTime'} = "";
4868              
4869             my $ans =
4870             ""
4871             . ""
4872             . WSRF::Time::ConvertEpochTimeToString(time)
4873             . "";
4874              
4875             return WSRF::Header::header($envelope),
4876             SOAP::Data->value($ans)->type('xml');
4877             }
4878              
4879             #BUG - with DateTime::Format::W3CDTF - does not
4880             #like subseconds - should patch DateTime::Format::W3CDTF
4881             #print "Called SetTerminationTime: $time\n";
4882             $time =~ s/\.\d+//;
4883              
4884             #print "Setting TerminationTime to: $time\n";
4885              
4886             #test time is good - this will die if the string is faulty, causing
4887             #a SOAP fault to be sent to the client
4888             DateTime::Format::W3CDTF->new->parse_datetime($time);
4889              
4890             #reset TerminationTime
4891             $WSRF::MultiResourceProperties::ResourceProperties->{$ID}
4892             {'TerminationTime'} = $time;
4893              
4894             my $result = "$time";
4895             $result .=
4896             ""
4897             . WSRF::Time::ConvertEpochTimeToString()
4898             . "";
4899              
4900             return WSRF::Header::header($envelope),
4901             SOAP::Data->value($result)->type('xml');
4902             }
4903              
4904             #===============================================================================
4905             # This package is for supporting ServiceGroups:
4906             # http://www.globus.org/wsrf/specs/ws-servicegroup.pdf
4907             #
4908             # ServiceGroups allows you to bunch a set of WS-Resources
4909             # together. They are the building blocks of Registries
4910             #
4911             #
4912             package WSRF::ServiceGroup;
4913              
4914             =pod
4915              
4916             =head1 WSRF::ServiceGroup
4917              
4918             Provides support for WS-ServiceGroups. This implementation of WS-ServiceGroups
4919             stores the state of the WS-ServiceGroup in a file, it extends
4920             WSRF::FileBasedResourceLifetimes.
4921              
4922             =head2 METHODS
4923              
4924             =over
4925              
4926             =item Add
4927              
4928             Adds a WS-Resource to the ServiceGroup
4929              
4930             =item createServiceGroup
4931              
4932             Creates a new ServiceGroup
4933              
4934             =back
4935              
4936             =cut
4937              
4938             use vars qw(@ISA);
4939              
4940             @ISA = qw(WSRF::FileBasedResourceLifetimes);
4941              
4942             # foo is an array of things
4943             $WSRF::WSRP::ResourceProperties{Entry} = [];
4944             $WSRF::WSRP::PropertyNamespaceMap->{Entry}{prefix} = "wssg";
4945             $WSRF::WSRP::PropertyNamespaceMap->{Entry}{namespace} = $WSRF::Constants::WSSG;
4946             $WSRF::WSRP::NotDeletable{Entry} = 1; #Cannot delete through SetResourceProperty
4947             $WSRF::WSRP::NotModifiable{Entry} =
4948             1; #Cannot modify through SetResourceProperty
4949              
4950             $WSRF::WSRP::ResourceProperties{ServiceGroupEPR} = "";
4951             $WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{prefix} = "wssg";
4952             $WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{namespace} =
4953             $WSRF::Constants::WSSG;
4954             $WSRF::WSRP::NotDeletable{ServiceGroupEPR} =
4955             1; #Cannot delete through SetResourceProperty
4956             $WSRF::WSRP::NotModifiable{ServiceGroupEPR} =
4957             1; #Cannot modify through SetResourceProperty
4958              
4959             # The module name and path to use when creating a new entry
4960             # in the SG. Can be overridden by any module that subclasses this one.
4961             $WSRF::ServiceGroup::ServiceGroupEntryModule = "ServiceGroupEntry";
4962             $WSRF::ServiceGroup::ServiceGroupEntryPath = "Session/ServiceGroupEntry/";
4963              
4964             $WSRF::WSRP::InsertMap{ServiceGroupEPR} = sub {
4965             my ($som) = @_;
4966              
4967             print STDERR
4968             "ServiceGroup WSRF::WSRP::InsertMap{ServiceGroupEPR} called\n";
4969              
4970             my $serializer = new WSRF::SimpleSerializer;
4971              
4972             #print STDERR "$$ WSRF::ServiceGroup serializing ServiceGroupEPR\n";
4973             $WSRF::WSRP::ResourceProperties{ServiceGroupEPR} =
4974             $serializer->serialize( $som->dataof('[1]') );
4975             };
4976              
4977             $WSRF::WSRP::InsertMap{Entry} = sub {
4978             my ($som) = @_;
4979              
4980             print STDERR "ServiceGroup WSRF::WSRP::InsertMap{Entry} called\n";
4981              
4982             my $serializer = new WSRF::SimpleSerializer;
4983              
4984             #We store the entry as follows
4985             # MemberServiceEPR
4986             # ServiceGroupEntryEPR
4987             # Content (optional)
4988             # EntryTerminationTime
4989             #We will use EntryTerminationTime as a marker
4990              
4991             #get MemberServiceEPR
4992             my $Entry = $serializer->serialize( $som->dataof('[1]') );
4993              
4994             #get ServiceGroupEntryEPR
4995             $Entry .= $serializer->serialize( $som->dataof('[2]') );
4996              
4997             #Get the Content
4998             my $ContentorTime = $serializer->serialize( $som->dataof('[3]') );
4999              
5000             my $Time = "";
5001             if ( $ContentorTime =~ m/EntryTerminationTime/o ) {
5002             $Time = $ContentorTime;
5003             $Entry .= $Time;
5004             } else {
5005             $Entry .= $ContentorTime;
5006             $Time = $serializer->serialize( $som->dataof('[4]') );
5007             $Entry .= $Time;
5008             }
5009              
5010             #print STDERR "$$ Entry= $Entry\n\n";
5011              
5012             #strip xml tags away from time
5013             $Time =~ s/<\/?EntryTerminationTime\/?>//og;
5014              
5015             #print STDERR "$$ TerminationTime for Entry= $Time\n";
5016              
5017             if ( $Time eq "nil" ) #No TerminationTime
5018             {
5019             push( @{ $WSRF::WSRP::ResourceProperties{Entry} }, $Entry );
5020             } else {
5021              
5022             #check TerminationTime
5023             if ( WSRF::Time::ConvertStringToEpochTime($Time) > time ) {
5024             push( @{ $WSRF::WSRP::ResourceProperties{Entry} }, $Entry );
5025             }
5026             }
5027              
5028             };
5029              
5030             my $strip_old_Entries = sub {
5031             my $parser = new XML::DOM::Parser;
5032             my @tmp = @{ $WSRF::WSRP::ResourceProperties{Entry} };
5033             @{ $WSRF::WSRP::ResourceProperties{Entry} } = ();
5034             foreach my $entry (@tmp) {
5035             my $tmpentry = "" . $entry . "";
5036             my $doc = $parser->parse($tmpentry);
5037              
5038             #print STDERR "Parsed document..\n";
5039             my $TermTime =
5040             defined( $doc->getElementsByTagName("EntryTerminationTime")->item(0)
5041             ->getFirstChild )
5042             ? $doc->getElementsByTagName("EntryTerminationTime")->item(0)
5043             ->getFirstChild->getNodeValue
5044             : "";
5045              
5046             next
5047             if ( ( $TermTime ne "nil" )
5048             && ( WSRF::Time::ConvertStringToEpochTime($TermTime) < time ) );
5049              
5050             push @{ $WSRF::WSRP::ResourceProperties{Entry} }, $entry;
5051             $doc->dispose;
5052             }
5053              
5054             };
5055              
5056             # wsrp GetResourceProperty
5057             sub GetResourceProperty {
5058             my $self = shift @_;
5059             my $envelope = pop @_;
5060              
5061             my $lock = WSRF::File->new($envelope);
5062             $strip_old_Entries->();
5063              
5064             my $search = $envelope->valueof('//GetResourceProperty/');
5065              
5066             #strip namespace - BUG we should handle namespaces properly and
5067             #not just ignore them
5068             $search =~ s/\w*://o;
5069              
5070             my $ans = "";
5071              
5072             #print STDERR "GetResourceProperty = $search\n";
5073             if ( $search eq "Entry" ) {
5074             foreach my $entry ( @{ $WSRF::WSRP::ResourceProperties{Entry} } ) {
5075             $ans .= "";
5076              
5077             #BUG - why must we take a copy?
5078             my $tmp = $entry;
5079             $tmp =~ s///o;
5080             $tmp =~ s/\w*<\/EntryTerminationTime>//o;
5081             $ans .= $tmp;
5082             $ans .= "";
5083             }
5084             } else {
5085             $ans = WSRF::WSRP::searchResourceProperty($search);
5086             }
5087              
5088             $lock->toFile();
5089             return WSRF::Header::header($envelope),
5090             SOAP::Data->value($ans)->type('xml');
5091             }
5092              
5093             # wsrp GetMultipleResourceProperties
5094             sub GetMultipleResourceProperties {
5095             my $self = shift @_;
5096             my $envelope = pop @_;
5097             my $lock = WSRF::File->new($envelope);
5098             $strip_old_Entries->();
5099              
5100             #print ">>>>BEFORE>>>>\n".WSRF::WSRP::xmlizeProperties()."\n<<<<<<<<<<<<\n\n";
5101              
5102             my $ans = ""; #we will just cat the answers together
5103              
5104             # print "XML>>>\n".$xmlizeProperties->($ID)."\n<<
5105              
5106             #loop over each ResourceProperty request
5107             foreach my $search ( $envelope->valueof('//ResourceProperty/') ) {
5108              
5109             #strip namespace
5110             $search =~ s/\w*://o;
5111             if ( $search eq "Entry" ) {
5112             foreach my $entry ( @{ $WSRF::WSRP::ResourceProperties{Entry} } ) {
5113             $ans .= "";
5114              
5115             #BUG - why must we take a copy?
5116             my $tmp = $entry;
5117             $tmp =~ s///o;
5118             $tmp =~ s/\w*<\/EntryTerminationTime>//o;
5119             $ans .= $tmp;
5120             $ans .= "";
5121             }
5122             } else {
5123             $ans .= WSRF::WSRP::searchResourceProperty($search);
5124             }
5125             }
5126              
5127             #print STDERR ">>>>AFTER>>>>\n".WSRF::WSRP::xmlizeProperties()."\n<<<<<<<<<<<<\n\n";
5128              
5129             $lock->toFile();
5130             return WSRF::Header::header($envelope),
5131             SOAP::Data->value($ans)->type('xml');
5132             }
5133              
5134             # operation to create a new File based Counter
5135             sub createServiceGroup {
5136             my $envelope = pop @_;
5137             my ( $class, @params ) = @_;
5138              
5139             # get an ID for the Resource
5140             my $ID = WSRF::GSutil::CalGSH_ID();
5141              
5142             #create a WS-Address for the Resource
5143             my $wsa = WSRF::GSutil::createWSAddress(
5144             module => 'ServiceGroup',
5145             path => 'Session/ServiceGroup/',
5146             ID => $ID
5147             );
5148              
5149             $WSRF::WSRP::ResourceProperties{ServiceGroupEPR} = $wsa;
5150              
5151             #write the properties to a file
5152             WSRF::File::toFile($ID);
5153              
5154             #return the WS-Address
5155             return WSRF::Header::header($envelope),
5156             SOAP::Data->value($wsa)->type('xml');
5157             }
5158              
5159             # add an entry to the SG
5160             sub Add {
5161             my $envelope = pop @_; #get the SOAP envelope
5162             my $lock = WSRF::File->new($envelope); #get the properties from the file
5163             $strip_old_Entries->();
5164             my ( $class, $val ) = @_; #get the operation paramaters
5165              
5166             my $serializer = new WSRF::SimpleSerializer;
5167              
5168             #print "$$ Message::\n".$serializer->serialize( $envelope->dataof('/') )."\n\n";
5169              
5170             # BUG
5171             # We cannot use the following to get the MemberEPR
5172             # my $mepr = $serializer->serialize( $envelope->dataof('//MemberEPR/[1]') );
5173             # because it screws up the namespaces - SimpleSerializer cannot
5174             # handle more than one namespace in a message.
5175              
5176             my $mepraddress =
5177             $envelope->match("//MemberEPR//{$WSRF::Constants::WSA}Address")
5178             ? $envelope->valueof("//MemberEPR//{$WSRF::Constants::WSA}Address")
5179             : die "No MemberEPR in Add message\n"; #BUG - BaseFault
5180              
5181             #check for ReferenceParameters
5182             my ($RefParam);
5183             if ( $envelope->dataof('//MemberEPR//ReferenceParameters/*') ) {
5184             my $i = 0;
5185             foreach
5186             my $a ( $envelope->dataof('//MemberEPR//ReferenceParameters/*') )
5187             {
5188             $i++;
5189             my $name = $a->name();
5190             my $uri = $a->uri();
5191             my $value = $a->value();
5192             $RefParam .=
5193             "
5194             . " xmlns:myns"
5195             . $i . "=\""
5196             . $uri . "\">"
5197             . $value
5198             . "
5199             . $i . ":"
5200             . $name . ">";
5201             }
5202             }
5203              
5204             my $mepr = "";
5205             $mepr .= "$mepraddress";
5206             $mepr .= $RefParam ? $RefParam : "";
5207             $mepr .= "";
5208              
5209             $mepr = "$mepr";
5210              
5211             #print STDERR "$$ MEPR = $mepr\n";
5212              
5213             my $content = "";
5214             if ( defined( $envelope->dataof('//Content/[1]') ) ) {
5215              
5216             #print "Content!! ". $envelope->dataof('//Content') ."\n";
5217             $content = $serializer->serialize( $envelope->dataof('//Content/[1]') );
5218              
5219             $content = "$content";
5220             }
5221              
5222             # print STDERR "Content = $content\n";
5223              
5224             my $termTime = "nil";
5225             if ( defined( $envelope->valueof('//InitialTerminationTime') ) ) {
5226             $termTime = $envelope->valueof('//InitialTerminationTime');
5227              
5228             #BUG with DateTime::Format::W3CDTF - does not
5229             #like subseconds - should patch DateTime::Format::W3CDTF
5230             #print "Called SetTerminationTime: $time\n";
5231             $termTime =~ s/\.\d+//;
5232              
5233             #print "Setting TerminationTime to: $time\n";
5234              
5235             #test time is good - this will die if the string is faulty, causing
5236             #a SOAP fault to be sent to the client
5237             #BUG should eval this and throw a WS-BaseFault
5238             DateTime::Format::W3CDTF->new->parse_datetime($termTime);
5239             }
5240              
5241             $termTime = "$termTime";
5242              
5243             # get an ID for the new ServiceGroupEntry
5244             my $ID = WSRF::GSutil::CalGSH_ID();
5245             $ID = $lock->ID() . "-" . $ID;
5246              
5247             #print STDERR "ServiceGroup ID = ".$lock->ID()."\n";
5248             #print STDERR "ServiceGroupEntry ID = $ID\n";
5249              
5250             my $sge_wsa = WSRF::GSutil::createWSAddress(
5251             module => $WSRF::ServiceGroup::ServiceGroupEntryModule,
5252             path => $WSRF::ServiceGroup::ServiceGroupEntryPath,
5253             ID => $ID
5254             );
5255              
5256             my $ans = $sge_wsa;
5257             $sge_wsa =
5258             "$sge_wsa";
5259              
5260             my $Entry = $mepr . $sge_wsa . $content . $termTime;
5261              
5262             push( @{ $WSRF::WSRP::ResourceProperties{Entry} }, $Entry );
5263              
5264             $lock->toFile(); #put the properties back in the file
5265             return WSRF::Header::header($envelope), #return result
5266             SOAP::Data->value($ans)->type('xml');
5267             }
5268              
5269             #===============================================================================
5270              
5271             package WSRF::ServiceGroupEntry;
5272              
5273             =pod
5274              
5275             =head1 WSRF::ServiceGroupEntry
5276              
5277             Provides support for ServiceGroupEntry WS-Resources defined in the
5278             WS-ServiceGroup specification. Each ServiceGroupEntry WS-Resource
5279             represents an entry in a ServiceGroup, destroy the ServiceGroupEntry
5280             and the entry disappears from the ServiceGroup.
5281              
5282             =head2 METHODS
5283              
5284             =over
5285              
5286             =item GetResourcePropertyDocument
5287              
5288             =item GetResourceProperty
5289              
5290             =item GetMultipleResourceProperties
5291              
5292             =item SetResourceProperties
5293              
5294             =item Destroy
5295              
5296             =item SetTerminationTime
5297              
5298             =back
5299              
5300             =cut
5301              
5302             use vars qw(@ISA);
5303             use XML::DOM;
5304             use Storable qw(lock_store lock_nstore lock_retrieve);
5305              
5306             @ISA = qw(WSRF::WSRL);
5307              
5308             # foo is an array of things
5309             $WSRF::WSRP::ResourceProperties{Content} = "";
5310             $WSRF::WSRP::PropertyNamespaceMap->{Content}{prefix} = "wssg";
5311             $WSRF::WSRP::PropertyNamespaceMap->{Content}{namespace} =
5312             $WSRF::Constants::WSSG;
5313             $WSRF::WSRP::NotDeletable{Content} =
5314             1; #Cannot delete through SetResourceProperty
5315             $WSRF::WSRP::NotModifiable{Content} =
5316             1; #Cannot modify through SetResourceProperty
5317              
5318             $WSRF::WSRP::ResourceProperties{ServiceGroupEPR} = "";
5319             $WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{prefix} = "wssg";
5320             $WSRF::WSRP::PropertyNamespaceMap->{ServiceGroupEPR}{namespace} =
5321             $WSRF::Constants::WSSG;
5322             $WSRF::WSRP::NotDeletable{ServiceGroupEPR} =
5323             1; #Cannot delete through SetResourceProperty
5324             $WSRF::WSRP::NotModifiable{ServiceGroupEPR} =
5325             1; #Cannot modify through SetResourceProperty
5326              
5327             $WSRF::WSRP::ResourceProperties{MemberEPR} = "";
5328             $WSRF::WSRP::PropertyNamespaceMap->{MemberEPR}{prefix} = "wssg";
5329             $WSRF::WSRP::PropertyNamespaceMap->{MemberEPR}{namespace} =
5330             $WSRF::Constants::WSSG;
5331             $WSRF::WSRP::NotDeletable{MemberEPR} =
5332             1; #Cannot delete through SetResourceProperty
5333             $WSRF::WSRP::NotModifiable{MemberEPR} =
5334             1; #Cannot modify through SetResourceProperty
5335              
5336             my $fromFile = sub {
5337              
5338             # get ID
5339             my ( $envelope, %args ) = @_;
5340              
5341             foreach my $key ( keys %args ) {
5342             print "$$ fromFile $key => " . $args{$key} . "\n";
5343             }
5344             if ( defined( $args{Destroy} ) ) {
5345             print "$$ fromFile Attempt to Destroy\n";
5346             }
5347              
5348             my $address = $envelope->headerof("//{$WSRF::Constants::WSA}To");
5349             if ( defined $address ) {
5350             $address = $envelope->headerof("//{$WSRF::Constants::WSA}To")->value;
5351             } else {
5352             print STDERR "ERROR: No ResourceID in the SOAP Header\n";
5353             die SOAP::Fault->faultcode("No WS-Resource Identifier")
5354             ->faultstring("No WS-Resource identifier in SOAP Header");
5355             }
5356              
5357             my @PathArray = split( /\//, $address );
5358             my $ID = pop @PathArray;
5359              
5360             #check the ID is safe - we do not accept dots,
5361             #all paths will be relative to $ENV{WRF_MODULES}
5362             #only allow alphanumeric, underscore and hyphen
5363             if ( $ID =~ /^([-\w]+)$/ ) {
5364             $ID = $1;
5365             } else {
5366             print STDERR "ERROR: Bad ResourceID $ID in SOAP Header\n";
5367             die SOAP::Fault->faultcode("Badly formed WS-Resource Identifier")
5368             ->faultstring("Badly formed WS-Resource Identifier in SOAP Header");
5369             }
5370              
5371             $ENV{ID} = $ID;
5372              
5373             my $ID_clipped = $ID;
5374              
5375             #ID can be of the form 1341-4565, we use this form to all multiple
5376             #WS-Resources to share the same state, the state is in the file
5377             #1341 - we use this with ServiceGroup/ServiceGroupEntry
5378             $ID_clipped =~ s/-\w*//o;
5379              
5380             my $path = $WSRF::Constants::Data . $ID_clipped;
5381              
5382             if ( !( -e $path ) ) {
5383             print STDERR "ERROR: No Resource $path\n";
5384             die SOAP::Fault->faultcode("No such WS-Resource")
5385             ->faultstring("No WS-Resource with identifier $ID");
5386             }
5387              
5388             my $lock = $path . ".lock";
5389              
5390             my $Lock = WSRF::FileLock->new($lock);
5391              
5392             my $hashref = Storable::lock_retrieve($path);
5393              
5394             %WSRF::WSRP::ResourceProperties =
5395             ( %WSRF::WSRP::ResourceProperties, %{ $hashref->{Properties} } );
5396              
5397             %WSRF::WSRP::Private = ( %WSRF::WSRP::Private, %{ $hashref->{Private} } );
5398              
5399             # print STDERR "$$ fromFile about to enter loop\n";
5400             my $parser = new XML::DOM::Parser;
5401             my $found = 0;
5402             my ( $doc, $TerminationTime, $MEPR, $Content, $Destroyed );
5403             my @tmp = @{ $WSRF::WSRP::ResourceProperties{Entry} };
5404             @{ $WSRF::WSRP::ResourceProperties{Entry} } = ();
5405              
5406             # print "$$ Number of Entries= @tmp\n";
5407             foreach my $entry (@tmp) {
5408              
5409             # print STDERR $entry."\n";
5410             my $tmpentry = "" . $entry . "";
5411             $doc = $parser->parse($tmpentry);
5412              
5413             #print STDERR "Parsed document..\n";
5414             my $TermTime =
5415             defined( $doc->getElementsByTagName("EntryTerminationTime")->item(0)
5416             ->getFirstChild )
5417             ? $doc->getElementsByTagName("EntryTerminationTime")->item(0)
5418             ->getFirstChild->getNodeValue
5419             : "";
5420              
5421             if ( ( $TermTime ne "nil" )
5422             && ( WSRF::Time::ConvertStringToEpochTime($TermTime) < time ) )
5423             {
5424             print STDERR "Deleting Node\n";
5425             next;
5426             }
5427              
5428             my $subnodes = $doc->getElementsByTagName("wssg:ServiceGroupEntryEPR");
5429              
5430             # print "Length= ".$subnodes->getLength."\n";
5431             my $ResourceID = $subnodes->item(0)->getElementsByTagName("Address");
5432             if ( $ResourceID->getLength == 0 ) {
5433             $ResourceID =
5434             $subnodes->item(0)->getElementsByTagName("wsa:Address");
5435             }
5436              
5437             # print "$$ ResourceID Length= ".$ResourceID->getLength."\n";
5438             $ResourceID = $ResourceID->item(0)->getFirstChild->getNodeValue;
5439              
5440             # print STDERR "$$ ResourceID = $ResourceID\n";
5441             if ( $ResourceID eq $address ) #found node we want
5442             {
5443             print STDERR "$$ ResourceIDs match\n";
5444             $TerminationTime = ( $TermTime eq "nil" ) ? "" : $TermTime;
5445             $Content =
5446             $doc->getElementsByTagName("wssg:Content")->item(0)
5447             ->getFirstChild->toString;
5448             $MEPR =
5449             $doc->getElementsByTagName("wssg:MemberServiceEPR")->item(0)
5450             ->getFirstChild->toString;
5451             $found = 1;
5452             if ( defined( $args{Destroy} ) ) {
5453              
5454             # print STDERR "$$ Destroying ServiceGroupEntry $ID\n";
5455             $Destroyed = "True";
5456             next;
5457             }
5458             if ( defined( $args{TerminationTime} ) ) {
5459             $doc->getElementsByTagName("EntryTerminationTime")->item(0)
5460             ->getFirstChild->setNodeValue( $args{TerminationTime} );
5461             }
5462             my $foo = $doc->toString;
5463             $foo =~ s/<\/?t>//og;
5464             $entry = $foo;
5465             }
5466             push @{ $WSRF::WSRP::ResourceProperties{Entry} }, $entry;
5467             $doc->dispose;
5468             }
5469              
5470             my %tmpPrivate = (%WSRF::WSRP::Private);
5471              
5472             #should use map?
5473             foreach my $key ( keys %tmpPrivate ) {
5474             if ( ref( $tmpPrivate{$key} ) eq "CODE" ) {
5475             delete $tmpPrivate{$key};
5476             }
5477             }
5478              
5479             #take a copy of the ResourceProperties to copy to file
5480             my %tmphash = (%WSRF::WSRP::ResourceProperties);
5481             foreach my $key ( keys %tmphash ) {
5482             if ( ref( $tmphash{$key} ) eq "CODE" ) {
5483             delete $tmphash{$key};
5484             }
5485             }
5486              
5487             my %tmpStore = ( Properties => \%tmphash, Private => \%tmpPrivate );
5488              
5489             local $Storable::forgive_me = "TRUE";
5490             lock_store \%tmpStore, $path;
5491              
5492             #ServiceGroupEntry not found
5493             if ( !$found && !$Destroyed ) {
5494             die SOAP::Fault->faultcode("No such WS-Resource")
5495             ->faultstring("No WS-Resource with identifier $address");
5496             }
5497              
5498             $WSRF::WSRP::ResourceProperties{TerminationTime} = $TerminationTime;
5499             $WSRF::WSRP::ResourceProperties{Content} = $Content;
5500             $WSRF::WSRP::ResourceProperties{MemberEPR} = $MEPR;
5501              
5502             return $path;
5503             };
5504              
5505             sub GetResourceProperty {
5506             my $self = shift @_;
5507             my $envelope = pop @_;
5508             $fromFile->($envelope);
5509              
5510             # print STDERR "ServiceGroupEntry::GetResourceProperty Dumping Properties..\n";
5511             # foreach my $key ( keys %WSRF::WSRP::ResourceProperties )
5512             # {
5513             # print " $key: ".$WSRF::WSRP::ResourceProperties{$key}."\n";
5514             # }
5515             my @resp = $self->SUPER::GetResourceProperty($envelope);
5516             return @resp;
5517             }
5518              
5519             sub GetResourcePropertyDocument {
5520             my $self = shift @_;
5521             my $envelope = pop @_;
5522             $fromFile->($envelope);
5523             my @resp = $self->SUPER::GetResourcePropertyDocument($envelope);
5524             return @resp;
5525             }
5526              
5527             sub SetResourceProperties {
5528             my $self = shift @_;
5529             my $envelope = pop @_;
5530             my $path = $fromFile->($envelope);
5531             my @resp = $self->SUPER::SetResourceProperties($envelope);
5532             return @resp;
5533             }
5534              
5535             sub GetMultipleResourceProperties {
5536             my $self = shift @_;
5537             my $envelope = pop @_;
5538             my $path = $fromFile->($envelope);
5539             my @resp = $self->SUPER::GetMultipleResourceProperties($envelope);
5540             return @resp;
5541             }
5542              
5543             sub Destroy {
5544              
5545             # get ID
5546             my ($envelope) = pop @_;
5547             print STDERR "$$ WSRF::ServiceGroupEntry Destroy invoked\n";
5548             $fromFile->( $envelope, Destroy => 1 );
5549             return WSRF::Header::header($envelope);
5550             }
5551              
5552             sub SetTerminationTime {
5553              
5554             # get ID
5555             my ($envelope) = pop @_;
5556             shift @_; #the first paramter is always the class of the object
5557             my $time = shift @_;
5558              
5559             #print STDERR "time= $time\n";
5560              
5561             #BUG with DateTime::Format::W3CDTF - does not
5562             #like subseconds - should patch DateTime::Format::W3CDTF
5563             #print "Called SetTerminationTime: $time\n";
5564             $time =~ s/\.\d+//;
5565              
5566             #check time is in good format - otherwise die!
5567             DateTime::Format::W3CDTF->new->parse_datetime($time);
5568              
5569             $fromFile->( $envelope, TerminationTime => $time );
5570              
5571             my $result = "$time";
5572             $result .=
5573             ""
5574             . WSRF::Time::ConvertEpochTimeToString()
5575             . "";
5576              
5577             return WSRF::Header::header($envelope),
5578             SOAP::Data->value($result)->type('xml');
5579              
5580             }
5581              
5582             # ======================================================================
5583              
5584             package WSRF;
5585              
5586             use vars qw($AUTOLOAD);
5587             require URI;
5588              
5589             my $soap; # shared between SOAP and SOAP::Lite packages
5590              
5591             {
5592             no strict 'refs';
5593             *AUTOLOAD = sub {
5594             local ( $1, $2 );
5595             my ( $package, $method ) = $AUTOLOAD =~ m/(?:(.+)::)([^:]+)$/;
5596             return if $method eq 'DESTROY';
5597              
5598             my $soap =
5599             ref $_[0] && UNIVERSAL::isa( $_[0] => 'SOAP::Lite' ) ? $_[0] : $soap
5600             || die
5601             "SOAP:: prefix shall only be used in combination with +autodispatch option\n";
5602              
5603             my $uri = URI->new( $soap->uri );
5604             my $currenturi = $uri->path;
5605             $package =
5606             ref $_[0] && UNIVERSAL::isa( $_[0] => 'SOAP::Lite' )
5607             ? $currenturi
5608             : $package eq 'SOAP'
5609             ? ref $_[0]
5610             || ( $_[0] eq 'SOAP'
5611             ? $currenturi || Carp::croak "URI is not specified for method call"
5612             : $_[0] )
5613             : $package eq 'main'
5614             ? $currenturi || $package
5615             : $package;
5616              
5617             # drop first parameter if it's a class name
5618             {
5619             my $pack = $package;
5620             for ($pack) { s!^/!!; s!/!::!g; }
5621             shift @_
5622             if @_ && !ref $_[0] && ( $_[0] eq $pack || $_[0] eq 'SOAP' )
5623             || ref $_[0] && UNIVERSAL::isa( $_[0] => 'SOAP::Lite' );
5624             }
5625              
5626             for ($package) { s!::!/!g; s!^/?!/!; }
5627             $uri->path($package);
5628              
5629             my $som = $soap->uri( $uri->as_string )->call( $method => @_ );
5630             UNIVERSAL::isa( $som => 'SOAP::SOM' )
5631             ? wantarray ? $som->paramsall : $som->result
5632             : $som;
5633             };
5634             }
5635              
5636             # ======================================================================
5637             # Copyright (C) 2000-2004 Paul Kulchenko (paulclinger@yahoo.com)
5638             # SOAP::Lite is free software; you can redistribute it
5639             # and/or modify it under the same terms as Perl itself.
5640              
5641             package WSRF::Lite;
5642              
5643             =pod
5644              
5645             =head1 WSRF::Lite
5646              
5647             Extends SOAP::Lite to provide support for WS-Addressing.
5648             WSRF::Lite uses WSRF::WSRFSerializer and WSRF::Deserializer
5649             by default, it will also automatically include the WS-Addressing
5650             SOAP headers in the SOAP message. If $ENV{WSS} is set to true,
5651             $ENV{HTTPS_CERT_FILE} points to the public part of a X.509
5652             certificate and $ENV{HTTPS_KEY_FILE} points to the unencrypted
5653             private key of the certificate then WSRF::Lite will digitally
5654             sign the message according to the WS-Security specification.
5655              
5656             =head2 METHODS
5657              
5658             WSRF::Lite supports the same set of methods as SOAP::Lite with the
5659             addition of wsaddess.
5660              
5661             =over
5662              
5663             =item wsaddress
5664              
5665             This can be used instead of the proxy method, it takes a WSRF::WS_Address
5666             object for the address of the service or WS-Resource:
5667            
5668             $ans= WSRF::Lite
5669             -> uri($uri)
5670             -> wsaddress(WSRF::WS_Address->new()->Address($target))
5671             -> createCounterResource();
5672            
5673             =back
5674              
5675             =cut
5676              
5677             use vars qw($AUTOLOAD @ISA);
5678             use Carp ();
5679              
5680             use SOAP::Packager;
5681              
5682             @ISA = qw(SOAP::Cloneable);
5683              
5684             # provide access to global/autodispatched object
5685             sub self { @_ > 1 ? $soap = $_[1] : $soap }
5686              
5687             # no more warnings about "used only once"
5688             *UNIVERSAL::AUTOLOAD if 0;
5689              
5690             sub autodispatched { \&{*UNIVERSAL::AUTOLOAD} eq \&{*SOAP::AUTOLOAD} }
5691              
5692             sub soapversion {
5693             my $self = shift;
5694             my $version = shift or return $SOAP::Constants::SOAP_VERSION;
5695              
5696             ($version) =
5697             grep { $SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV} eq $version }
5698             keys %SOAP::Constants::SOAP_VERSIONS
5699             unless exists $SOAP::Constants::SOAP_VERSIONS{$version};
5700              
5701             die qq!$SOAP::Constants::WRONG_VERSION Supported versions:\n@{[
5702             join "\n", map {" $_ ($SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV})"} keys %SOAP::Constants::SOAP_VERSIONS
5703             ]}\n!
5704             unless defined($version)
5705             && defined( my $def = $SOAP::Constants::SOAP_VERSIONS{$version} );
5706              
5707             foreach ( keys %$def ) {
5708             eval
5709             "\$SOAP::Constants::$_ = '$SOAP::Constants::SOAP_VERSIONS{$version}->{$_}'";
5710             }
5711              
5712             $SOAP::Constants::SOAP_VERSION = $version;
5713             $self;
5714             }
5715              
5716             BEGIN { WSRF::Lite->soapversion(1.1) }
5717              
5718             sub import {
5719             my $pkg = shift;
5720             my $caller = caller;
5721             no strict 'refs';
5722              
5723             # emulate 'use SOAP::Lite 0.99' behavior
5724             $pkg->require_version(shift) if defined $_[0] && $_[0] =~ /^\d/;
5725              
5726             while (@_) {
5727             my $command = shift;
5728              
5729             my @parameters =
5730             UNIVERSAL::isa( $_[0] => 'ARRAY' ) ? @{ shift() } : shift
5731             if @_ && $command ne 'autodispatch';
5732             if ( $command eq 'autodispatch' || $command eq 'dispatch_from' ) {
5733             $soap = ( $soap || $pkg )->new;
5734             no strict 'refs';
5735             foreach ( $command eq 'autodispatch' ? 'UNIVERSAL' : @parameters ) {
5736             my $sub = "${_}::AUTOLOAD";
5737             defined &{*$sub}
5738             ? ( \&{*$sub} eq \&{*SOAP::AUTOLOAD}
5739             ? ()
5740             : Carp::croak
5741             "$sub already assigned and won't work with DISPATCH. Died"
5742             )
5743             : ( *$sub = *SOAP::AUTOLOAD );
5744             }
5745             } elsif ( $command eq 'service' ) {
5746             foreach (
5747             keys %{ SOAP::Schema->schema_url( shift(@parameters) )
5748             ->parse(@parameters)->load->services
5749             }
5750             )
5751             {
5752             $_->export_to_level( 1, undef, ':all' );
5753             }
5754             } elsif ( $command eq 'debug' || $command eq 'trace' ) {
5755             SOAP::Trace->import( @parameters ? @parameters : 'all' );
5756             } elsif ( $command eq 'import' ) {
5757             local $^W; # supress warnings about redefining
5758             my $package = shift(@parameters);
5759             $package->export_to_level( 1, undef,
5760             @parameters ? @parameters : ':all' )
5761             if $package;
5762             } else {
5763             Carp::carp
5764             "Odd (wrong?) number of parameters in import(), still continue"
5765             if $^W && !( @parameters & 1 );
5766             $soap = ( $soap || $pkg )->$command(@parameters);
5767             }
5768             }
5769             }
5770              
5771             sub DESTROY { SOAP::Trace::objects('()') }
5772              
5773             sub new {
5774             my $self = shift;
5775             return $self if ref $self;
5776             unless ( ref $self ) {
5777             my $class = ref($self) || $self;
5778              
5779             # Check whether we can clone. Only the SAME class allowed, no inheritance
5780             $self = ref($soap) eq $class ? $soap->clone : {
5781             _transport => SOAP::Transport->new,
5782             _serializer => WSRF::WSRFSerializer->new,
5783             _deserializer => WSRF::Deserializer->new,
5784             _packager => SOAP::Packager::MIME->new,
5785             _schema => undef,
5786             _wsaddress => undef,
5787             _autoresult => 0,
5788             _on_action => sub { sprintf '"%s#%s"', shift || '', shift },
5789             _on_fault => sub {
5790             ref $_[1] ? return $_[1]
5791             : Carp::croak $_[0]->transport->is_success ? $_[1]
5792             : $_[0]->transport->status;
5793             },
5794             };
5795             bless $self => $class;
5796             $self->on_nonserialized( $self->on_nonserialized
5797             || $self->serializer->on_nonserialized );
5798             SOAP::Trace::objects('()');
5799             }
5800              
5801             Carp::carp "Odd (wrong?) number of parameters in new()"
5802             if $^W && ( @_ & 1 );
5803             while (@_) {
5804             my ( $method, $params ) = splice( @_, 0, 2 );
5805             $self->can($method)
5806             ? $self->$method( ref $params eq 'ARRAY' ? @$params : $params )
5807             : $^W && Carp::carp "Unrecognized parameter '$method' in new()";
5808             }
5809              
5810             return $self;
5811             }
5812              
5813             sub init_context {
5814             my $self = shift->new;
5815             $self->{'_deserializer'}->{'_context'} = $self;
5816             $self->{'_serializer'}->{'_context'} = $self;
5817             }
5818              
5819             sub destroy_context {
5820             my $self = shift;
5821             delete( $self->{'_deserializer'}->{'_context'} );
5822             delete( $self->{'_serializer'}->{'_context'} );
5823             }
5824              
5825             # Naming? wsdl_parser
5826             sub schema {
5827             my $self = shift;
5828             if (@_) {
5829             $self->{'_schema'} = shift;
5830             return $self;
5831             } else {
5832             if ( !defined $self->{'_schema'} ) {
5833             $self->{'_schema'} = SOAP::Schema->new;
5834             }
5835             return $self->{'_schema'};
5836             }
5837             }
5838              
5839             sub BEGIN {
5840             no strict 'refs';
5841             for my $method (qw(serializer deserializer)) {
5842             my $field = '_' . $method;
5843             *$method = sub {
5844             my $self = shift->new;
5845             if (@_) {
5846             my $context =
5847             $self->{$field}->{'_context'}; # save the old context
5848             $self->{$field} = shift;
5849             $self->{$field}->{'_context'} =
5850             $context; # restore the old context
5851             return $self;
5852             } else {
5853             return $self->{$field};
5854             }
5855             }
5856             }
5857             for my $method (
5858             qw(endpoint transport outputxml autoresult packager wsaddress))
5859             {
5860             my $field = '_' . $method;
5861             *$method = sub {
5862             my $self = shift->new;
5863             @_
5864             ? ( $self->{$field} = shift, return $self )
5865             : return $self->{$field};
5866             }
5867             }
5868             for my $method (qw(on_action on_fault on_nonserialized)) {
5869             my $field = '_' . $method;
5870             *$method = sub {
5871             my $self = shift->new;
5872             return $self->{$field} unless @_;
5873             local $@;
5874              
5875             # commented out because that 'eval' was unsecure
5876             # > ref $_[0] eq 'CODE' ? shift : eval shift;
5877             # Am I paranoid enough?
5878             $self->{$field} = shift;
5879             Carp::croak $@ if $@;
5880             Carp::croak
5881             "$method() expects subroutine (CODE) or string that evaluates into subroutine (CODE)"
5882             unless ref $self->{$field} eq 'CODE';
5883             return $self;
5884             }
5885             }
5886              
5887             # SOAP::Transport Shortcuts
5888             # TODO - deprecate proxy() in favor of new language endpoint_url()
5889             for my $method (qw(proxy)) {
5890             *$method = sub {
5891             my $self = shift->new;
5892             if (@_) {
5893             my $endpoint = shift @_;
5894             if ( UNIVERSAL::isa( $endpoint => 'WSRF::WS_Address' ) ) {
5895             $self->{_wsaddress} = $endpoint;
5896             $endpoint = $endpoint->Address();
5897             }
5898             $self->transport->$method( $endpoint, @_ );
5899             return $self;
5900             }
5901             return $self->transport->$method();
5902             }
5903             }
5904              
5905             # SOAP::Seriailizer Shortcuts
5906             for my $method (
5907             qw(autotype readable envprefix encodingStyle
5908             encprefix multirefinplace encoding typelookup uri
5909             header maptype xmlschema use_prefix ns default_ns)
5910             )
5911             {
5912             *$method = sub {
5913             my $self = shift->new;
5914             @_
5915             ? ( $self->serializer->$method(@_), return $self )
5916             : return $self->serializer->$method();
5917             }
5918             }
5919              
5920             # SOAP::Schema Shortcuts
5921             for my $method (qw(cache_dir cache_ttl)) {
5922             *$method = sub {
5923             my $self = shift->new;
5924             @_
5925             ? ( $self->schema->$method(@_), return $self )
5926             : return $self->schema->$method();
5927             }
5928             }
5929             }
5930              
5931             sub parts {
5932             my $self = shift;
5933             $self->packager->parts(@_);
5934             return $self;
5935             }
5936              
5937             # Naming? wsdl
5938             sub service {
5939             my $self = shift->new;
5940             return $self->{'_service'} unless @_;
5941             $self->schema->schema_url( $self->{'_service'} = shift );
5942             my %services = %{ $self->schema->parse(@_)->load->services };
5943              
5944             Carp::croak
5945             "More than one service in service description. Service and port names have to be specified\n"
5946             if keys %services > 1;
5947             my $service = ( keys %services )[0]->new;
5948             return $service;
5949             }
5950              
5951             sub AUTOLOAD {
5952             my $method = substr( $AUTOLOAD, rindex( $AUTOLOAD, '::' ) + 2 );
5953             return if $method eq 'DESTROY';
5954              
5955             ref $_[0]
5956             or Carp::croak qq!Can\'t locate class method "$method" via package \"!
5957             . __PACKAGE__ . '\"';
5958              
5959             no strict 'refs';
5960             *$AUTOLOAD = sub {
5961             my $self = shift;
5962             my $som = $self->call( $method => @_ );
5963             return $self->autoresult
5964             && UNIVERSAL::isa( $som => 'SOAP::SOM' )
5965             ? wantarray ? $som->paramsall : $som->result
5966             : $som;
5967             };
5968             goto &$AUTOLOAD;
5969             }
5970              
5971             sub call {
5972             SOAP::Trace::trace('()');
5973             my $self = shift;
5974              
5975             if (
5976             !(
5977             defined $self->proxy
5978             && UNIVERSAL::isa( $self->proxy => 'SOAP::Client' )
5979             )
5980             && defined( $self->wsaddress )
5981             && UNIVERSAL::isa( $self->wsaddress => 'WSRF::WS_Address' )
5982             )
5983             {
5984             $self->proxy( $self->wsaddress->Address() );
5985             }
5986              
5987             # Why is this here? Can't call be null? Indicating that there are no input arguments?
5988             #return $self->{_call} unless @_;
5989             die
5990             "A service address has not been specified either by using SOAP::Lite->proxy() or a service description)\n"
5991             unless defined $self->proxy
5992             && UNIVERSAL::isa( $self->proxy => 'SOAP::Client' );
5993              
5994             $self->init_context();
5995             my $serializer = $self->serializer;
5996             $serializer->on_nonserialized( $self->on_nonserialized );
5997             if ( defined $self->wsaddress ) {
5998             my $header =
5999             ""
6000             . scalar( $self->on_action->( $serializer->uriformethod( $_[0] ) ) )
6001             . "";
6002             $header .=
6003             "" . $self->wsaddress->Address() . "";
6004             $header .=
6005             ""
6006             . $self->wsaddress->MessageID()
6007             . "";
6008             $header .=
6009             $self->wsaddress->serializeReferenceParameters()
6010             ? $self->wsaddress->serializeReferenceParameters()
6011             : '';
6012              
6013             #bug fix - John Newman
6014             $header .=
6015             "$WSRF::Constants::WSA_ANON";
6016             @_ = ( @_, SOAP::Header->value($header)->type('xml') );
6017             }
6018              
6019             my $response = $self->transport->send_receive(
6020             context => $self, # this is provided for context
6021             endpoint => $self->endpoint,
6022             action =>
6023             scalar( $self->on_action->( $serializer->uriformethod( $_[0] ) ) ),
6024              
6025             # leave only parameters so we can later update them if required
6026             envelope => $serializer->envelope( method => shift, @_ ),
6027              
6028             # envelope => $serializer->envelope(method => shift, @_),
6029             encoding => $serializer->encoding,
6030             parts => @{ $self->packager->parts } ? $self->packager->parts : undef,
6031             );
6032              
6033             #BUG fix by Luke AT yahoo.com
6034             #return $response if $self->outputxml;
6035             # if ( $self->outputxml ) { $self->destroy_context(); return $response; }
6036              
6037             # deserialize and store result
6038             my $result = $self->{'_call'} =
6039             eval { $self->deserializer->deserialize($response) }
6040             if $response;
6041              
6042             if (
6043             !$self->transport->is_success || # transport fault
6044             $@ || # not deserializible
6045             # fault message even if transport OK
6046             # or no transport error (for example, fo TCP, POP3, IO implementations)
6047             UNIVERSAL::isa( $result => 'SOAP::SOM' ) && $result->fault
6048             )
6049             {
6050             return $self->{'_call'} =
6051             ( $self->on_fault->( $self, $@ ? $@ . ( $response || '' ) : $result )
6052             || $result );
6053             }
6054              
6055             return unless $response; # nothing to do for one-ways
6056              
6057             # little bit tricky part that binds in/out parameters
6058             if ( UNIVERSAL::isa( $result => 'SOAPSOM' )
6059             && ( $result->paramsout || $result->headers )
6060             && $serializer->signature )
6061             {
6062             my $num = 0;
6063             my %signatures = map { $_ => $num++ } @{ $serializer->signature };
6064             for ( $result->dataof(SOAP::SOM::paramsout),
6065             $result->dataof(SOAP::SOM::headers) )
6066             {
6067             my $signature = join $;, $_->name, $_->type || '';
6068             if ( exists $signatures{$signature} ) {
6069             my $param = $signatures{$signature};
6070             my ($value) = $_->value; # take first value
6071             UNIVERSAL::isa( $_[$param] => 'SOAP::Data' )
6072             ? $_[$param]->SOAP::Data::value($value)
6073             : UNIVERSAL::isa( $_[$param] => 'ARRAY' )
6074             ? ( @{ $_[$param] } = @$value )
6075             : UNIVERSAL::isa( $_[$param] => 'HASH' )
6076             ? ( %{ $_[$param] } = %$value )
6077             : UNIVERSAL::isa( $_[$param] => 'SCALAR' )
6078             ? ( ${ $_[$param] } = $$value )
6079             : ( $_[$param] = $value );
6080             }
6081             }
6082             }
6083             $self->destroy_context();
6084              
6085             if ( $self->outputxml ) {
6086             return ($result, $response);
6087             } else {
6088             return $result;
6089             }
6090             } # end of call()
6091              
6092             # ======================================================================
6093              
6094             package WSRF::WSS;
6095              
6096             =pod
6097              
6098             =head1 WSRF::WSS
6099              
6100             Provides support for digitally signing SOAP messages according to the
6101             WS-Security specification.
6102              
6103             =head2 METHODS
6104              
6105             =over
6106              
6107             =item sign
6108              
6109             =item verify
6110              
6111             =back
6112              
6113             =cut
6114              
6115             %WSRF::WSS::ASNMTAP = ();
6116             $WSRF::WSS::ASNMTAP{UsernameToken} = undef;
6117             $WSRF::WSS::ASNMTAP{SAML} = undef;
6118             $WSRF::WSS::ASNMTAP{Assertion} = undef;
6119             $WSRF::WSS::ASNMTAP{SAMLAssertionID} = undef;
6120              
6121             %WSRF::WSS::ID = ();
6122             $WSRF::WSS::ID{X509Token} = "X509Token-" . time();
6123             $WSRF::WSS::ID{TimeStamp} = "TimeStamp-" . time();
6124             $WSRF::WSS::ID{myBody} = "myBody-" . time();
6125              
6126             %WSRF::WSS::Sign = ();
6127             $WSRF::WSS::Sign{BinarySecurityToken} = 1;
6128             $WSRF::WSS::Sign{Timestamp} = 1;
6129             $WSRF::WSS::Sign{MessageID} = 1;
6130             $WSRF::WSS::Sign{To} = 1;
6131             $WSRF::WSS::Sign{Action} = 1;
6132             $WSRF::WSS::Sign{From} = 1;
6133             $WSRF::WSS::Sign{RelatesTo} = 1;
6134             $WSRF::WSS::Sign{ReplyTo} = 1;
6135             $WSRF::WSS::Sign{Body} = 1;
6136              
6137             %WSRF::WSS::ID_Xpath = ();
6138              
6139             #XPaths to the parts of the SOAP message we want to sign
6140             $WSRF::WSS::sec_xpath =
6141             '(//. | //@* | //namespace::*)[ancestor-or-self::wsse:BinarySecurityToken]';
6142              
6143             #$WSRF::WSS::sec_xpath =
6144             # ' 6145             # . $WSRF::Constants::WSSE
6146             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsse:BinarySecurityToken]';
6147              
6148             $WSRF::WSS::si_xpath =
6149             # '(//. | //@* | //namespace::*)[ancestor-or-self::ds:SignedInfo]';
6150             '(//. | //@* | //namespace::*)[ancestor-or-self::ds:SignedInfo]';
6151             $WSRF::WSS::timestamp_xpath =
6152             # ' 6153             # . $WSRF::Constants::WSU
6154             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsu:Timestamp]';
6155             '(//. | //@* | //namespace::*)[ancestor-or-self::wsu:Timestamp]';
6156              
6157             $WSRF::WSS::ID_Xpath{MessageID} =
6158             # ' 6159             # . $WSRF::Constants::WSA
6160             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:MessageID]';
6161             '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:MessageID]';
6162              
6163             $WSRF::WSS::ID_Xpath{To} =
6164             # ' 6165             # . $WSRF::Constants::WSA
6166             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:To]';
6167             '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:To]';
6168              
6169             $WSRF::WSS::ID_Xpath{Action} =
6170             # ' 6171             # . $WSRF::Constants::WSA
6172             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:Action]';
6173             '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:Action]';
6174              
6175             $WSRF::WSS::ID_Xpath{From} =
6176             # ' 6177             # . $WSRF::Constants::WSA
6178             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:From]';
6179             '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:From]';
6180              
6181             $WSRF::WSS::ID_Xpath{ReplyTo} =
6182             # ' 6183             # . $WSRF::Constants::WSA
6184             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:ReplyTo]';
6185             '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:ReplyTo]';
6186              
6187             $WSRF::WSS::ID_Xpath{RelatesTo} =
6188             # ' 6189             # . $WSRF::Constants::WSA
6190             # . '">(//. | //@* | //namespace::*)[ancestor-or-self::wsa:RelatesTo]';
6191             '(//. | //@* | //namespace::*)[ancestor-or-self::wsa:RelatesTo]';
6192              
6193             $WSRF::WSS::body_xpath =
6194             #""
6195             # . '(//. | //@* | //namespace::*)'
6196             # . "[ancestor-or-self::$SOAP::Constants::PREFIX_ENV:Body]";
6197             '(//. | //@* | //namespace::*)' . "[ancestor-or-self::$SOAP::Constants::PREFIX_ENV:Body]";
6198              
6199             $WSRF::WSS::priv_key = undef;
6200             $WSRF::WSS::pub_key = undef;
6201              
6202             sub load_priv_key {
6203              
6204             if ( defined($WSRF::WSS::priv_key) ) {
6205             if ( ref($WSRF::WSS::priv_key) eq 'CODE' ) {
6206             return $WSRF::WSS::priv_key->();
6207             } else {
6208             return $WSRF::WSS::priv_key;
6209             }
6210             }
6211              
6212             eval { require Crypt::OpenSSL::RSA };
6213             die "Failed to access class Crypt::OpenSSL::RSA: $@" if $@;
6214              
6215             my $key_file_name =
6216             $ENV{HTTPS_KEY_FILE} ? $ENV{HTTPS_KEY_FILE} : die "No Private Key\n";
6217             open( PRIVKEY, $key_file_name )
6218             || die("Could not open file $key_file_name");
6219             my $privkey = join "", ;
6220             close(PRIVKEY);
6221             Crypt::OpenSSL::RSA->new_private_key($privkey);
6222             }
6223              
6224             #returns the cert block between the begin and end delimiters
6225             sub load_cert {
6226              
6227             if ( defined($WSRF::WSS::pub_key) ) {
6228             if ( ref($WSRF::WSS::pub_key) eq 'CODE' ) {
6229             return $WSRF::WSS::pub_key->();
6230             } else {
6231             return $WSRF::WSS::pub_key;
6232             }
6233             }
6234              
6235             my $cert_file_name =
6236             $ENV{HTTPS_CERT_FILE} ? $ENV{HTTPS_CERT_FILE} : die "No Public Key\n";
6237             open( CERT, $cert_file_name )
6238             || die("Could not open certificate file $cert_file_name");
6239             my $start = 0;
6240             my $cert = "";
6241             while () {
6242             if ( !m/-----END CERTIFICATE-----/ && $start == 1 ) {
6243             $cert = $cert . $_;
6244             }
6245             if (/-----BEGIN CERTIFICATE-----/) {
6246             $start = 1;
6247             }
6248             }
6249             close(CERT);
6250             return $cert;
6251             }
6252              
6253             sub sign {
6254             my $envelope = shift;
6255              
6256             eval { require XML::LibXML };
6257             die "Failed to access class XML::LibXML: $@" if $@;
6258             eval { require MIME::Base64 };
6259             die "Failed to access class MIME::Base64: $@" if $@;
6260              
6261             #Get Certificate
6262             my $certificate = WSRF::WSS::load_cert();
6263              
6264             my $header = "";
6265              
6266             my $for_signing =
6267             ''
6268             . ''
6269             . '';
6270              
6271             #search through the envelope for things to sign
6272             foreach my $key ( keys(%WSRF::WSS::ID_Xpath) ) {
6273             next unless (defined $WSRF::WSS::ID_Xpath{$key});
6274             $for_signing .=
6275             WSRF::WSS::make_token( $envelope, $WSRF::WSS::ID_Xpath{$key}, $key )
6276             if defined( $WSRF::WSS::Sign{$key} );
6277             my $parser = XML::LibXML->new();
6278             my $doc = $parser->parse_string($envelope);
6279             my $canon = undef;
6280             eval {$canon = $doc->toStringEC14N( 0, $WSRF::WSS::ID_Xpath{$key}, [''] );};
6281             $header .= defined($canon) ? $canon : "";
6282             }
6283              
6284             $for_signing .=
6285             WSRF::WSS::make_token( $envelope, $WSRF::WSS::body_xpath, $WSRF::WSS::ID{myBody} )
6286             if defined( $WSRF::WSS::Sign{Body} );
6287              
6288             #create a security token using the certificate
6289             my $sec_token =
6290             ''
6291             . $certificate
6292             . '';
6293             if ( defined( $WSRF::WSS::Sign{BinarySecurityToken} )
6294             && defined($WSRF::WSS::sec_xpath) )
6295             {
6296             $for_signing .=
6297             WSRF::WSS::make_token( $sec_token, $WSRF::WSS::sec_xpath,
6298             $WSRF::WSS::ID{X509Token} );
6299             }
6300              
6301             #create a timestamp
6302             my $timestamp = '';
6303             if ( defined($WSRF::WSS::timestamp_xpath) ) {
6304             $timestamp =
6305             '';
6306             $timestamp .=
6307             ''
6308             . WSRF::Time::ConvertEpochTimeToString(time)
6309             . '';
6310             $timestamp .=
6311             ''
6312             . WSRF::Time::ConvertEpochTimeToString( time + ($WSRF::TIME::EXPIRES_IN ? $WSRF::TIME::EXPIRES_IN : 60))
6313             . '';
6314              
6315             #$timestamp .= '2004-02-07T14:31:59Z';
6316             #$timestamp .= '2006-02-07T14:36:59Z';
6317             $timestamp .= '';
6318              
6319             #canonicalize,digest + Base64 the timestamp
6320             $for_signing .=
6321             WSRF::WSS::make_token( $timestamp, $WSRF::WSS::timestamp_xpath,
6322             $WSRF::WSS::ID{TimeStamp} )
6323             if defined( $WSRF::WSS::Sign{Timestamp} );
6324             }
6325              
6326             $for_signing .= '';
6327              
6328             my $parser = XML::LibXML->new();
6329             my $doc = $parser->parse_string($for_signing);
6330             my $can_signed_info = $doc->toStringEC14N( 0, $WSRF::WSS::si_xpath, [''] );
6331              
6332             # print ">>>can_signed>>>>".MIME::Base64::encode(sha1($can_signed_info))."<<<<
6333             # print ">>>can_signed_info>>>>\n$can_signed_info\n<<<<
6334              
6335             my $rsa_priv = WSRF::WSS::load_priv_key();
6336             my $signature = $rsa_priv->sign($can_signed_info);
6337             $signature = MIME::Base64::encode($signature);
6338              
6339             my $sec_token_reference = '';
6340              
6341             if ( defined $WSRF::WSS::ASNMTAP{Assertion} and $WSRF::WSS::ASNMTAP{SAMLAssertionID} ) {
6342             $sec_token = $WSRF::WSS::ASNMTAP{Assertion};
6343             $WSRF::WSS::ASNMTAP{Assertion} =~ $WSRF::WSS::ASNMTAP{SAMLAssertionID};
6344             $sec_token_reference = '' . ( defined $1 ? $1 : '?' ) . '';
6345             }
6346              
6347             my $extraheader =
6348             '
6349             xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">'
6350             . $sec_token . "\n"
6351             . ''
6352             . $can_signed_info . ''
6353             . $signature . ''
6354             . '' . $sec_token_reference . ''
6355             . '';
6356              
6357             $extraheader .= $WSRF::WSS::ASNMTAP{UsernameToken} if ( $WSRF::WSS::ASNMTAP{UsernameToken} );
6358              
6359             if ( defined($WSRF::WSS::timestamp_xpath) ) {
6360             $extraheader .= $timestamp;
6361             }
6362             $extraheader .= '';
6363             $header = $extraheader . $header;
6364              
6365             $doc = $parser->parse_string($envelope);
6366             my $Body = $doc->toStringEC14N( 0, $WSRF::WSS::body_xpath, ((defined $WSRF::WSS::ASNMTAP{SAML}) ? ['saml', 'samlp'] : ['']));
6367             # TODO: replace ['saml', 'samlp'] with the array created from the content of $WSRF::WSS::ASNMTAP{SAML}!!!
6368             #my $Body = $doc->toStringEC14N( 0, $WSRF::WSS::body_xpath, [''] );
6369             #my $Body = $doc->toStringC14N(0,$WSRF::WSS::body_xpath);
6370            
6371             #print ">>>header newline body>>>>\n$header\n\n$Body\n<<<<
6372             return $header, $Body;
6373             }
6374              
6375             sub make_token {
6376             my ( $XML, $Path, $ID ) = @_;
6377              
6378             eval { require XML::LibXML };
6379             die "Failed to access class XML::LibXML: $@" if $@;
6380             eval { require Digest::SHA1 };
6381             die "Failed to access class Digest::SHA1: $@" if $@;
6382             eval { require MIME::Base64 };
6383             die "Failed to access class MIME::Base64: $@" if $@;
6384              
6385             # print "make_token $ID\n";
6386             # print "Xpath=> $Path\n";
6387             my $parser = XML::LibXML->new();
6388             my $doc = $parser->parse_string($XML);
6389             my $can_token = undef;
6390             eval {$can_token = $doc->toStringEC14N( 0, $Path, [''] );};
6391             return '' unless $can_token;
6392              
6393             # print ">>>token-$ID>>>\n$can_token\n<<
6394              
6395             #take digest of token
6396             my $token_digest = Digest::SHA1::sha1($can_token);
6397              
6398             #base64 encode digest
6399             $token_digest = MIME::Base64::encode($token_digest);
6400             chomp($token_digest);
6401              
6402             #print ">>>>token-$ID-digest>>>".$token_digest."<<
6403              
6404             return ''
6405             . ''
6406             . ''
6407             #. ''
6408             . ''
6409             . ''
6410             . ''
6411             . $token_digest
6412             . ''
6413             . '';
6414              
6415             }
6416              
6417             %WSRF::WSS::ThingsThatShouldBeSigned = ();
6418              
6419             $WSRF::WSS::ThingsThatShouldBeSigned{Body} = $SOAP::Constants::NS_ENV;
6420             $WSRF::WSS::Xpath{Body} = $WSRF::WSS::body_xpath;
6421              
6422             $WSRF::WSS::ThingsThatShouldBeSigned{To} = $WSRF::Constants::WSA;
6423             $WSRF::WSS::Xpath{To} = $WSRF::WSS::ID_Xpath{To};
6424              
6425             $WSRF::WSS::ThingsThatShouldBeSigned{MessageID} = $WSRF::Constants::WSA;
6426             $WSRF::WSS::Xpath{MessageID} = $WSRF::WSS::ID_Xpath{MessageID};
6427              
6428             $WSRF::WSS::ThingsThatShouldBeSigned{ReplyTo} = $WSRF::Constants::WSA;
6429             $WSRF::WSS::Xpath{ReplyTo} = $WSRF::WSS::ID_Xpath{ReplyTo};
6430              
6431             $WSRF::WSS::ThingsThatShouldBeSigned{Action} = $WSRF::Constants::WSA;
6432             $WSRF::WSS::Xpath{Action} = $WSRF::WSS::ID_Xpath{Action};
6433              
6434             $WSRF::WSS::ThingsThatShouldBeSigned{Timestamp} = $WSRF::Constants::WSU;
6435             $WSRF::WSS::Xpath{Timestamp} = $WSRF::WSS::timestamp_xpath;
6436              
6437             $WSRF::WSS::ThingsThatShouldBeSigned{BinarySecurityToken} =
6438             $WSRF::Constants::WSSE;
6439             $WSRF::WSS::Xpath{BinarySecurityToken} = $WSRF::WSS::sec_xpath;
6440              
6441             $WSRF::WSS::ThingsThatShouldBeSigned{From} = $WSRF::Constants::WSA;
6442             $WSRF::WSS::Xpath{From} = $WSRF::WSS::ID_Xpath{From};
6443              
6444             $WSRF::WSS::ThingsThatShouldBeSigned{RelatesTo} = $WSRF::Constants::WSA;
6445             $WSRF::WSS::Xpath{RelatesTo} = $WSRF::WSS::ID_Xpath{RelatesTo};
6446              
6447             sub verify {
6448             my $envelope = shift;
6449              
6450             eval { require XML::LibXML };
6451             die "Failed to access class XML::LibXML: $@" if $@;
6452             eval { require Digest::SHA1 };
6453             die "Failed to access class Digest::SHA1: $@" if $@;
6454             eval { require Crypt::OpenSSL::RSA };
6455             die "Failed to access class Crypt::OpenSSL::RSA: $@" if $@;
6456             eval { require Crypt::OpenSSL::X509 };
6457             die "Failed to access class Crypt::OpenSSL::X509: $@" if $@;
6458             eval { require MIME::Base64 };
6459             die "Failed to access class MIME::Base64: $@" if $@;
6460              
6461             my %results = ();
6462              
6463             #get Security Token
6464             my $Token =
6465             $envelope->match(
6466             "/Envelope/Header/Security/{$WSRF::Constants::WSSE}BinarySecurityToken")
6467             ? $envelope->valueof(
6468             "/Envelope/Header/Security/{$WSRF::Constants::WSSE}BinarySecurityToken")
6469             : die "WSRF::WSS::verify Fault - No Security Token in SOAP Header\n";
6470              
6471             $Token =~ s/\s+$//;
6472             $Token =
6473             "-----BEGIN CERTIFICATE-----\n" . $Token . "\n-----END CERTIFICATE-----";
6474              
6475             # print ">>>>Token>>>\n$Token\n<<<
6476              
6477             #create an X509 object from the string - this will die if it is not an X509 cert
6478             my $x509 = Crypt::OpenSSL::X509->new_from_string($Token);
6479              
6480             #if we get here then $Token IS a X509 cert
6481             $results{X509} = $Token;
6482              
6483             my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key( $x509->pubkey() );
6484              
6485             #get the piece of XML that has been signed
6486             my $parser = XML::LibXML->new();
6487             my $doc = $parser->parse_string( $envelope->raw_xml );
6488             my $can_signed_info = $doc->toStringEC14N( 0, $WSRF::WSS::si_xpath, [''] );
6489              
6490             #get the Signature value
6491             my $SignatureValue =
6492             $envelope->match(
6493             "/Envelope/Header//{$WSRF::Constants::DS}SignatureValue")
6494             ? $envelope->valueof(
6495             "/Envelope/Header//{$WSRF::Constants::DS}SignatureValue")
6496             : die "WSRF::WSS::verify Fault - No Signature Value in SOAP Header\n";
6497              
6498             $SignatureValue = MIME::Base64::decode($SignatureValue);
6499              
6500             if ( $rsa_pub->verify( $can_signed_info, $SignatureValue ) ) {
6501             $results{Signed} = 'true';
6502              
6503             #print STDERR "WSRF::WSS::verify Message Signature is Correct\n";
6504             } else {
6505             die "WSRF::WSS::verify Fault - Message Signature is NOT Correct\n";
6506             }
6507              
6508             my $i = 1;
6509             my %SignedStuff = ();
6510             while (
6511             $envelope->match("/Envelope/Header/Security/Signature/SignedInfo/[$i]")
6512             )
6513             {
6514             my $data =
6515             $envelope->dataof(
6516             "/Envelope/Header/Security/Signature/SignedInfo/[$i]");
6517             if ( $data->name eq "Reference" ) {
6518             my $attr = $data->attr;
6519             my $name = $attr->{URI};
6520             my $DigestValue =
6521             $envelope->match(
6522             "/Envelope/Header/Security/Signature/SignedInfo/[$i]//{$WSRF::Constants::DS}DigestValue"
6523             )
6524             ? $envelope->valueof(
6525             "/Envelope/Header/Security/Signature/SignedInfo/[$i]//{$WSRF::Constants::DS}DigestValue"
6526             )
6527             : die "WSRF::WSS::verify No DigestValue for $name";
6528              
6529             #strip the # that is part of the XLink stuff for pointing to other parts of the XML doc
6530             $name =~ s/^\#//o;
6531             $SignedStuff{$name} = $DigestValue;
6532             }
6533             $i++;
6534             }
6535              
6536             my %Signed = ();
6537             foreach my $key ( keys %WSRF::WSS::ThingsThatShouldBeSigned ) {
6538             if (
6539             $envelope->match(
6540             "/Envelope//{$WSRF::WSS::ThingsThatShouldBeSigned{$key}}$key")
6541             )
6542             {
6543             my $data =
6544             $envelope->dataof(
6545             "/Envelope//{$WSRF::WSS::ThingsThatShouldBeSigned{$key}}$key");
6546             my $attr = $data->attr;
6547             my $ID = $attr->{"{$WSRF::Constants::WSU}Id"};
6548             $Signed{$key} = $ID;
6549             }
6550             }
6551              
6552             foreach my $key ( keys %Signed ) {
6553             my $parser = XML::LibXML->new();
6554             my $doc = $parser->parse_string( $envelope->raw_xml );
6555             my $CanonicalForm =
6556             $doc->toStringEC14N( 0, $WSRF::WSS::Xpath{$key}, [''] );
6557             die "Could not get the Canonicalize $key from Envelope\n"
6558             unless $CanonicalForm;
6559             my $token_digest = Digest::SHA1::sha1($CanonicalForm);
6560             $token_digest = MIME::Base64::encode($token_digest);
6561             chomp($token_digest);
6562             if ( $SignedStuff{ $Signed{$key} } eq $token_digest ) {
6563              
6564             #print "WSRF::WSS::verify Message \"$key\" is signed\n";
6565             $results{PartsSigned}{$key} = 'true';
6566             } else {
6567             die "WSRF::WSS::verify $key digest hashs do not match\n";
6568             }
6569             }
6570              
6571             $results{Created} =
6572             $envelope->match(
6573             "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Created")
6574             ? $envelope->valueof(
6575             "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Created")
6576             : undef;
6577              
6578             #print STDERR "WSRF::WSS::verify Message Created at $results{Created} (should be GMT)\n" if $results{Created};
6579              
6580             $results{Expires} =
6581             $envelope->match(
6582             "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Expires")
6583             ? $envelope->valueof(
6584             "/Envelope/Header/Security/Timestamp/{$WSRF::Constants::WSU}Expires")
6585             : undef;
6586              
6587             #print STDERR "WSRF::WSS::verify Message Expires at \"$results{Expires}\" (should be GMT)\n" if $results{Expires};
6588              
6589             return %results;
6590             }
6591              
6592             1;