2 Copyright (C) 2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
21 // $Id: SoObjectSOAPDispatcher.m 1 2004-08-20 10:08:27Z znek $
23 #include "SoObjectSOAPDispatcher.h"
25 #include "NSException+HTTP.h"
26 #include "WOContext+SoObjects.h"
27 #include "SoDefaultRenderer.h"
28 #include <NGObjWeb/WOActionResults.h>
29 #include <NGObjWeb/WOContext.h>
30 #include <NGObjWeb/WOResponse.h>
31 #include <NGObjWeb/WORequest.h>
34 #include <SaxObjC/XMLNamespaces.h>
37 TODO: is it required by SOAP that the HTTP method is POST?
40 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
42 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
43 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
44 xmlns:xsd="http://www.w3.org/1999/XMLSchema"
45 xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
48 xmlns:types="http://schemas.novell.com/2003/10/NCSP/types.xsd"
49 SOAP-ENV:encodingStyle=""
52 <types:auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
53 xsi:type="types:PlainText"
55 <types:username>dummy</types:username>
56 <types:password>user</types:password>
63 @interface SoSOAPRenderer : SoDefaultRenderer
66 @implementation SoObjectSOAPDispatcher
68 static BOOL debugOn = NO;
69 static BOOL debugParsing = NO;
72 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
74 debugOn = [ud boolForKey:@"SoObjectSOAPDispatcherDebugEnabled"];
75 if (debugOn) NSLog(@"Note: SOPE SOAP dispatcher debugging turned on.");
80 - (id)performSOAPAction:(NSString *)_actionName
81 header:(id<DOMElement>)_header body:(id<DOMElement>)_body
82 inContext:(WOContext *)_ctx
89 [self debugWithFormat:@"calling SOAP method: '%@'", _actionName];
91 /* find client object */
93 if ((clientObject = [_ctx clientObject]) != nil) {
95 [self debugWithFormat:@" client object from ctx: %@", clientObject];
97 else if ((clientObject = [self->object clientObject])) {
99 [self debugWithFormat:@" setting client object: %@", clientObject];
100 [_ctx setClientObject:clientObject];
103 /* find callable (method) object */
105 // TODO: should we allow acquisition?
106 methodObject = [clientObject lookupName:_actionName inContext:_ctx
108 if (methodObject == nil) {
109 if ([_actionName hasSuffix:@"Request"])
110 _actionName = [_actionName substringToIndex:[_actionName length] - 7];
111 methodObject = [clientObject lookupName:_actionName inContext:_ctx
115 if (methodObject == nil) {
116 [self warnWithFormat:@"could not locate SOAP method: %@",
118 return [NSException exceptionWithHTTPStatus:501 /* not implemented */
119 reason:@"did not find the specified SOAP method"];
121 else if (![methodObject isCallable]) {
122 [self warnWithFormat:
123 @"object found for SOAP method '%@' is not callable: "
124 @"%@", _actionName, methodObject];
125 return [NSException exceptionWithHTTPStatus:501 /* not implemented */
126 reason:@"did not find the specified SOAP method"];
128 if (debugOn) [self debugWithFormat:@" method: %@", methodObject];
130 /* apply arguments */
132 // TODO: use some query syntax in product.plist to retrieve parameters
135 // TODO: somehow apply SOPE header/body?
136 if (_header) [_ctx setObject:_header forKey:@"SOAPHeader"];
137 if (_body) [_ctx setObject:_body forKey:@"SOAPBody"];
139 if ([methodObject respondsToSelector:
140 @selector(takeValuesFromRequest:inContext:)]) {
142 [self debugWithFormat:@" applying values from request ..."];
143 [methodObject takeValuesFromRequest:[_ctx request] inContext:_ctx];
148 resultObject = [methodObject callOnObject:[_ctx clientObject]
150 if (debugOn) [self debugWithFormat:@"got SOAP result: %@", resultObject];
154 - (id)performSOAPAction:(NSString *)_actionName document:(id)_dom
155 inContext:(WOContext *)_ctx
157 id<DOMElement> envelope;
158 id<DOMElement> header;
160 id<DOMNodeList> list;
164 envelope = [_dom documentElement];
165 if (![[envelope tagName] isEqualToString:@"Envelope"] ||
166 ![[envelope namespaceURI] isEqualToString:XMLNS_SOAP_ENVELOPE]) {
167 [self debugWithFormat:@"Note: missing SOAP envelope at document root."];
168 return [NSException exceptionWithHTTPStatus:400 /* bad request */
169 reason:@"could not parse SOAP content of request"];
171 if (debugParsing) [self debugWithFormat:@"envelope: %@", envelope];
173 [_ctx setObject:envelope forKey:@"SOAPEnvelope"];
177 list = [envelope getElementsByTagName:@"Header"];
178 // TODO: not yet supported by DOMElement: namespaceURI:XMLNS_SOAP_ENVELOPE];
179 if ([list length] > 1) {
180 [self warnWithFormat:@"multiple SOAP headers in request?! (using first)"];
182 header = [list length] > 0 ? [list objectAtIndex:0] : nil;
183 if (debugParsing) [self debugWithFormat:@"header: %@", header];
187 list = [envelope getElementsByTagName:@"Body"];
188 // TODO: not yet supported by DOMElement: namespaceURI:XMLNS_SOAP_ENVELOPE];
189 if ([list length] == 0) {
190 [self debugWithFormat:@"Note: missing SOAP body."];
191 return [NSException exceptionWithHTTPStatus:400 /* bad request */
192 reason:@"could not parse SOAP body of request"];
194 else if ([list length] > 1) {
195 [self warnWithFormat:@"multiple SOAP bodies in request?! (using first)"];
197 body = [list objectAtIndex:0];
198 if (debugParsing) [self debugWithFormat:@"body: %@", body];
202 return [self performSOAPAction:_actionName
203 header:header body:body inContext:_ctx];
206 /* main dispatcher */
208 - (id)dispatchInContext:(WOContext *)_ctx {
209 NSAutoreleasePool *pool;
211 NSString *SOAPAction;
215 pool = [[NSAutoreleasePool alloc] init];
217 if ((rq = [_ctx request]) == nil) {
218 [self errorWithFormat:@"missing request in context!"];
223 Note: the SOAPAction is also contained in the body which is probably
224 considered the authority.
227 SOAPAction = [rq headerForKey:@"soapaction"];
228 if ([SOAPAction length] == 0) {
229 [self errorWithFormat:@"missing SOAPAction HTTP header!"];
235 if ((dom = [rq contentAsDOMDocument]) == nil) {
236 [self debugWithFormat:@"Note: could not parse XML content of request"];
237 return [NSException exceptionWithHTTPStatus:400 /* bad request */
238 reason:@"could not parse XML content of request"];
242 [[self performSOAPAction:SOAPAction document:dom inContext:_ctx]retain];
245 return [resultObject autorelease];
250 - (NSString *)loggingPrefix {
251 return @"[obj-soap-dispatch]";
253 - (BOOL)isDebuggingEnabled {
257 @end /* SoObjectSOAPDispatcher */
259 @implementation SoSOAPRenderer
261 // TODO: render exceptions as SOAP faults
262 // TODO: maybe support rendering of DOM trees? (should be supported by default)
263 // TODO: maybe some "schema" driven rendering
265 @end /* SoSOAPRenderer */