]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoDefaultRenderer.m
improved query string handling
[sope] / sope-appserver / NGObjWeb / SoObjects / SoDefaultRenderer.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
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"
29 #include "SoObject.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>
36 #include "common.h"
37
38 @implementation SoDefaultRenderer
39
40 static int debugOn = 0;
41
42 + (void)initialize {
43   static BOOL didInit = NO;
44   if (!didInit) {
45     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
46     didInit = YES;
47     
48     debugOn = [ud boolForKey:@"SoRendererDebugEnabled"];
49   }
50 }
51
52 + (id)sharedRenderer {
53   static SoDefaultRenderer *singleton = nil;
54   if (singleton == nil)
55     singleton = [[SoDefaultRenderer alloc] init];
56   return singleton;
57 }
58
59 /* rendering */
60
61 - (NSException *)renderException:(NSException *)_ex 
62   inContext:(WOContext *)_ctx 
63 {
64   WOResponse *r = [_ctx response];
65   int stat;
66   
67   /* check whether it's a security framework exception */
68   
69   if ([_ex isKindOfClass:[SoSecurityException class]]) {
70     id authenticator;
71     
72     if (debugOn)
73       [self debugWithFormat:@"    render as security exception: %@", _ex];
74     
75     authenticator = [(SoSecurityException *)_ex authenticator];
76     if (authenticator == nil)
77       authenticator = [[_ctx application] authenticatorInContext:_ctx];
78     
79     if (authenticator) {
80       if (debugOn)
81         [self debugWithFormat:@"    authenticator: %@", authenticator];
82       
83       if (([authenticator renderException:_ex inContext:_ctx])) {
84         if (debugOn)
85           [self debugWithFormat:@"    authenticator did render exception."];
86         return nil;
87       }
88     }
89     else {
90       if (debugOn)
91         [self debugWithFormat:@"    missing authenticator."];
92     }
93   }
94   
95   if (debugOn)
96     [self debugWithFormat:@"    as exception"];
97   
98   // TODO: add ability to specify HTTP headers in the user info?
99   
100   if ((stat = [_ex httpStatus]) > 0) {
101     [r setStatus:stat];
102     if (stat >= 200 && stat < 300) {
103       [r appendContentString:[_ex reason]];
104       return nil;
105     }
106   }
107   else
108     [r setStatus:500];
109   
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"
114        @"<body>"
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"];
121   return nil;
122 }
123
124 - (NSException *)renderComponent:(WOComponent *)_c inContext:(WOContext *)_ctx{
125   WOResponse *r = [_ctx response];
126   
127   if (debugOn) {
128     [self debugWithFormat:
129             @"    as component (use appendToResponse:inContext:)"];
130   }
131   [r setHeader:@"text/html" forKey:@"content-type"];
132   [_ctx setPage:_c];
133   [_ctx enterComponent:_c content:nil];
134   [_c appendToResponse:r inContext:_ctx];
135   [_ctx leaveComponent:_c];
136   return nil;
137 }
138
139 - (NSException *)renderElement:(WOElement *)_e inContext:(WOContext *)_ctx {
140   if (debugOn)
141     [self debugWithFormat:@"    as element (use appendToResponse:inContext:"];
142   [_e appendToResponse:[_ctx response] inContext:_ctx];
143   return nil;
144 }
145
146 - (NSException *)renderData:(NSData *)_data inContext:(WOContext *)_ctx {
147   /* this could be extended to do some MIME magic */
148   WOResponse *r = [_ctx response];
149   
150   [r setStatus:200];
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];
155   return nil;
156 }
157
158 - (NSException *)renderTuple:(NSArray *)_tuple inContext:(WOContext *)_ctx {
159   WOResponse *r = [_ctx response];
160   NSString   *title = nil, *body = nil;
161   
162   if (debugOn)
163     [self debugWithFormat:@"    as tuple"];
164   [r setStatus:200];
165   [r appendContentString:@"<html>"];
166   
167   switch ([_tuple count]) {
168   case 0: break;
169   case 1: 
170     body = [_tuple objectAtIndex:0]; 
171     break;
172   case 2: 
173     title = [_tuple objectAtIndex:0];
174     body  = [_tuple objectAtIndex:1];
175     break;
176   case 3: 
177     title = [_tuple objectAtIndex:0];
178     body  = [[_tuple subarrayWithRange:NSMakeRange(1, [_tuple count] - 1)]
179               componentsJoinedByString:@"<br />"];
180     break;
181   }
182   
183   if ([title length] > 0) {
184     [r appendContentString:@"<head><title>"];
185     [r appendContentHTMLString:title];
186     [r appendContentString:@"</title></head>"];
187   }
188   if ([body length] > 0) {
189     [r appendContentString:@"<body>"];
190     [r appendContentHTMLString:body];
191     [r appendContentString:@"</body>"];
192   }
193   [r appendContentString:@"</html>"];
194   return nil;
195 }
196
197 - (NSException *)renderObjectAsString:(id)_object inContext:(WOContext *)_ctx {
198   /* fall back, use stringValue */
199   WOResponse *r;
200   
201   if (debugOn)
202     [self debugWithFormat:@"    render as string (last fallback)"];
203   
204   r = [_ctx response];
205   [r setStatus:200];
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"
210        @"<body>"];
211   [r appendContentHTMLString:[_object stringValue]];
212   [r appendContentString:@"</body>\n"];
213   [r appendContentString:@"</html>\n"];
214   return nil;
215 }
216
217 /* master dispatcher */
218
219 - (BOOL)processTupleResults {
220   return NO;
221 }
222
223 - (NSException *)renderObject:(id)_object inContext:(WOContext *)_ctx {
224   SoSecurityManager *sm;
225   NSException *e;
226   
227   if ([_object isKindOfClass:[WOResponse class]]) {
228     if (_object != [_ctx response]) {
229       [self logWithFormat:@"response mismatch !"];
230       return [NSException exceptionWithHTTPStatus:500 /* internal error */];
231     }
232     return nil; /* already rendered */
233   }
234   
235   /* base types, no useful security validation possible */
236   
237   if ([_object isKindOfClass:[NSException class]])
238     return [self renderException:_object inContext:_ctx];
239   
240   if ([_object isKindOfClass:[NSData class]])
241     return [self renderData:_object inContext:_ctx];
242   
243   if ([_object isKindOfClass:[NSArray class]] && [self processTupleResults])
244     return [self renderTuple:_object inContext:_ctx];
245   
246   /* objects that require validation */
247   
248   sm = [_ctx soSecurityManager];
249   if ((e = [sm validateObject:_object inContext:_ctx]) != nil)
250     return [self renderException:e inContext:_ctx];
251   
252   if ([_object isKindOfClass:[WOComponent class]])
253     return [self renderComponent:_object inContext:_ctx];
254   
255   if ([_object respondsToSelector:@selector(appendToResponse:inContext:)])
256     return [self renderElement:_object inContext:_ctx];
257   
258   return [self renderObjectAsString:_object inContext:_ctx];
259 }
260
261 - (BOOL)canRenderObject:(id)_object inContext:(WOContext *)_ctx {
262   return YES;
263 }
264
265 /* debugging */
266
267 - (NSString *)loggingPrefix {
268   return @"[so-dflt-renderer]";
269 }
270
271 @end /* SoDefaultRenderer */