2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "SoDefaultRenderer.h"
23 #include "SoObjectRequestHandler.h"
24 #include "SoSecurityManager.h"
25 #include "WOContext+private.h" // required for page rendering
26 #include "WOContext+SoObjects.h"
27 #include "SoSecurityManager.h"
28 #include "SoSecurityException.h"
30 #include "NSException+HTTP.h"
31 #include <NGObjWeb/WOApplication.h>
32 #include <NGObjWeb/WORequest.h>
33 #include <NGObjWeb/WOResponse.h>
34 #include <NGObjWeb/WOElement.h>
35 #include <NGObjWeb/WOComponent.h>
38 @implementation SoDefaultRenderer
40 static int debugOn = 0;
43 static BOOL didInit = NO;
45 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
48 debugOn = [ud boolForKey:@"SoRendererDebugEnabled"];
52 + (id)sharedRenderer {
53 static SoDefaultRenderer *singleton = nil;
55 singleton = [[SoDefaultRenderer alloc] init];
61 - (NSException *)renderException:(NSException *)_ex
62 inContext:(WOContext *)_ctx
64 WOResponse *r = [_ctx response];
67 /* check whether it's a security framework exception */
69 if ([_ex isKindOfClass:[SoSecurityException class]]) {
73 [self debugWithFormat:@" render as security exception: %@", _ex];
75 authenticator = [(SoSecurityException *)_ex authenticator];
76 if (authenticator == nil)
77 authenticator = [[_ctx application] authenticatorInContext:_ctx];
81 [self debugWithFormat:@" authenticator: %@", authenticator];
83 if (([authenticator renderException:_ex inContext:_ctx])) {
85 [self debugWithFormat:@" authenticator did render exception."];
91 [self debugWithFormat:@" missing authenticator."];
96 [self debugWithFormat:@" as exception"];
98 // TODO: add ability to specify HTTP headers in the user info?
100 if ((stat = [_ex httpStatus]) > 0) {
102 if (stat >= 200 && stat < 300) {
103 [r appendContentString:[_ex reason]];
110 [r setHeader:@"text/html; charset=\"iso-8859-1\"" forKey:@"content-type"];
111 [r appendContentString:
112 @"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
113 @"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
115 @"<h3>An error occurred during object publishing</h3>"];
116 [r appendContentString:@"<p>"];
117 [r appendContentString:[_ex reason]];
118 [r appendContentString:@"</p>"];
119 [r appendContentString:@"</body>\n"];
120 [r appendContentString:@"</html>\n"];
124 - (NSException *)renderComponent:(WOComponent *)_c inContext:(WOContext *)_ctx{
125 WOResponse *r = [_ctx response];
128 [self debugWithFormat:
129 @" as component (use appendToResponse:inContext:)"];
131 [r setHeader:@"text/html" forKey:@"content-type"];
133 [_ctx enterComponent:_c content:nil];
134 [_c appendToResponse:r inContext:_ctx];
135 [_ctx leaveComponent:_c];
139 - (NSException *)renderElement:(WOElement *)_e inContext:(WOContext *)_ctx {
141 [self debugWithFormat:@" as element (use appendToResponse:inContext:"];
142 [_e appendToResponse:[_ctx response] inContext:_ctx];
146 - (NSException *)renderData:(NSData *)_data inContext:(WOContext *)_ctx {
147 /* this could be extended to do some MIME magic */
148 WOResponse *r = [_ctx response];
151 [r setHeader:@"application/octet-stream" forKey:@"content-type"];
152 [r setHeader:[NSString stringWithFormat:@"%i", [_data length]]
153 forKey:@"content-length"];
154 [r setContent:_data];
158 - (NSException *)renderTuple:(NSArray *)_tuple inContext:(WOContext *)_ctx {
159 WOResponse *r = [_ctx response];
160 NSString *title = nil, *body = nil;
163 [self debugWithFormat:@" as tuple"];
165 [r appendContentString:@"<html>"];
167 switch ([_tuple count]) {
170 body = [_tuple objectAtIndex:0];
173 title = [_tuple objectAtIndex:0];
174 body = [_tuple objectAtIndex:1];
177 title = [_tuple objectAtIndex:0];
178 body = [[_tuple subarrayWithRange:NSMakeRange(1, [_tuple count] - 1)]
179 componentsJoinedByString:@"<br />"];
183 if ([title length] > 0) {
184 [r appendContentString:@"<head><title>"];
185 [r appendContentHTMLString:title];
186 [r appendContentString:@"</title></head>"];
188 if ([body length] > 0) {
189 [r appendContentString:@"<body>"];
190 [r appendContentHTMLString:body];
191 [r appendContentString:@"</body>"];
193 [r appendContentString:@"</html>"];
197 - (NSException *)renderObjectAsString:(id)_object inContext:(WOContext *)_ctx {
198 /* fall back, use stringValue */
202 [self debugWithFormat:@" render as string (last fallback)"];
206 [r setHeader:@"text/html" forKey:@"content-type"];
207 [r appendContentString:
208 @"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
209 @"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
211 [r appendContentHTMLString:[_object stringValue]];
212 [r appendContentString:@"</body>\n"];
213 [r appendContentString:@"</html>\n"];
217 /* master dispatcher */
219 - (BOOL)processTupleResults {
223 - (NSException *)renderObject:(id)_object inContext:(WOContext *)_ctx {
224 SoSecurityManager *sm;
227 if ([_object isKindOfClass:[WOResponse class]]) {
228 if (_object != [_ctx response]) {
229 [self logWithFormat:@"response mismatch !"];
230 return [NSException exceptionWithHTTPStatus:500 /* internal error */];
232 return nil; /* already rendered */
235 /* base types, no useful security validation possible */
237 if ([_object isKindOfClass:[NSException class]])
238 return [self renderException:_object inContext:_ctx];
240 if ([_object isKindOfClass:[NSData class]])
241 return [self renderData:_object inContext:_ctx];
243 if ([_object isKindOfClass:[NSArray class]] && [self processTupleResults])
244 return [self renderTuple:_object inContext:_ctx];
246 /* objects that require validation */
248 sm = [_ctx soSecurityManager];
249 if ((e = [sm validateObject:_object inContext:_ctx]) != nil)
250 return [self renderException:e inContext:_ctx];
252 if ([_object isKindOfClass:[WOComponent class]])
253 return [self renderComponent:_object inContext:_ctx];
255 if ([_object respondsToSelector:@selector(appendToResponse:inContext:)])
256 return [self renderElement:_object inContext:_ctx];
258 return [self renderObjectAsString:_object inContext:_ctx];
261 - (BOOL)canRenderObject:(id)_object inContext:(WOContext *)_ctx {
267 - (NSString *)loggingPrefix {
268 return @"[so-dflt-renderer]";
271 @end /* SoDefaultRenderer */