Uncommented beaudio code
[pwlib.git] / src / ptclib / psoap.cxx
blob25b65b7f589d10e11d6404b8cf3ac008c3dad408
1 /*
2 * psoap.cxx
4 * SOAP client / server classes.
6 * Portable Windows Library
8 * Copyright (c) 2003 Andreas Sikkema
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Andreas Sikkema
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.7 2004/01/17 17:45:59 csoutheren
28 * Changed to use PString::MakeEmpty
30 * Revision 1.6 2003/10/08 21:58:13 dereksmithies
31 * Add client authentication support. many thanks to Ben Lear.
33 * Revision 1.5 2003/04/28 00:09:14 craigs
34 * Patches from Andreas Sikkema
36 * Revision 1.4 2003/03/31 06:20:56 craigs
37 * Split the expat wrapper from the XML file handling to allow reuse of the parser
39 * Revision 1.3 2003/02/09 23:31:54 robertj
40 * Added referention PString's for efficiency.
42 * Revision 1.2 2003/02/09 23:22:46 robertj
43 * Fixed spelling errors, and setting return values, thanks Andreas Sikkema
45 * Revision 1.1 2003/02/04 22:46:48 robertj
46 * Added basic SOAP support, thanks Andreas Sikkema
50 #ifdef __GNUC__
51 #pragma implementation "psoap.h"
52 #endif
55 #include <ptlib.h>
58 #if P_EXPAT
60 #include <ptclib/psoap.h>
65 SOAP message classes
66 ####################
70 PSOAPMessage::PSOAPMessage( int options ) :
71 PXML( options ),
72 pSOAPBody( 0 ),
73 pSOAPMethod( 0 ),
74 faultCode( PSOAPMessage::NoFault )
78 PSOAPMessage::PSOAPMessage( const PString & method, const PString & nameSpace ) :
79 PXML( PXMLParser::Indent + PXMLParser::NewLineAfterElement ),
80 pSOAPBody( 0 ),
81 pSOAPMethod( 0 ),
82 faultCode( PSOAPMessage::NoFault )
84 SetMethod( method, nameSpace );
89 void PSOAPMessage::SetMethod( const PString & name, const PString & nameSpace )
91 PXMLElement* rtElement = 0;
93 if ( pSOAPBody == 0 )
95 SetRootElement("SOAP-ENV:Envelope");
97 rtElement = GetRootElement();
99 rtElement->SetAttribute("xmlns:SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", TRUE );
100 rtElement->SetAttribute("xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance", TRUE );
101 rtElement->SetAttribute("xmlns:xsd", "http://www.w3.org/1999/XMLSchema", TRUE );
102 rtElement->SetAttribute("xmlns:SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", TRUE );
104 pSOAPBody = new PXMLElement( rtElement, "SOAP-ENV:Body");
106 rtElement->AddChild( pSOAPBody, TRUE );
109 if ( pSOAPMethod == 0 )
111 rtElement = GetRootElement();
113 pSOAPMethod = new PXMLElement( rtElement, PString( "m:") + name );
114 if ( nameSpace != "" )
116 pSOAPMethod->SetAttribute("xmlns:m", nameSpace, TRUE );
118 pSOAPBody->AddChild( pSOAPMethod, TRUE );
123 void PSOAPMessage::GetMethod( PString & name, PString & nameSpace )
125 PString fullMethod = pSOAPMethod->GetName();
126 PINDEX sepLocation = fullMethod.Find(':');
127 PString methodID = fullMethod.Left( sepLocation );
128 name = fullMethod.Right( fullMethod.GetSize() - 2 - sepLocation );
130 nameSpace = pSOAPMethod->GetAttribute( "xmlns:" + methodID );
135 void PSOAPMessage::AddParameter( PString name, PString type, PString value )
137 if ( pSOAPMethod )
139 PXMLElement* rtElement = GetRootElement();
141 PXMLElement* pParameter = new PXMLElement( rtElement, name);
142 PXMLData* pParameterData = new PXMLData( pParameter, value);
144 if ( type != "" )
146 pParameter->SetAttribute( "xsi:type", PString( "xsd:" ) + type );
149 pParameter->AddChild( pParameterData, TRUE );
151 AddParameter( pParameter, TRUE );
155 void PSOAPMessage::AddParameter( PXMLElement* parameter, BOOL dirty )
157 if ( pSOAPMethod )
159 pSOAPMethod->AddChild( parameter, dirty );
163 void PSOAPMessage::PrintOn(ostream & strm) const
165 BOOL newLine = ( options & PXMLParser::NewLineAfterElement ) != 0;
167 PString ver = version;
168 PString enc = encoding;
169 int salone = standAlone;
171 if ( ver.IsEmpty() )
172 ver= "1.0";
173 if ( enc.IsEmpty() )
174 enc = "UTF-8";
175 if ( salone == -2 )
176 salone = -1;
178 strm << "<?xml version=\"" << ver << "\" encoding=\"" << enc << "\"";
179 switch ( salone ) {
180 case 0:
181 strm << " standalone=\"no\"";
182 break;
183 case 1:
184 strm << " standalone=\"yes\"";
185 break;
186 default:
187 break;
190 strm << "?>";
191 if ( newLine )
192 strm << endl;
194 if ( rootElement != NULL ) {
195 rootElement->Output(strm, *(this), 2 );
199 PString PSOAPMessage::AsString( void )
201 PStringStream stringStream;
202 PrintOn( stringStream );
204 PString SOAPString = stringStream;
206 return SOAPString;
210 PString faultCodeToString( PINDEX faultCode )
212 PString faultCodeStr;
213 switch ( faultCode )
215 case PSOAPMessage::VersionMismatch:
216 faultCodeStr = "VersionMisMatch";
217 break;
218 case PSOAPMessage::MustUnderstand:
219 faultCodeStr = "MustUnderstand";
220 break;
221 case PSOAPMessage::Client:
222 faultCodeStr = "Client";
223 break;
224 case PSOAPMessage::Server:
225 faultCodeStr = "Server";
226 break;
227 default:
228 // Default it's the server's fault. Can't blame it on the customer, because he/she is king ;-)
229 faultCodeStr = "Server";
230 break;
233 return faultCodeStr;
236 PINDEX stringToFaultCode( PString & faultStr )
238 if ( faultStr == "VersionMisMatch" )
239 return PSOAPMessage::VersionMismatch;
241 if ( faultStr == "MustUnderstand" )
242 return PSOAPMessage::MustUnderstand;
244 if ( faultStr == "Client" )
245 return PSOAPMessage::Client;
247 if ( faultStr == "Server" )
248 return PSOAPMessage::Server;
250 return PSOAPMessage::Server;
253 BOOL PSOAPMessage::GetParameter( const PString & name, PString & value )
255 PXMLElement* pElement = GetParameter( name );
257 if ( pElement->GetAttribute( "xsi:type") == "xsd:string" )
259 value = pElement->GetData();
260 return TRUE;
263 value.MakeEmpty();
264 return FALSE;
267 BOOL PSOAPMessage::GetParameter( const PString & name, int & value )
269 PXMLElement* pElement = GetParameter( name );
271 if ( pElement->GetAttribute( "xsi:type") == "xsd:int" )
273 value = pElement->GetData().AsInteger();
274 return TRUE;
277 value = -1;
278 return FALSE;
281 PXMLElement* PSOAPMessage::GetParameter( const PString & name )
283 if ( pSOAPMethod )
285 return pSOAPMethod->GetElement( name, 0 );
287 else
289 return 0;
293 BOOL PSOAPMessage::Load( const PString & str )
295 if ( !PXML::Load( str ) )
296 return FALSE;
298 if ( rootElement != NULL )
300 PString soapEnvelopeName = rootElement->GetName();
301 PString soapEnvelopeID = soapEnvelopeName.Left( soapEnvelopeName.Find(':') );
303 pSOAPBody = rootElement->GetElement( soapEnvelopeID + ":Body", 0 );
305 if ( pSOAPBody != NULL )
307 PXMLObjectArray subObjects = pSOAPBody->GetSubObjects() ;
309 PINDEX idx;
310 PINDEX size = subObjects.GetSize();
312 for ( idx = 0; idx < size; idx++ ) {
313 if ( subObjects[ idx ].IsElement() ) {
314 // First subobject being an element is the method
315 pSOAPMethod = ( PXMLElement * ) &subObjects[ idx ];
317 PString method;
318 PString nameSpace;
320 GetMethod( method, nameSpace );
322 // Check if method name is "Fault"
323 if ( method == "Fault" )
325 // The SOAP server has signalled an error
326 PString faultCodeData = GetParameter( "faultcode" )->GetData();
327 faultCode = stringToFaultCode( faultCodeData );
328 faultText = GetParameter( "faultstring" )->GetData();
330 else
332 return TRUE;
338 return FALSE;
341 void PSOAPMessage::SetFault( PINDEX code, const PString & text)
343 faultCode = code;
344 faultText = text;
346 PString faultCodeStr = faultCodeToString( code );
348 SetMethod( "Fault", "" );
350 AddParameter( "faultcode", "", faultCodeStr );
351 AddParameter( "faultstring", "", text );
358 SOAP server classes
359 ####################
364 PSOAPServerResource::PSOAPServerResource()
365 : PHTTPResource( DEFAULT_SOAP_URL ),
366 soapAction( " " )
370 PSOAPServerResource::PSOAPServerResource(
371 const PHTTPAuthority & auth ) // Authorisation for the resource.
372 : PHTTPResource( DEFAULT_SOAP_URL, auth ),
373 soapAction( " " )
376 PSOAPServerResource::PSOAPServerResource(
377 const PURL & url ) // Name of the resource in URL space.
378 : PHTTPResource(url )
382 PSOAPServerResource::PSOAPServerResource(
383 const PURL & url, // Name of the resource in URL space.
384 const PHTTPAuthority & auth // Authorisation for the resource.
386 : PHTTPResource( url, auth )
390 BOOL PSOAPServerResource::SetMethod(const PString & methodName, const PNotifier & func)
392 // Set the method for the notifier function and add it to the list
393 PWaitAndSignal m( methodMutex );
395 // Find the method, or create a new one
396 PSOAPServerMethod * methodInfo;
398 PINDEX pos = methodList.GetValuesIndex( methodName );
399 if (pos != P_MAX_INDEX)
401 methodInfo = ( PSOAPServerMethod *) methodList.GetAt( pos );
403 else
405 methodInfo = new PSOAPServerMethod( methodName );
406 methodList.Append( methodInfo );
409 // set the function
410 methodInfo->methodFunc = func;
412 return TRUE;
415 BOOL PSOAPServerResource::LoadHeaders( PHTTPRequest& /* request */ ) // Information on this request.
417 return TRUE;
420 BOOL PSOAPServerResource::OnPOSTData( PHTTPRequest & request,
421 const PStringToString & /*data*/)
423 PTRACE( 2, "PSOAPServerResource\tReceived post data, request: " << request.entityBody );
425 PString reply;
427 BOOL ok = FALSE;
429 // Check for the SOAPAction header
430 PString* pSOAPAction = request.inMIME.GetAt( "SOAPAction" );
431 if ( pSOAPAction )
433 // If it's available check if we are expecting a special header value
434 if ( soapAction == " " )
436 // A space means anything goes
437 ok = OnSOAPRequest( request.entityBody, reply );
439 else
441 // Check if the incoming header is the same as we expected
442 if ( *pSOAPAction == soapAction )
444 ok = OnSOAPRequest( request.entityBody, reply );
446 else
448 ok = FALSE;
449 reply = FormatFault( PSOAPMessage::Client, "Incorrect SOAPAction in HTTP Header: " + *pSOAPAction ).AsString();
453 else
455 ok = FALSE;
456 reply = FormatFault( PSOAPMessage::Client, "SOAPAction is missing in HTTP Header" ).AsString();
459 // If everything went OK, reply with ReturnCode 200 (OK)
460 if ( ok )
461 request.code = PHTTP::RequestOK;
462 else
463 // Reply with InternalServerError (500)
464 request.code = PHTTP::InternalServerError;
466 // Set the correct content-type
467 request.outMIME.SetAt(PHTTP::ContentTypeTag, "text/xml");
469 // Start constructing the response
470 PINDEX len = reply.GetLength();
471 request.server.StartResponse( request.code, request.outMIME, len );
473 // Write the reply to the client
474 return request.server.Write( (const char* ) reply, len );
478 BOOL PSOAPServerResource::OnSOAPRequest( const PString & body, PString & reply )
480 // Load the HTTP body into the SOAP (XML) parser
481 PSOAPMessage request;
482 BOOL ok = request.Load( body );
484 // If parsing the XML to SOAP failed reply with an error
485 if ( !ok )
487 reply = FormatFault( PSOAPMessage::Client, "XML error:" + request.GetErrorString() ).AsString();
488 return FALSE;
492 PString method;
493 PString nameSpace;
495 // Retrieve the method from the SOAP messsage
496 request.GetMethod( method, nameSpace );
498 PTRACE( 3, "PSOAPServerResource\tReceived SOAP message for method " << method);
500 return OnSOAPRequest( method, request, reply );
503 BOOL PSOAPServerResource::OnSOAPRequest( const PString & methodName,
504 PSOAPMessage & request,
505 PString & reply )
507 methodMutex.Wait();
509 // Find the method information
510 PINDEX pos = methodList.GetValuesIndex( methodName );
512 if ( pos == P_MAX_INDEX )
514 reply = FormatFault( PSOAPMessage::Client, "Unknown method = " + methodName ).AsString();
515 return FALSE;
518 PSOAPServerMethod * methodInfo = ( PSOAPServerMethod * )methodList.GetAt( pos );
519 PNotifier notifier = methodInfo->methodFunc;
521 methodMutex.Signal();
523 // create a request/response container to be passed to the notifier function
524 PSOAPServerRequestResponse p( request );
526 // call the notifier
527 notifier( p, 0 );
529 // get the reply
531 reply = p.response.AsString();
533 return p.response.GetFaultCode() == PSOAPMessage::NoFault;
537 PSOAPMessage PSOAPServerResource::FormatFault( PINDEX code, const PString & str )
539 PTRACE(2, "PSOAPServerResource\trequest failed: " << str);
541 PSOAPMessage reply;
543 PString faultCodeStr = faultCodeToString( code );
545 reply.SetMethod( "Fault", "" );
547 reply.AddParameter( "faultcode", "", faultCodeStr );
548 reply.AddParameter( "faultstring", "", str );
550 return reply;
555 SOAP client classes
556 ####################
560 PSOAPClient::PSOAPClient( const PURL & _url )
561 : url(_url),
562 soapAction( " " )
564 timeout = 10000;
567 BOOL PSOAPClient::MakeRequest( const PString & method, const PString & nameSpace )
569 PSOAPMessage request( method, nameSpace );
570 PSOAPMessage response;
572 return MakeRequest( request, response );
575 BOOL PSOAPClient::MakeRequest( const PString & method, const PString & nameSpace, PSOAPMessage & response )
577 PSOAPMessage request( method, nameSpace );
579 return MakeRequest( request, response );
582 BOOL PSOAPClient::MakeRequest( PSOAPMessage & request, PSOAPMessage & response )
584 return PerformRequest( request, response );
587 BOOL PSOAPClient::PerformRequest( PSOAPMessage & request, PSOAPMessage & response )
589 // create SOAP request
590 PString soapRequest;
592 PStringStream txt;
594 if ( !request.Save( soapRequest ) )
597 txt << "Error creating request XML ("
598 << request.GetErrorLine()
599 << ") :"
600 << request.GetErrorString();
601 return FALSE;
604 // End with a newline
605 soapRequest += "\n";
607 PTRACE( 5, "SOAPClient\tOutgoing SOAP is " << soapRequest );
609 // do the request
610 PHTTPClient client;
611 PMIMEInfo sendMIME, replyMIME;
612 sendMIME.SetAt( "Server", url.GetHostName() );
613 sendMIME.SetAt( PHTTP::ContentTypeTag, "text/xml" );
614 sendMIME.SetAt( "SOAPAction", soapAction );
616 if(url.GetUserName() != "") {
617 PStringStream SoapAuthToken;
618 SoapAuthToken << url.GetUserName() << ":" << url.GetPassword();
619 sendMIME.SetAt( "Authorization", PBase64::Encode(SoapAuthToken) );
622 // Set thetimeout
623 client.SetReadTimeout( timeout );
625 // Send the POST request to the server
626 BOOL ok = client.PostData( url, sendMIME, soapRequest, replyMIME );
628 // Find the length of the response
629 PINDEX contentLength;
630 if ( replyMIME.Contains( PHTTP::ContentLengthTag ) )
631 contentLength = ( PINDEX ) replyMIME[ PHTTP::ContentLengthTag ].AsUnsigned();
632 else if ( ok)
633 contentLength = P_MAX_INDEX;
634 else
635 contentLength = 0;
637 // Retrieve the response
638 PString replyBody = client.ReadString( contentLength );
640 PTRACE( 5, "PSOAP\tIncoming SOAP is " << replyBody );
642 // Check if the server really gave us something
643 if ( !ok || replyBody.IsEmpty() )
645 txt << "HTTP POST failed: "
646 << client.GetLastResponseCode() << ' '
647 << client.GetLastResponseInfo();
650 // Parse the response only if the response code from the server
651 // is either 500 (Internal server error) or 200 (RequestOK)
653 if ( ( client.GetLastResponseCode() == PHTTP::RequestOK ) ||
654 ( client.GetLastResponseCode() == PHTTP::InternalServerError ) )
656 if (!response.Load(replyBody))
658 txt << "Error parsing response XML ("
659 << response.GetErrorLine()
660 << ") :"
661 << response.GetErrorString();
663 PStringArray lines = replyBody.Lines();
664 for ( int offset = -2; offset <= 2; offset++ ) {
665 int line = response.GetErrorLine() + offset;
667 if ( line >= 0 && line < lines.GetSize() )
668 txt << lines[ ( PINDEX ) line ];
674 if ( ( client.GetLastResponseCode() != PHTTP::RequestOK ) &&
675 ( client.GetLastResponseCode() != PHTTP::InternalServerError ) &&
676 ( !ok ) )
678 response.SetFault( PSOAPMessage::Server, txt );
679 return FALSE;
683 return TRUE;
687 #endif // P_EXPAT
690 // End of File ////////////////////////////////////////////////////////////////