]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoObjectSOAPDispatcher.m
fixed various warnings
[sope] / sope-appserver / NGObjWeb / SoObjects / SoObjectSOAPDispatcher.m
1 /*
2   Copyright (C) 2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id: SoObjectSOAPDispatcher.m 1 2004-08-20 10:08:27Z znek $
22
23 #include "SoObjectSOAPDispatcher.h"
24 #include "SoObject.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>
32 #include "common.h"
33 #include <DOM/DOM.h>
34 #include <SaxObjC/XMLNamespaces.h>
35
36 /*
37   TODO: is it required by SOAP that the HTTP method is POST?
38
39   SOAP sample:
40     <?xml version="1.0" encoding="UTF-8" standalone="no"?>
41     <SOAP-ENV:Envelope 
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"
46     >
47       <SOAP-ENV:Body 
48         xmlns:types="http://schemas.novell.com/2003/10/NCSP/types.xsd" 
49         SOAP-ENV:encodingStyle=""
50       >
51         <loginRequest>
52           <types:auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
53                       xsi:type="types:PlainText"
54           >
55             <types:username>dummy</types:username>
56             <types:password>user</types:password>
57           </types:auth>
58         </loginRequest>
59       </SOAP-ENV:Body>
60     </SOAP-ENV:Envelope>
61 */
62
63 @interface SoSOAPRenderer : SoDefaultRenderer
64 @end
65
66 @implementation SoObjectSOAPDispatcher
67
68 static BOOL debugOn      = NO;
69 static BOOL debugParsing = NO;
70
71 + (void)initialize {
72   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
73   
74   debugOn = [ud boolForKey:@"SoObjectSOAPDispatcherDebugEnabled"];
75   if (debugOn) NSLog(@"Note: SOPE SOAP dispatcher debugging turned on.");
76 }
77
78 /* XML actions */
79
80 - (id)performSOAPAction:(NSString *)_actionName
81   header:(id<DOMElement>)_header body:(id<DOMElement>)_body
82   inContext:(WOContext *)_ctx
83 {
84   id clientObject;
85   id methodObject;
86   id resultObject;
87
88   if (debugOn) 
89     [self debugWithFormat:@"calling SOAP method: '%@'", _actionName];
90   
91   /* find client object */
92   
93   if ((clientObject = [_ctx clientObject]) != nil) {
94     if (debugOn)
95       [self debugWithFormat:@"  client object from ctx: %@", clientObject];
96   }
97   else if ((clientObject = [self->object clientObject])) {
98     if (debugOn)
99       [self debugWithFormat:@"  setting client object: %@", clientObject];
100     [_ctx setClientObject:clientObject];
101   }
102
103   /* find callable (method) object */
104   
105   // TODO: should we allow acquisition?
106   methodObject = [clientObject lookupName:_actionName inContext:_ctx
107                                acquire:NO];
108   if (methodObject == nil) {
109     if ([_actionName hasSuffix:@"Request"])
110       _actionName = [_actionName substringToIndex:[_actionName length] - 7];
111     methodObject = [clientObject lookupName:_actionName inContext:_ctx
112                                  acquire:NO];
113   }
114   
115   if (methodObject == nil) {
116     [self warnWithFormat:@"could not locate SOAP method: %@", 
117             _actionName];
118     return [NSException exceptionWithHTTPStatus:501 /* not implemented */
119                         reason:@"did not find the specified SOAP method"];
120   }
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"];
127   }
128   if (debugOn) [self debugWithFormat:@"  method: %@", methodObject];
129   
130   /* apply arguments */
131   
132   // TODO: use some query syntax in product.plist to retrieve parameters
133   //       from SOAP
134   
135   // TODO: somehow apply SOPE header/body?
136   if (_header) [_ctx setObject:_header forKey:@"SOAPHeader"];
137   if (_body)   [_ctx setObject:_body   forKey:@"SOAPBody"];
138   
139   if ([methodObject respondsToSelector:
140                       @selector(takeValuesFromRequest:inContext:)]) {
141     if (debugOn)
142       [self debugWithFormat:@"  applying values from request ..."];
143     [methodObject takeValuesFromRequest:[_ctx request] inContext:_ctx];
144   }
145   
146   /* perform call */
147   
148   resultObject = [methodObject callOnObject:[_ctx clientObject] 
149                                inContext:_ctx];
150   if (debugOn) [self debugWithFormat:@"got SOAP result: %@", resultObject];
151   return resultObject;
152 }
153
154 - (id)performSOAPAction:(NSString *)_actionName document:(id)_dom
155   inContext:(WOContext *)_ctx
156 {
157   id<DOMElement>  envelope;
158   id<DOMElement>  header;
159   id<DOMElement>  body;
160   id<DOMNodeList> list;
161
162   /* envelope */
163
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"];
170   }
171   if (debugParsing) [self debugWithFormat:@"envelope: %@", envelope];
172   
173   [_ctx setObject:envelope forKey:@"SOAPEnvelope"];
174   
175   /* header */
176
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)"];
181   }
182   header = [list length] > 0 ? [list objectAtIndex:0] : nil;
183   if (debugParsing) [self debugWithFormat:@"header: %@", header];
184
185   /* body */
186   
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"];
193   }
194   else if ([list length] > 1) {
195     [self warnWithFormat:@"multiple SOAP bodies in request?! (using first)"];
196   }
197   body = [list objectAtIndex:0];
198   if (debugParsing) [self debugWithFormat:@"body: %@", body];
199   
200   /* process */
201   
202   return [self performSOAPAction:_actionName 
203                header:header body:body inContext:_ctx];
204 }
205
206 /* main dispatcher */
207
208 - (id)dispatchInContext:(WOContext *)_ctx {
209   NSAutoreleasePool *pool;
210   WORequest         *rq;
211   NSString          *SOAPAction;
212   id<DOMDocument>   dom;
213   id resultObject;
214   
215   pool = [[NSAutoreleasePool alloc] init];
216   
217   if ((rq = [_ctx request]) == nil) {
218     [self errorWithFormat:@"missing request in context!"];
219     return nil;
220   }
221   
222   /* 
223      Note: the SOAPAction is also contained in the body which is probably
224            considered the authority.
225   */
226   
227   SOAPAction = [rq headerForKey:@"soapaction"];
228   if ([SOAPAction length] == 0) {
229     [self errorWithFormat:@"missing SOAPAction HTTP header!"];
230     return nil;
231   }
232
233   /* parse XML */
234   
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"];
239   }
240   
241   resultObject = 
242     [[self performSOAPAction:SOAPAction document:dom inContext:_ctx]retain];
243   [pool release];
244   
245   return [resultObject autorelease];
246 }
247
248 /* debugging */
249
250 - (NSString *)loggingPrefix {
251   return @"[obj-soap-dispatch]";
252 }
253 - (BOOL)isDebuggingEnabled {
254   return debugOn;
255 }
256
257 @end /* SoObjectSOAPDispatcher */
258
259 @implementation SoSOAPRenderer
260
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
264
265 @end /* SoSOAPRenderer */