sbsm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/COPYING +515 -0
  2. data/History.txt +5 -0
  3. data/Manifest.txt +49 -0
  4. data/README.txt +41 -0
  5. data/Rakefile +28 -0
  6. data/data/_flavored_uri.grammar +24 -0
  7. data/data/_uri.grammar +22 -0
  8. data/data/_zone_uri.grammar +24 -0
  9. data/data/flavored_uri.grammar +24 -0
  10. data/data/uri.grammar +22 -0
  11. data/data/zone_uri.grammar +24 -0
  12. data/install.rb +1098 -0
  13. data/lib/cgi/drbsession.rb +37 -0
  14. data/lib/sbsm/cgi.rb +79 -0
  15. data/lib/sbsm/drb.rb +19 -0
  16. data/lib/sbsm/drbserver.rb +162 -0
  17. data/lib/sbsm/exception.rb +28 -0
  18. data/lib/sbsm/flavored_uri_parser.rb +47 -0
  19. data/lib/sbsm/index.rb +66 -0
  20. data/lib/sbsm/lookandfeel.rb +176 -0
  21. data/lib/sbsm/lookandfeelfactory.rb +50 -0
  22. data/lib/sbsm/lookandfeelwrapper.rb +109 -0
  23. data/lib/sbsm/redefine_19_cookie.rb +4 -0
  24. data/lib/sbsm/redirector.rb +37 -0
  25. data/lib/sbsm/request.rb +162 -0
  26. data/lib/sbsm/session.rb +542 -0
  27. data/lib/sbsm/state.rb +301 -0
  28. data/lib/sbsm/time.rb +29 -0
  29. data/lib/sbsm/trans_handler.rb +119 -0
  30. data/lib/sbsm/turing.rb +25 -0
  31. data/lib/sbsm/uri_parser.rb +45 -0
  32. data/lib/sbsm/user.rb +47 -0
  33. data/lib/sbsm/validator.rb +256 -0
  34. data/lib/sbsm/viralstate.rb +47 -0
  35. data/lib/sbsm/zone_uri_parser.rb +48 -0
  36. data/test/data/dos_file.txt +2 -0
  37. data/test/data/lnf_file.txt +2 -0
  38. data/test/data/mac_file.txt +1 -0
  39. data/test/stub/cgi.rb +35 -0
  40. data/test/suite.rb +29 -0
  41. data/test/test_drbserver.rb +83 -0
  42. data/test/test_index.rb +90 -0
  43. data/test/test_lookandfeel.rb +230 -0
  44. data/test/test_session.rb +372 -0
  45. data/test/test_state.rb +176 -0
  46. data/test/test_trans_handler.rb +447 -0
  47. data/test/test_user.rb +44 -0
  48. data/test/test_validator.rb +126 -0
  49. data/usage-en.txt +112 -0
  50. 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