sbsm 1.0.0
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/COPYING +515 -0
- data/History.txt +5 -0
- data/Manifest.txt +49 -0
- data/README.txt +41 -0
- data/Rakefile +28 -0
- data/data/_flavored_uri.grammar +24 -0
- data/data/_uri.grammar +22 -0
- data/data/_zone_uri.grammar +24 -0
- data/data/flavored_uri.grammar +24 -0
- data/data/uri.grammar +22 -0
- data/data/zone_uri.grammar +24 -0
- data/install.rb +1098 -0
- data/lib/cgi/drbsession.rb +37 -0
- data/lib/sbsm/cgi.rb +79 -0
- data/lib/sbsm/drb.rb +19 -0
- data/lib/sbsm/drbserver.rb +162 -0
- data/lib/sbsm/exception.rb +28 -0
- data/lib/sbsm/flavored_uri_parser.rb +47 -0
- data/lib/sbsm/index.rb +66 -0
- data/lib/sbsm/lookandfeel.rb +176 -0
- data/lib/sbsm/lookandfeelfactory.rb +50 -0
- data/lib/sbsm/lookandfeelwrapper.rb +109 -0
- data/lib/sbsm/redefine_19_cookie.rb +4 -0
- data/lib/sbsm/redirector.rb +37 -0
- data/lib/sbsm/request.rb +162 -0
- data/lib/sbsm/session.rb +542 -0
- data/lib/sbsm/state.rb +301 -0
- data/lib/sbsm/time.rb +29 -0
- data/lib/sbsm/trans_handler.rb +119 -0
- data/lib/sbsm/turing.rb +25 -0
- data/lib/sbsm/uri_parser.rb +45 -0
- data/lib/sbsm/user.rb +47 -0
- data/lib/sbsm/validator.rb +256 -0
- data/lib/sbsm/viralstate.rb +47 -0
- data/lib/sbsm/zone_uri_parser.rb +48 -0
- data/test/data/dos_file.txt +2 -0
- data/test/data/lnf_file.txt +2 -0
- data/test/data/mac_file.txt +1 -0
- data/test/stub/cgi.rb +35 -0
- data/test/suite.rb +29 -0
- data/test/test_drbserver.rb +83 -0
- data/test/test_index.rb +90 -0
- data/test/test_lookandfeel.rb +230 -0
- data/test/test_session.rb +372 -0
- data/test/test_state.rb +176 -0
- data/test/test_trans_handler.rb +447 -0
- data/test/test_user.rb +44 -0
- data/test/test_validator.rb +126 -0
- data/usage-en.txt +112 -0
- metadata +142 -0
data/lib/sbsm/state.rb
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# State Based Session Management
|
4
|
+
# Copyright (C) 2004 Hannes Wyss
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License as published by the Free Software Foundation; either
|
9
|
+
# version 2.1 of the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This library is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this library; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
|
+
#
|
20
|
+
# ywesee - intellectual capital connected, Winterthurerstrasse 52, CH-8006 Z�rich, Switzerland
|
21
|
+
# hwyss@ywesee.com
|
22
|
+
#
|
23
|
+
# State -- sbsm -- 22.10.2002 -- hwyss@ywesee.com
|
24
|
+
|
25
|
+
module SBSM
|
26
|
+
class ProcessingError < RuntimeError
|
27
|
+
attr_reader :key, :value
|
28
|
+
def initialize(message, key, value)
|
29
|
+
super(message)
|
30
|
+
@key = key
|
31
|
+
@value = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
class Warning
|
35
|
+
attr_reader :key, :value, :message
|
36
|
+
def initialize(message, key, value)
|
37
|
+
@message = message
|
38
|
+
@key = key
|
39
|
+
@value = value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
class State
|
43
|
+
attr_reader :errors, :infos, :events, :previous, :warnings, :model
|
44
|
+
attr_accessor :next, :request_path, :redirected
|
45
|
+
DIRECT_EVENT = nil
|
46
|
+
ZONE = nil
|
47
|
+
ZONES = []
|
48
|
+
ZONE_EVENT = nil
|
49
|
+
EVENT_MAP = {}
|
50
|
+
GLOBAL_MAP = {}
|
51
|
+
REVERSE_MAP = {}
|
52
|
+
VIEW = nil
|
53
|
+
VOLATILE = false
|
54
|
+
def State::direct_event
|
55
|
+
self::DIRECT_EVENT
|
56
|
+
end
|
57
|
+
def State::zone
|
58
|
+
self::ZONE
|
59
|
+
end
|
60
|
+
def State::zones
|
61
|
+
self::ZONES
|
62
|
+
end
|
63
|
+
def initialize(session, model)
|
64
|
+
@session = session
|
65
|
+
@model = model
|
66
|
+
@events = self::class::GLOBAL_MAP.dup.update(self::class::EVENT_MAP.dup)
|
67
|
+
@default_view = self::class::VIEW
|
68
|
+
@errors = {}
|
69
|
+
@infos = []
|
70
|
+
@warnings = []
|
71
|
+
@viral_modules = []
|
72
|
+
touch()
|
73
|
+
end
|
74
|
+
def init
|
75
|
+
end
|
76
|
+
def add_warning(message, key, value)
|
77
|
+
if(key.is_a? String)
|
78
|
+
key = key.intern
|
79
|
+
end
|
80
|
+
warning = Warning.new(message, key, value)
|
81
|
+
@warnings.push(warning)
|
82
|
+
end
|
83
|
+
def back
|
84
|
+
@previous
|
85
|
+
end
|
86
|
+
def __checkout
|
87
|
+
return if(@checked_out)
|
88
|
+
@checked_out = true
|
89
|
+
@model = nil
|
90
|
+
if(@next.respond_to?(:unset_previous))
|
91
|
+
@next.unset_previous
|
92
|
+
end
|
93
|
+
@next = nil
|
94
|
+
if(@previous.respond_to?(:__checkout))
|
95
|
+
@previous.__checkout
|
96
|
+
end
|
97
|
+
@previous = nil
|
98
|
+
end
|
99
|
+
def create_error(msg, key, val)
|
100
|
+
ProcessingError.new(msg.to_s, key, val)
|
101
|
+
end
|
102
|
+
def default
|
103
|
+
self
|
104
|
+
end
|
105
|
+
def direct_event
|
106
|
+
self::class::DIRECT_EVENT
|
107
|
+
end
|
108
|
+
def error?
|
109
|
+
!@errors.empty?
|
110
|
+
end
|
111
|
+
def error(key)
|
112
|
+
@errors[key]
|
113
|
+
end
|
114
|
+
def error_check_and_store(key, value, mandatory=[])
|
115
|
+
if(value.is_a? RuntimeError)
|
116
|
+
@errors.store(key, value)
|
117
|
+
elsif(mandatory.include?(key) && mandatory_violation(value))
|
118
|
+
error = create_error('e_missing_' << key.to_s, key, value)
|
119
|
+
@errors.store(key, error)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
if RUBY_VERSION >= '1.9'
|
123
|
+
def extend(mod)
|
124
|
+
if(mod.constants.include?(:VIRAL))
|
125
|
+
@viral_modules.push(mod)
|
126
|
+
end
|
127
|
+
if(mod.constants.include?(:EVENT_MAP))
|
128
|
+
@events.update(mod::EVENT_MAP)
|
129
|
+
end
|
130
|
+
super
|
131
|
+
end
|
132
|
+
else
|
133
|
+
def extend(mod)
|
134
|
+
if(mod.constants.include?('VIRAL'))
|
135
|
+
@viral_modules.push(mod)
|
136
|
+
end
|
137
|
+
if(mod.constants.include?('EVENT_MAP'))
|
138
|
+
@events.update(mod::EVENT_MAP)
|
139
|
+
end
|
140
|
+
super
|
141
|
+
end
|
142
|
+
end
|
143
|
+
def http_headers
|
144
|
+
@http_headers || view.http_headers
|
145
|
+
end
|
146
|
+
def info?
|
147
|
+
!@infos.empty?
|
148
|
+
end
|
149
|
+
def info(key)
|
150
|
+
@infos[key]
|
151
|
+
end
|
152
|
+
def mandatory_violation(value)
|
153
|
+
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
154
|
+
end
|
155
|
+
def previous=(state)
|
156
|
+
if(@previous.nil? && state.respond_to?(:next=))
|
157
|
+
state.next = self
|
158
|
+
@previous = state
|
159
|
+
while state
|
160
|
+
if state.previous == self
|
161
|
+
state.unset_previous
|
162
|
+
end
|
163
|
+
state = state.previous
|
164
|
+
end
|
165
|
+
@previous
|
166
|
+
end
|
167
|
+
end
|
168
|
+
def sort
|
169
|
+
return self unless @model.respond_to?(:sort!)
|
170
|
+
get_sortby!
|
171
|
+
@model.sort! { |a, b| compare_entries(a, b) }
|
172
|
+
@model.reverse! if(@sort_reverse)
|
173
|
+
self
|
174
|
+
end
|
175
|
+
def touch
|
176
|
+
@mtime = Time.now
|
177
|
+
end
|
178
|
+
def to_html(context)
|
179
|
+
view.to_html(context)
|
180
|
+
end
|
181
|
+
def trigger(event)
|
182
|
+
if(@redirected)
|
183
|
+
@redirected = false
|
184
|
+
else
|
185
|
+
@errors = {}
|
186
|
+
@infos = []
|
187
|
+
@warnings = []
|
188
|
+
end
|
189
|
+
state = if(event && !event.to_s.empty? && self.respond_to?(event))
|
190
|
+
_trigger(event)
|
191
|
+
elsif(klass = @events[event])
|
192
|
+
klass.new(@session, @model)
|
193
|
+
end
|
194
|
+
state ||= self.default
|
195
|
+
if(state.respond_to?(:previous=))
|
196
|
+
state.previous = self
|
197
|
+
end
|
198
|
+
state
|
199
|
+
end
|
200
|
+
def _trigger(event)
|
201
|
+
self.send(event)
|
202
|
+
end
|
203
|
+
def unset_previous
|
204
|
+
if @previous.respond_to?(:next=)
|
205
|
+
@previous.next = nil
|
206
|
+
end
|
207
|
+
@previous = nil
|
208
|
+
end
|
209
|
+
def warning(key)
|
210
|
+
@warnings.select { |warning| warning.key == key }.first
|
211
|
+
end
|
212
|
+
def warning?
|
213
|
+
!@warnings.empty?
|
214
|
+
end
|
215
|
+
def user_input(keys=[], mandatory=[])
|
216
|
+
keys = [keys] unless keys.is_a?(Array)
|
217
|
+
mandatory = [mandatory] unless mandatory.is_a?(Array)
|
218
|
+
if(hash = @session.user_input(*keys))
|
219
|
+
if keys.size == 1
|
220
|
+
unless(error_check_and_store(keys.first, hash, mandatory))
|
221
|
+
hash
|
222
|
+
end
|
223
|
+
else
|
224
|
+
hash.each { |key, value|
|
225
|
+
if(error_check_and_store(key, value, mandatory))
|
226
|
+
hash.delete(key)
|
227
|
+
end
|
228
|
+
}
|
229
|
+
hash
|
230
|
+
end
|
231
|
+
else
|
232
|
+
{}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
def view
|
236
|
+
klass = @default_view
|
237
|
+
if(klass.is_a?(Hash))
|
238
|
+
klass = klass.fetch(@session.user.class) {
|
239
|
+
klass[:default]
|
240
|
+
}
|
241
|
+
end
|
242
|
+
model = @filter ? @filter.call(@model) : @model
|
243
|
+
view = klass.new(model, @session)
|
244
|
+
@http_headers = view.http_headers
|
245
|
+
view
|
246
|
+
end
|
247
|
+
def volatile?
|
248
|
+
self::class::VOLATILE
|
249
|
+
end
|
250
|
+
def zone
|
251
|
+
self::class::ZONE
|
252
|
+
end
|
253
|
+
def zones
|
254
|
+
self::class::ZONES
|
255
|
+
end
|
256
|
+
def zone_navigation
|
257
|
+
[]
|
258
|
+
end
|
259
|
+
def <=>(other)
|
260
|
+
@mtime <=> other.mtime
|
261
|
+
end
|
262
|
+
protected
|
263
|
+
def compare_entries(a, b)
|
264
|
+
@sortby.each { |sortby|
|
265
|
+
aval, bval = nil
|
266
|
+
begin
|
267
|
+
aval = a.send(sortby)
|
268
|
+
bval = b.send(sortby)
|
269
|
+
rescue
|
270
|
+
warn "could not sort by #{sortby}"
|
271
|
+
next
|
272
|
+
end
|
273
|
+
res = if (aval.nil? && bval.nil?)
|
274
|
+
0
|
275
|
+
elsif (aval.nil?)
|
276
|
+
1
|
277
|
+
elsif (bval.nil?)
|
278
|
+
-1
|
279
|
+
else
|
280
|
+
aval <=> bval
|
281
|
+
end
|
282
|
+
return res if(res.nonzero?)
|
283
|
+
}
|
284
|
+
0
|
285
|
+
end
|
286
|
+
def get_sortby!
|
287
|
+
@sortby ||= []
|
288
|
+
if(sortvalue = @session.user_input(:sortvalue))
|
289
|
+
sortvalue = sortvalue.to_sym
|
290
|
+
if(@sortby.first == sortvalue)
|
291
|
+
@sort_reverse = !@sort_reverse
|
292
|
+
else
|
293
|
+
@sort_reverse = self.class::REVERSE_MAP[sortvalue]
|
294
|
+
end
|
295
|
+
@sortby.delete(sortvalue)
|
296
|
+
@sortby.unshift(sortvalue)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
attr_reader :mtime
|
300
|
+
end
|
301
|
+
end
|
data/lib/sbsm/time.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# State Based Session Management
|
4
|
+
# Copyright (C) 2004 Hannes Wyss
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License as published by the Free Software Foundation; either
|
9
|
+
# version 2.1 of the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This library is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this library; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
|
+
#
|
20
|
+
# ywesee - intellectual capital connected, Winterthurerstrasse 52, CH-8006 Z�rich, Switzerland
|
21
|
+
# hwyss@ywesee.com
|
22
|
+
#
|
23
|
+
# Time -- sbsm -- 20.11.2002 -- hwyss@ywesee.com
|
24
|
+
|
25
|
+
class Time
|
26
|
+
def rfc1123
|
27
|
+
gmtime.strftime('%a, %d %b %Y %H:%M:%S %Z')
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# TransHandler -- sbsm -- 23.09.2004 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
$USING_STRSCAN = true
|
5
|
+
require 'rockit/rockit'
|
6
|
+
require 'cgi'
|
7
|
+
require 'singleton'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
module SBSM
|
11
|
+
class AbstractTransHandler
|
12
|
+
attr_reader :parser_name
|
13
|
+
CONFIG_PATH = '../etc/trans_handler.yml'
|
14
|
+
@@empty_check ||= nil
|
15
|
+
@@lang_check ||= nil
|
16
|
+
@@uri_parser ||= nil
|
17
|
+
HANDLER_URI = '/index.rbx'
|
18
|
+
def initialize(name)
|
19
|
+
@parser_name = name
|
20
|
+
@parser_method = "_#{name}_parser"
|
21
|
+
@grammar_path = File.expand_path("../../data/#{name}.grammar",
|
22
|
+
File.dirname(__FILE__))
|
23
|
+
@parser_path = File.expand_path("#{name}_parser.rb",
|
24
|
+
File.dirname(__FILE__))
|
25
|
+
end
|
26
|
+
def config(request)
|
27
|
+
config = Hash.new { {} }
|
28
|
+
begin
|
29
|
+
path = File.expand_path(CONFIG_PATH, request.server.document_root)
|
30
|
+
path.untaint
|
31
|
+
config.update(YAML.load(File.read(path)))
|
32
|
+
config
|
33
|
+
rescue StandardError => err
|
34
|
+
fmt = 'Unable to load url configuration: %s'
|
35
|
+
request.server.log_warn(fmt, err.message)
|
36
|
+
fmt = 'Hint: store configuration in a YAML-File at DOCUMENT_ROOT/%s'
|
37
|
+
request.server.log_notice(fmt, CONFIG_PATH)
|
38
|
+
config
|
39
|
+
end
|
40
|
+
end
|
41
|
+
def handle_shortcut(request, config)
|
42
|
+
if(notes = config['shortcut'][request.uri])
|
43
|
+
notes.each { |key, val|
|
44
|
+
request.notes.add(key, val)
|
45
|
+
}
|
46
|
+
request.uri = HANDLER_URI
|
47
|
+
end
|
48
|
+
end
|
49
|
+
def parse_uri(request)
|
50
|
+
@uri_parser ||= self.uri_parser
|
51
|
+
ast = @uri_parser.parse(request.uri)
|
52
|
+
values = request.notes
|
53
|
+
ast.children_names.each { |name|
|
54
|
+
case name
|
55
|
+
when'language', 'flavor', 'event', 'zone'
|
56
|
+
values.add(name, ast.send(name).value)
|
57
|
+
when 'variables'
|
58
|
+
ast.variables.each { |pair|
|
59
|
+
key = pair.key.value
|
60
|
+
val = if(pair.children_names.include?('value'))
|
61
|
+
CGI.unescape(pair.value.value.to_s)
|
62
|
+
else
|
63
|
+
''
|
64
|
+
end
|
65
|
+
values.add(key, val)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
}
|
69
|
+
end
|
70
|
+
def translate_uri(request)
|
71
|
+
@@empty_check ||= Regexp.new('^/?$')
|
72
|
+
@@lang_check ||= Regexp.new('^/[a-z]{2}(/|$)')
|
73
|
+
config = config(request)
|
74
|
+
handle_shortcut(request, config)
|
75
|
+
uri = request.uri
|
76
|
+
case uri
|
77
|
+
when @@empty_check
|
78
|
+
request.uri = config['redirect']['/'] || HANDLER_URI
|
79
|
+
when @@lang_check
|
80
|
+
self.parse_uri(request)
|
81
|
+
request.uri = HANDLER_URI
|
82
|
+
end
|
83
|
+
Apache::DECLINED
|
84
|
+
end
|
85
|
+
def uri_parser(grammar_path=@grammar_path, parser_path=@parser_path)
|
86
|
+
if(File.exist?(grammar_path))
|
87
|
+
oldpath = File.expand_path("_" << File.basename(grammar_path),
|
88
|
+
File.dirname(grammar_path))
|
89
|
+
src = File.read(grammar_path)
|
90
|
+
unless(File.exists?(oldpath) && File.read(oldpath)==src)
|
91
|
+
File.delete(oldpath) if File.exists?(oldpath)
|
92
|
+
Parse.generate_parser_from_file_to_file(grammar_path,
|
93
|
+
parser_path, @parser_method, 'SBSM')
|
94
|
+
File.open(oldpath, 'w') { |f| f << src }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
require parser_path
|
98
|
+
SBSM.send(@parser_method)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
class TransHandler < AbstractTransHandler
|
102
|
+
include Singleton
|
103
|
+
def initialize
|
104
|
+
super('uri')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
class FlavoredTransHandler < AbstractTransHandler
|
108
|
+
include Singleton
|
109
|
+
def initialize
|
110
|
+
super('flavored_uri')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
class ZoneTransHandler < AbstractTransHandler
|
114
|
+
include Singleton
|
115
|
+
def initialize
|
116
|
+
super('zone_uri')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|