egalite 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/README.md +91 -0
- data/auth/basic.rb +32 -0
- data/blank.rb +53 -0
- data/egalite.rb +742 -0
- data/errorconsole.rb +77 -0
- data/examples/simple/example.rb +39 -0
- data/examples/simple/pages/test.html +15 -0
- data/examples/simple_db/example_db.rb +103 -0
- data/examples/simple_db/pages/edit.html +6 -0
- data/helper.rb +251 -0
- data/keitai/keitai.rb +107 -0
- data/keitai/ketai.rb +11 -0
- data/keitai/rack/ketai/carrier/abstract.rb +131 -0
- data/keitai/rack/ketai/carrier/au.rb +78 -0
- data/keitai/rack/ketai/carrier/docomo.rb +80 -0
- data/keitai/rack/ketai/carrier/emoji/ausjisstrtoemojiid.rb +1391 -0
- data/keitai/rack/ketai/carrier/emoji/docomosjisstrtoemojiid.rb +759 -0
- data/keitai/rack/ketai/carrier/emoji/emojidata.rb +836 -0
- data/keitai/rack/ketai/carrier/emoji/softbankutf8strtoemojiid.rb +1119 -0
- data/keitai/rack/ketai/carrier/emoji/softbankwebcodetoutf8str.rb +499 -0
- data/keitai/rack/ketai/carrier/iphone.rb +8 -0
- data/keitai/rack/ketai/carrier/softbank.rb +82 -0
- data/keitai/rack/ketai/carrier.rb +17 -0
- data/keitai/rack/ketai/middleware.rb +24 -0
- data/m17n.rb +193 -0
- data/rack/auth/abstract/handler.rb +37 -0
- data/rack/auth/abstract/request.rb +37 -0
- data/rack/auth/basic.rb +58 -0
- data/rack/auth/digest/md5.rb +124 -0
- data/rack/auth/digest/nonce.rb +51 -0
- data/rack/auth/digest/params.rb +55 -0
- data/rack/auth/digest/request.rb +40 -0
- data/rack/builder.rb +80 -0
- data/rack/cascade.rb +41 -0
- data/rack/chunked.rb +49 -0
- data/rack/commonlogger.rb +49 -0
- data/rack/conditionalget.rb +47 -0
- data/rack/config.rb +15 -0
- data/rack/content_length.rb +29 -0
- data/rack/content_type.rb +23 -0
- data/rack/deflater.rb +96 -0
- data/rack/directory.rb +157 -0
- data/rack/etag.rb +32 -0
- data/rack/file.rb +92 -0
- data/rack/handler/cgi.rb +62 -0
- data/rack/handler/evented_mongrel.rb +8 -0
- data/rack/handler/fastcgi.rb +89 -0
- data/rack/handler/lsws.rb +63 -0
- data/rack/handler/mongrel.rb +90 -0
- data/rack/handler/scgi.rb +59 -0
- data/rack/handler/swiftiplied_mongrel.rb +8 -0
- data/rack/handler/thin.rb +18 -0
- data/rack/handler/webrick.rb +73 -0
- data/rack/handler.rb +88 -0
- data/rack/head.rb +19 -0
- data/rack/lint.rb +567 -0
- data/rack/lobster.rb +65 -0
- data/rack/lock.rb +16 -0
- data/rack/logger.rb +20 -0
- data/rack/methodoverride.rb +27 -0
- data/rack/mime.rb +208 -0
- data/rack/mock.rb +190 -0
- data/rack/nulllogger.rb +18 -0
- data/rack/recursive.rb +61 -0
- data/rack/reloader.rb +109 -0
- data/rack/request.rb +273 -0
- data/rack/response.rb +150 -0
- data/rack/rewindable_input.rb +103 -0
- data/rack/runtime.rb +27 -0
- data/rack/sendfile.rb +144 -0
- data/rack/server.rb +271 -0
- data/rack/session/abstract/id.rb +140 -0
- data/rack/session/cookie.rb +90 -0
- data/rack/session/memcache.rb +119 -0
- data/rack/session/pool.rb +100 -0
- data/rack/showexceptions.rb +349 -0
- data/rack/showstatus.rb +106 -0
- data/rack/static.rb +38 -0
- data/rack/urlmap.rb +55 -0
- data/rack/utils.rb +662 -0
- data/rack.rb +81 -0
- data/route.rb +231 -0
- data/sendmail.rb +222 -0
- data/sequel_helper.rb +20 -0
- data/session.rb +132 -0
- data/stringify_hash.rb +63 -0
- data/support.rb +35 -0
- data/template.rb +287 -0
- data/test/french.html +13 -0
- data/test/french_msg.html +3 -0
- data/test/m17n.txt +30 -0
- data/test/mobile.html +15 -0
- data/test/setup.rb +8 -0
- data/test/static/test.txt +1 -0
- data/test/template.html +58 -0
- data/test/template_inner.html +1 -0
- data/test/template_innerparam.html +1 -0
- data/test/test_auth.rb +43 -0
- data/test/test_blank.rb +44 -0
- data/test/test_csrf.rb +87 -0
- data/test/test_errorconsole.rb +91 -0
- data/test/test_handler.rb +155 -0
- data/test/test_helper.rb +296 -0
- data/test/test_keitai.rb +107 -0
- data/test/test_m17n.rb +129 -0
- data/test/test_route.rb +192 -0
- data/test/test_sendmail.rb +146 -0
- data/test/test_session.rb +83 -0
- data/test/test_stringify_hash.rb +67 -0
- data/test/test_template.rb +114 -0
- data/test.bat +2 -0
- metadata +240 -0
data/egalite.rb
ADDED
@@ -0,0 +1,742 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
require 'blank'
|
5
|
+
|
6
|
+
require 'stringify_hash'
|
7
|
+
|
8
|
+
require 'template'
|
9
|
+
require 'route'
|
10
|
+
require 'session'
|
11
|
+
require 'helper'
|
12
|
+
|
13
|
+
require 'time'
|
14
|
+
require 'monitor'
|
15
|
+
require 'digest/md5'
|
16
|
+
|
17
|
+
module Rack
|
18
|
+
module Utils
|
19
|
+
def normalize_params(params,name,v)
|
20
|
+
params[name] = v
|
21
|
+
end
|
22
|
+
module_function :normalize_params
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class CriticalError < RuntimeError
|
27
|
+
end
|
28
|
+
|
29
|
+
module Egalite
|
30
|
+
|
31
|
+
module AccessLogger
|
32
|
+
@@io = nil
|
33
|
+
@@time = nil
|
34
|
+
@@lock = Monitor.new
|
35
|
+
class <<self
|
36
|
+
def io=(io)
|
37
|
+
@@io=io
|
38
|
+
end
|
39
|
+
def _open
|
40
|
+
@@dir = dir
|
41
|
+
@@time = Time.now
|
42
|
+
fn = sprintf("egaliteaccess-%04d-%02d-%02d-p%d.log", @@time.year, @@time.month, @@time.mday, Process.pid)
|
43
|
+
@@io = open(File.join(dir,fn), "a")
|
44
|
+
end
|
45
|
+
def open(dir)
|
46
|
+
@@dir = dir
|
47
|
+
yield
|
48
|
+
ensure
|
49
|
+
@@io.close if @@io
|
50
|
+
end
|
51
|
+
def write(line)
|
52
|
+
return nil unless @@io
|
53
|
+
|
54
|
+
@@lock.synchronize {
|
55
|
+
if @@time and (@@time.mday != Time.now.mday)
|
56
|
+
## log rotation
|
57
|
+
@@io.close
|
58
|
+
_open
|
59
|
+
end
|
60
|
+
@@io.puts(line)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class DebugLogger
|
67
|
+
def initialize(path)
|
68
|
+
@path = path
|
69
|
+
end
|
70
|
+
def puts(s)
|
71
|
+
open(@path, "a") { |f|
|
72
|
+
begin
|
73
|
+
f.flock(File::LOCK_EX)
|
74
|
+
f.puts s
|
75
|
+
f.flush
|
76
|
+
ensure
|
77
|
+
f.flock(File::LOCK_UN)
|
78
|
+
end
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module ErrorLogger
|
84
|
+
@@table = nil
|
85
|
+
@@admin_emails = nil
|
86
|
+
class <<self
|
87
|
+
def table=(t)
|
88
|
+
@@table=t
|
89
|
+
end
|
90
|
+
def admin_emails=(t)
|
91
|
+
@@admin_emails=t
|
92
|
+
end
|
93
|
+
def write(hash)
|
94
|
+
hash[:md5] = Digest::MD5.hexdigest(hash[:text]) unless hash[:md5]
|
95
|
+
if hash[:severity] == 'critical' and @@admin_emails
|
96
|
+
Sendmail.send(hash[:text],{
|
97
|
+
:from => 'info@xcream.net',
|
98
|
+
:to => @@admin_emails,
|
99
|
+
:subject => 'Critical error at xcream.net'
|
100
|
+
})
|
101
|
+
end
|
102
|
+
@@table.insert(hash) if @@table
|
103
|
+
end
|
104
|
+
def write_exception(e, hash)
|
105
|
+
severity = 'exception'
|
106
|
+
severity = 'security' if e.is_a?(SecurityError)
|
107
|
+
severity = 'critical' if e.is_a?(CriticalError)
|
108
|
+
|
109
|
+
text = "#{e.to_s}\n#{e.backtrace.join("\n")}"
|
110
|
+
|
111
|
+
ErrorLogger.write({:severity => severity, :text => text}.merge(hash))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Controller
|
117
|
+
attr_accessor :env, :req, :params, :template_file, :log_values
|
118
|
+
undef id
|
119
|
+
|
120
|
+
# filters
|
121
|
+
def before_filter
|
122
|
+
true
|
123
|
+
end
|
124
|
+
def after_filter_return_value(response) # right after controller
|
125
|
+
response
|
126
|
+
end
|
127
|
+
def after_filter(response) # after filter for final http output
|
128
|
+
response
|
129
|
+
end
|
130
|
+
def filter_on_html_load(html, htmlfile)
|
131
|
+
html
|
132
|
+
end
|
133
|
+
|
134
|
+
# accessors
|
135
|
+
def db
|
136
|
+
@env.db
|
137
|
+
end
|
138
|
+
def cookies
|
139
|
+
@req.cookies
|
140
|
+
end
|
141
|
+
def session
|
142
|
+
@req.session
|
143
|
+
end
|
144
|
+
def id
|
145
|
+
@params[:id]
|
146
|
+
end
|
147
|
+
|
148
|
+
# results
|
149
|
+
def notfound
|
150
|
+
EgaliteResponse.new(:notfound)
|
151
|
+
end
|
152
|
+
def redirect(url)
|
153
|
+
url = url_for(url) if url.is_a?(Hash)
|
154
|
+
EgaliteResponse.new(:redirect, url)
|
155
|
+
end
|
156
|
+
alias :redirect_to :redirect
|
157
|
+
|
158
|
+
def redirect_permanent(url)
|
159
|
+
url = url_for(url) if url.is_a?(Hash)
|
160
|
+
[301,{'Location' => url}, [url]]
|
161
|
+
end
|
162
|
+
|
163
|
+
def delegate(params)
|
164
|
+
EgaliteResponse.new(:delegate, params)
|
165
|
+
end
|
166
|
+
def include(params)
|
167
|
+
raw(req.handler.inner_dispatch(req, params)[2].to_s)
|
168
|
+
end
|
169
|
+
def send_file(path, content_type = nil)
|
170
|
+
ext = File.extname(path)[1..-1]
|
171
|
+
|
172
|
+
if File.file?(path) && File.readable?(path)
|
173
|
+
s = nil
|
174
|
+
open(path, "rb") { |file|
|
175
|
+
s = file.read
|
176
|
+
}
|
177
|
+
return [200, {
|
178
|
+
"Last-Modified" => File.mtime(path).rfc822,
|
179
|
+
"Content-Type" => content_type || MIME_TYPES[ext] || "text/plain",
|
180
|
+
"Content-Length" => File.size(path).to_s
|
181
|
+
}, s]
|
182
|
+
else
|
183
|
+
return [404, {"Content-Type" => "text/plain"}, ["File not found\n"]]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
def send_data(data, content_type)
|
187
|
+
[200,{"Content-Type" => content_type},[data]]
|
188
|
+
end
|
189
|
+
|
190
|
+
# helpers
|
191
|
+
def url_for(prms)
|
192
|
+
@req.route.url_for(prms, req.host, req.port, req.scheme)
|
193
|
+
end
|
194
|
+
def link_to(title,prms)
|
195
|
+
return tags.a(prms,title) if prms.is_a?(String)
|
196
|
+
raw(@req.route.link_to(title,prms, req.host, req.port, req.scheme))
|
197
|
+
end
|
198
|
+
def raw(text)
|
199
|
+
NonEscapeString.new(text)
|
200
|
+
end
|
201
|
+
def escape_html(s)
|
202
|
+
tags.escape_html(s)
|
203
|
+
end
|
204
|
+
def tags
|
205
|
+
HTMLTagBuilder
|
206
|
+
end
|
207
|
+
def table_by_array(header,content,opts={})
|
208
|
+
TableHelper.table_by_array(header,content,opts)
|
209
|
+
end
|
210
|
+
def form(data={},param_name = nil, opts = {})
|
211
|
+
FormHelper.new(data,param_name,opts)
|
212
|
+
end
|
213
|
+
def file_form(data={},param_name = nil, opts = {})
|
214
|
+
FormHelper.new(data,param_name,opts.merge(:enctype => 'multipart/form-data'))
|
215
|
+
end
|
216
|
+
def errorlog(severity, text)
|
217
|
+
logid = Egalite::ErrorLogger.write(:severity => severity, :ipaddress => @req.ipaddr, :text => text, :url => @req.url)
|
218
|
+
logid
|
219
|
+
end
|
220
|
+
|
221
|
+
# From WEBrick.
|
222
|
+
MIME_TYPES = {
|
223
|
+
"ai" => "application/postscript",
|
224
|
+
"asc" => "text/plain",
|
225
|
+
"avi" => "video/x-msvideo",
|
226
|
+
"bin" => "application/octet-stream",
|
227
|
+
"bmp" => "image/bmp",
|
228
|
+
"class" => "application/octet-stream",
|
229
|
+
"cer" => "application/pkix-cert",
|
230
|
+
"crl" => "application/pkix-crl",
|
231
|
+
"crt" => "application/x-x509-ca-cert",
|
232
|
+
#"crl" => "application/x-pkcs7-crl",
|
233
|
+
"css" => "text/css",
|
234
|
+
"dms" => "application/octet-stream",
|
235
|
+
"doc" => "application/msword",
|
236
|
+
"dvi" => "application/x-dvi",
|
237
|
+
"eps" => "application/postscript",
|
238
|
+
"etx" => "text/x-setext",
|
239
|
+
"exe" => "application/octet-stream",
|
240
|
+
"gif" => "image/gif",
|
241
|
+
"htm" => "text/html",
|
242
|
+
"html" => "text/html",
|
243
|
+
"jpe" => "image/jpeg",
|
244
|
+
"jpeg" => "image/jpeg",
|
245
|
+
"jpg" => "image/jpeg",
|
246
|
+
"js" => "text/javascript",
|
247
|
+
"lha" => "application/octet-stream",
|
248
|
+
"lzh" => "application/octet-stream",
|
249
|
+
"mov" => "video/quicktime",
|
250
|
+
"mpe" => "video/mpeg",
|
251
|
+
"mpeg" => "video/mpeg",
|
252
|
+
"mpg" => "video/mpeg",
|
253
|
+
"pbm" => "image/x-portable-bitmap",
|
254
|
+
"pdf" => "application/pdf",
|
255
|
+
"pgm" => "image/x-portable-graymap",
|
256
|
+
"png" => "image/png",
|
257
|
+
"pnm" => "image/x-portable-anymap",
|
258
|
+
"ppm" => "image/x-portable-pixmap",
|
259
|
+
"ppt" => "application/vnd.ms-powerpoint",
|
260
|
+
"ps" => "application/postscript",
|
261
|
+
"qt" => "video/quicktime",
|
262
|
+
"ras" => "image/x-cmu-raster",
|
263
|
+
"rb" => "text/plain",
|
264
|
+
"rd" => "text/plain",
|
265
|
+
"rtf" => "application/rtf",
|
266
|
+
"sgm" => "text/sgml",
|
267
|
+
"sgml" => "text/sgml",
|
268
|
+
"tif" => "image/tiff",
|
269
|
+
"tiff" => "image/tiff",
|
270
|
+
"txt" => "text/plain",
|
271
|
+
"xbm" => "image/x-xbitmap",
|
272
|
+
"xls" => "application/vnd.ms-excel",
|
273
|
+
"xml" => "text/xml",
|
274
|
+
"xpm" => "image/x-xpixmap",
|
275
|
+
"xwd" => "image/x-xwindowdump",
|
276
|
+
"zip" => "application/zip",
|
277
|
+
}
|
278
|
+
end
|
279
|
+
|
280
|
+
module CSRFFilter
|
281
|
+
def after_filter_return_value(response) # right after controller
|
282
|
+
p "CSRFFilter"
|
283
|
+
if session and session.sstr and response.is_a?(Hash)
|
284
|
+
response.merge(:csrf => session.sstr)
|
285
|
+
elsif session and session.sstr and response.is_a?(Sequel::Model)
|
286
|
+
response[:csrf] = session.sstr
|
287
|
+
response
|
288
|
+
else
|
289
|
+
response
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
class CSRFController < Controller
|
294
|
+
def after_filter_return_value(response) # right after controller
|
295
|
+
if session and session.sstr and response.is_a?(Hash)
|
296
|
+
response.merge(:csrf => session.sstr)
|
297
|
+
elsif session and session.sstr and response.is_a?(Sequel::Model)
|
298
|
+
response[:csrf] = session.sstr
|
299
|
+
response
|
300
|
+
else
|
301
|
+
response
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class EgaliteError < RuntimeError
|
307
|
+
end
|
308
|
+
class EgaliteResponse
|
309
|
+
attr_accessor :command
|
310
|
+
attr_accessor :param
|
311
|
+
|
312
|
+
def initialize(com, param = nil)
|
313
|
+
@command = com
|
314
|
+
@param = param
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
class Environment
|
319
|
+
attr_reader :db, :opts
|
320
|
+
|
321
|
+
def initialize(db,opts)
|
322
|
+
@db = db
|
323
|
+
@opts = opts
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
class Request
|
328
|
+
attr_accessor :session, :cookies, :authorization
|
329
|
+
attr_accessor :language, :method
|
330
|
+
attr_accessor :route, :controller, :action, :params, :path_info, :path_params
|
331
|
+
attr_accessor :controller_class, :action_method
|
332
|
+
attr_reader :rack_request, :time, :handler
|
333
|
+
|
334
|
+
def initialize(values = {})
|
335
|
+
@cookies = []
|
336
|
+
@rack_request = values[:rack_request]
|
337
|
+
@handler = values[:handler]
|
338
|
+
@rack_env = values[:rack_env]
|
339
|
+
@time = Time.now
|
340
|
+
end
|
341
|
+
def accept_language
|
342
|
+
@rack_env['HTTP_ACCEPT_LANGUAGE']
|
343
|
+
end
|
344
|
+
def scheme
|
345
|
+
@rack_request.scheme
|
346
|
+
end
|
347
|
+
def port
|
348
|
+
@rack_request.port
|
349
|
+
end
|
350
|
+
def host
|
351
|
+
@rack_request.host
|
352
|
+
end
|
353
|
+
def ipaddr
|
354
|
+
@rack_request.ip
|
355
|
+
end
|
356
|
+
def path
|
357
|
+
@rack_request.path
|
358
|
+
end
|
359
|
+
def url
|
360
|
+
@rack_request.url
|
361
|
+
end
|
362
|
+
def referrer
|
363
|
+
@rack_request.referrer
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
class Handler
|
368
|
+
attr_accessor :routes
|
369
|
+
|
370
|
+
def initialize(opts = {})
|
371
|
+
@routes = opts[:routes] || Route.default_routes
|
372
|
+
|
373
|
+
db = opts[:db]
|
374
|
+
@db = db
|
375
|
+
@opts = opts
|
376
|
+
@env = Environment.new(db, opts)
|
377
|
+
opts[:static_root] ||= "static/"
|
378
|
+
|
379
|
+
@template_path = opts[:template_path] || 'pages/'
|
380
|
+
@template_path << '/' if @template_path[-1..-1] != '/'
|
381
|
+
@template_engine = opts[:template_engine] || HTMLTemplate
|
382
|
+
|
383
|
+
@profile_logger = opts[:profile_logger]
|
384
|
+
@notfound_template = opts[:notfound_template]
|
385
|
+
@error_template = opts[:error_template]
|
386
|
+
@admin_emails = opts[:admin_emails]
|
387
|
+
@exception_log_table = opts[:exception_log_table]
|
388
|
+
if @exception_log_table
|
389
|
+
Egalite::ErrorLogger.table = db[@exception_log_table]
|
390
|
+
Egalite::ErrorLogger.admin_emails = @admin_emails
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
private
|
395
|
+
def load_template(tmpl)
|
396
|
+
# to expand: template caching
|
397
|
+
if File.file?(tmpl) && File.readable?(tmpl)
|
398
|
+
open(tmpl) { |f| f.read }
|
399
|
+
else
|
400
|
+
nil
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
def escape_html(s)
|
405
|
+
Rack::Utils.escape_html(s)
|
406
|
+
end
|
407
|
+
|
408
|
+
def display_notfound
|
409
|
+
if @notfound_template
|
410
|
+
[404, {"Content-Type" => "text/html"}, [load_template(@notfound_template)]]
|
411
|
+
else
|
412
|
+
[404, {"Content-Type" => "text/plain"}, ['404 not found']]
|
413
|
+
end
|
414
|
+
end
|
415
|
+
def redirect(url)
|
416
|
+
[302,{'Location' => url}, [url]]
|
417
|
+
end
|
418
|
+
def get_controller(controllername,action, method)
|
419
|
+
action = method if action.blank?
|
420
|
+
action.downcase!
|
421
|
+
action.gsub!(/[^0-9a-z_]/,'')
|
422
|
+
|
423
|
+
return nil if action == ""
|
424
|
+
|
425
|
+
Controller.new.methods.each { |s| raise SecurityError if action == s }
|
426
|
+
|
427
|
+
controllername ||= ''
|
428
|
+
controllername = controllername.split('/').map { |c|
|
429
|
+
c.downcase!
|
430
|
+
c.gsub!(/[^0-9a-z]/,'')
|
431
|
+
c.capitalize
|
432
|
+
}.join
|
433
|
+
controllername = 'Default' if controllername.blank?
|
434
|
+
|
435
|
+
kontroller = nil
|
436
|
+
begin
|
437
|
+
kontroller = Object.const_get(controllername+'Controller')
|
438
|
+
rescue Exception => e
|
439
|
+
return nil
|
440
|
+
end
|
441
|
+
|
442
|
+
controller = kontroller.new
|
443
|
+
method = method.downcase
|
444
|
+
|
445
|
+
unless controller.respond_to?(action)
|
446
|
+
if controller.respond_to?("#{action}_#{method}")
|
447
|
+
action = "#{action}_#{method}"
|
448
|
+
else
|
449
|
+
return nil
|
450
|
+
end
|
451
|
+
end
|
452
|
+
[controller, action]
|
453
|
+
end
|
454
|
+
|
455
|
+
def forbidden
|
456
|
+
[403, {'Content-Type' => 'text/plain'}, ['Forbidden']]
|
457
|
+
end
|
458
|
+
|
459
|
+
def display_internal_server_error(e)
|
460
|
+
html = [
|
461
|
+
"<html><body>",
|
462
|
+
"<h1>Internal server error.</h1>"
|
463
|
+
]
|
464
|
+
if ShowException
|
465
|
+
html += [
|
466
|
+
"<p>Exception: #{escape_html(e.to_s)}</p>",
|
467
|
+
"<h2>Back trace</h2>",
|
468
|
+
"<p>#{e.backtrace.map{|s|escape_html(s)}.join("<br/>\n")}</p>"
|
469
|
+
]
|
470
|
+
end
|
471
|
+
html << "</body></html>"
|
472
|
+
[500, {'Content-Type' => 'text/html'}, [html.join("\n")]]
|
473
|
+
end
|
474
|
+
|
475
|
+
def set_cookies_to_response(response,req)
|
476
|
+
req.cookies.each { |k,v|
|
477
|
+
cookie_opts = @opts[:cookie_opts] || {}
|
478
|
+
unless v.is_a?(Hash)
|
479
|
+
req.cookies[k] = {
|
480
|
+
:value => v.to_s,
|
481
|
+
:expires => Time.now + (cookie_opts[:expire_after] || 3600),
|
482
|
+
:path => cookie_opts[:path] || '/',
|
483
|
+
:secure => cookie_opts[:secure] || false
|
484
|
+
}
|
485
|
+
end
|
486
|
+
}
|
487
|
+
a = req.cookies.map { |k,v|
|
488
|
+
s = "#{Rack::Utils.escape(k)}=#{Rack::Utils.escape(v[:value])}"
|
489
|
+
s += "; domain=#{v[:domain]}" if v[:domain]
|
490
|
+
s += "; path=#{v[:path]}" if v[:path]
|
491
|
+
s += "; expires=#{v[:expires].clone.gmtime.strftime("%a, %d-%b-%Y %H:%M:%S GMT")}" if v[:expires]
|
492
|
+
s += "; secure" if v[:secure]
|
493
|
+
s
|
494
|
+
}
|
495
|
+
s = a.join("\n")
|
496
|
+
response[1]['Set-Cookie'] = s
|
497
|
+
end
|
498
|
+
|
499
|
+
public
|
500
|
+
def inner_dispatch(req, values)
|
501
|
+
# recursive controller call to handle include tag or delegate.
|
502
|
+
stringified = StringifyHash.create(values)
|
503
|
+
(path, params) = req.route.get_path_and_params_from_params(stringified)
|
504
|
+
newreq = req.clone
|
505
|
+
newreq.params = params
|
506
|
+
method = 'GET'
|
507
|
+
method = values[:http_method] if values[:http_method]
|
508
|
+
dispatch(path, params, method, newreq)
|
509
|
+
end
|
510
|
+
def run_controller(controller, action, req)
|
511
|
+
# invoke controller
|
512
|
+
controller.env = @env
|
513
|
+
controller.req = req
|
514
|
+
controller.params = req.params
|
515
|
+
|
516
|
+
before_filter_result = controller.before_filter
|
517
|
+
if before_filter_result != true
|
518
|
+
return before_filter_result if before_filter_result.is_a?(Array)
|
519
|
+
return forbidden unless before_filter_result.respond_to?(:command)
|
520
|
+
response = case before_filter_result.command
|
521
|
+
when :delegate
|
522
|
+
inner_dispatch(req, before_filter_result.param)
|
523
|
+
when :redirect
|
524
|
+
redirect(before_filter_result.param)
|
525
|
+
when :notfound
|
526
|
+
display_notfound
|
527
|
+
else
|
528
|
+
forbidden
|
529
|
+
end
|
530
|
+
set_cookies_to_response(response,req)
|
531
|
+
return response
|
532
|
+
end
|
533
|
+
|
534
|
+
nargs = controller.method(action).arity
|
535
|
+
args = req.path_params[0,nargs.abs] || []
|
536
|
+
if nargs > 0
|
537
|
+
args.size.upto(nargs-1) { args << nil }
|
538
|
+
end
|
539
|
+
raise SecurityError unless controller.respond_to?(action, false)
|
540
|
+
|
541
|
+
s = Time.now
|
542
|
+
values = controller.send(action,*args)
|
543
|
+
t = Time.now - s
|
544
|
+
@profile_logger.puts "#{Time.now}: ctrl #{t}sec #{controller.class.name}.#{action} (#{req.path_info})" if @profile_logger
|
545
|
+
|
546
|
+
values = controller.after_filter_return_value(values)
|
547
|
+
|
548
|
+
# result handling
|
549
|
+
result = if values.respond_to?(:command)
|
550
|
+
case values.command
|
551
|
+
when :delegate
|
552
|
+
inner_dispatch(req, values.param)
|
553
|
+
when :redirect
|
554
|
+
redirect(values.param)
|
555
|
+
when :notfound
|
556
|
+
display_notfound
|
557
|
+
end
|
558
|
+
elsif values.is_a?(Array)
|
559
|
+
values
|
560
|
+
elsif values.is_a?(String)
|
561
|
+
[200,{'Content-Type' => "text/html"},[values]]
|
562
|
+
elsif values.is_a?(Rack::Response)
|
563
|
+
values.to_a
|
564
|
+
elsif values == nil
|
565
|
+
raise "egalite error: controller returned nil as a response."
|
566
|
+
else
|
567
|
+
htmlfile = controller.template_file
|
568
|
+
unless htmlfile
|
569
|
+
htmlfile = [req.controller,req.action].compact.join('_')
|
570
|
+
htmlfile = 'index' if htmlfile.blank?
|
571
|
+
htmlfile += '.html'
|
572
|
+
end
|
573
|
+
html = load_template(@template_path + htmlfile)
|
574
|
+
return [404, {"Content-Type" => "text/plain"}, ["Template not found: #{htmlfile}\n"]] unless html
|
575
|
+
|
576
|
+
# apply on_html_load filter
|
577
|
+
html = controller.filter_on_html_load(html, htmlfile)
|
578
|
+
|
579
|
+
# apply html template
|
580
|
+
template = @template_engine.new
|
581
|
+
template.controller = controller
|
582
|
+
|
583
|
+
s = Time.now
|
584
|
+
template.handleTemplate(html,values) { |values|
|
585
|
+
inner_dispatch(req,values)[2]
|
586
|
+
}
|
587
|
+
t = Time.now - s
|
588
|
+
@profile_logger.puts "#{Time.now}: view #{t}sec #{controller.class.name}.#{action} (#{req.path_info})" if @profile_logger
|
589
|
+
|
590
|
+
[200,{"Content-Type"=>"text/html"},[html]]
|
591
|
+
end
|
592
|
+
set_cookies_to_response(result,req)
|
593
|
+
return result
|
594
|
+
end
|
595
|
+
|
596
|
+
def dispatch(path, params, method, req, first_call = false)
|
597
|
+
# routing
|
598
|
+
(controller_name, action_name, path_params, prmhash) = nil
|
599
|
+
(controller, action) = nil
|
600
|
+
|
601
|
+
route = @routes.find { |route|
|
602
|
+
puts "Routing: matching: #{route.inspect}" if RouteDebug
|
603
|
+
route_result = route.match(path)
|
604
|
+
(controller_name, action_name, path_params, prmhash) = route_result
|
605
|
+
next if route_result == nil
|
606
|
+
puts "Routing: pre-matched: #{route_result.inspect}" if RouteDebug
|
607
|
+
(controller, action) = get_controller(controller_name, action_name, method)
|
608
|
+
true if controller
|
609
|
+
}
|
610
|
+
return display_notfound unless controller
|
611
|
+
|
612
|
+
puts "Routing: matched: #{controller.class} #{action}" if RouteDebug
|
613
|
+
params = prmhash.merge(params)
|
614
|
+
|
615
|
+
req.route = route
|
616
|
+
req.controller = controller_name
|
617
|
+
req.controller_class = controller
|
618
|
+
req.action = action_name
|
619
|
+
req.action_method = action
|
620
|
+
req.path_params = path_params
|
621
|
+
req.path_info = path_params.join('/')
|
622
|
+
|
623
|
+
# todo: language handling (by pathinfo?)
|
624
|
+
# todo: session handling (by pathinfo?)
|
625
|
+
|
626
|
+
res = run_controller(controller, action, req)
|
627
|
+
|
628
|
+
if first_call
|
629
|
+
controller.after_filter(res.to_a)
|
630
|
+
|
631
|
+
# access log
|
632
|
+
t = Time.now - req.time
|
633
|
+
log = [req.time.iso8601, req.ipaddr, t, req.url, req.referrer]
|
634
|
+
log += controller.log_values.to_a
|
635
|
+
line = log.map {|s| s.to_s.gsub(/\t/,'')}.join("\t").gsub(/\n/,'')
|
636
|
+
AccessLogger.write(line)
|
637
|
+
end
|
638
|
+
res
|
639
|
+
end
|
640
|
+
|
641
|
+
def call(rack_env)
|
642
|
+
# set up logging
|
643
|
+
|
644
|
+
res = nil
|
645
|
+
|
646
|
+
req = Rack::Request.new(rack_env)
|
647
|
+
|
648
|
+
begin
|
649
|
+
ereq = Request.new(
|
650
|
+
:rack_request => req,
|
651
|
+
:rack_env => rack_env,
|
652
|
+
:handler => self
|
653
|
+
)
|
654
|
+
|
655
|
+
# parameter handling
|
656
|
+
params = StringifyHash.new
|
657
|
+
req.params.each { |k,v|
|
658
|
+
# raise 'egalite: no multiple query parameter allowed in same keyword.' if v.is_a?(Array)
|
659
|
+
next unless k
|
660
|
+
frags = k.split(/[\]\[]{1,2}/)
|
661
|
+
last = frags.pop
|
662
|
+
list = params
|
663
|
+
frags.each { |frag|
|
664
|
+
list[frag] ||= StringifyHash.new
|
665
|
+
list = list[frag]
|
666
|
+
}
|
667
|
+
list[last] = v
|
668
|
+
}
|
669
|
+
|
670
|
+
puts "before-cookie: #{req.cookies.inspect}" if @opts[:cookie_debug]
|
671
|
+
|
672
|
+
ereq.params = params
|
673
|
+
ereq.cookies = req.cookies
|
674
|
+
|
675
|
+
authorization_keys = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
|
676
|
+
key = authorization_keys.detect { |key| rack_env.has_key?(key) }
|
677
|
+
ereq.authorization = rack_env[key] if key
|
678
|
+
|
679
|
+
if @opts[:session_handler]
|
680
|
+
ereq.session = @opts[:session_handler].new(@env,ereq.cookies, @opts[:session_opts] || {})
|
681
|
+
ereq.session.load
|
682
|
+
end
|
683
|
+
|
684
|
+
res = dispatch(req.path_info, params, req.request_method, ereq, true)
|
685
|
+
res = res.to_a
|
686
|
+
|
687
|
+
puts "after-cookie: #{res[1]['Set-Cookie'].inspect}" if @opts[:cookie_debug]
|
688
|
+
|
689
|
+
if res[0] == 200
|
690
|
+
if res[1]['Content-Type'] !~ /charset/i and res[1]['Content-Type'] =~ /text\/html/i
|
691
|
+
res[1]["Content-Type"] = @opts[:charset] || 'text/html; charset=utf-8'
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
rescue Exception => e
|
696
|
+
raise e if $raise_exception
|
697
|
+
|
698
|
+
begin
|
699
|
+
# write error log
|
700
|
+
logid = nil
|
701
|
+
if @exception_log_table
|
702
|
+
logid = ErrorLogger.write_exception(e,{:ipaddress => req.ip, :url => req.url})
|
703
|
+
end
|
704
|
+
|
705
|
+
# show exception
|
706
|
+
|
707
|
+
if @error_template
|
708
|
+
values = {}
|
709
|
+
values[:logid] = logid if logid
|
710
|
+
values[:exception] = e.to_s
|
711
|
+
values[:backtrace] = e.backtrace
|
712
|
+
html = @template_engine.new.handleTemplate(@error_template.dup,values)
|
713
|
+
res = [500, {"Content-type"=>"text/html; charset=utf-8"}, [html]]
|
714
|
+
else
|
715
|
+
res = display_internal_server_error(e)
|
716
|
+
end
|
717
|
+
rescue Exception => e2
|
718
|
+
res = display_internal_server_error(e2)
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
res = res.to_a
|
723
|
+
p res if @opts[:response_debug]
|
724
|
+
res
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
end # module end
|
729
|
+
|
730
|
+
class StaticController < Egalite::Controller
|
731
|
+
def get
|
732
|
+
raise SecurityError unless env.opts[:static_root]
|
733
|
+
|
734
|
+
path = req.path_info
|
735
|
+
path.gsub!(/[^0-9a-zA-Z\(\)\. \/_\-]/,'')
|
736
|
+
if path.include?("..") or path =~ /^\//
|
737
|
+
return [403, {"Content-Type" => "text/plain"}, ["Forbidden\n"]]
|
738
|
+
end
|
739
|
+
path = File.join(env.opts[:static_root], path)
|
740
|
+
send_file(path)
|
741
|
+
end
|
742
|
+
end
|