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.
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
@@ -0,0 +1,176 @@
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
+ # Lookandfeel -- sbsm -- hwyss@ywesee.com
24
+
25
+ require 'sbsm/time'
26
+ require 'cgi'
27
+
28
+ module SBSM
29
+ class Lookandfeel
30
+ attr_reader :language, :flavor, :session
31
+ DICTIONARIES = {}
32
+ DISABLED = []
33
+ ENABLED = []
34
+ HTML_ATTRIBUTES = {}
35
+ RESOURCES = {}
36
+ RESOURCE_BASE = "resources"
37
+ TXT_RESOURCES = "/resources"
38
+ class << self
39
+ def txt_file(filename)
40
+ Proc.new {
41
+ path = File.expand_path(filename, self::TXT_RESOURCES)
42
+ File.read(path).strip.gsub(/(\r\n)|(\n)|(\r)/, '<br>')
43
+ }
44
+ end
45
+ end
46
+ def initialize(session)
47
+ @session = session
48
+ @flavor = @session.flavor
49
+ @language = @session.language
50
+ set_dictionary(@language)
51
+ end
52
+ def attributes(key)
53
+ self::class::HTML_ATTRIBUTES.fetch(key.to_sym, {}).dup rescue {}
54
+ end
55
+ def base_url
56
+ [@session.http_protocol + ':/', @session.server_name, @language, @flavor].compact.join("/")
57
+ end
58
+ def direct_event
59
+ @session.direct_event
60
+ end
61
+ def disabled?(event)
62
+ self::class::DISABLED.include?(event)
63
+ end
64
+ def enabled?(event, default=true)
65
+ default || self::class::ENABLED.include?(event)
66
+ end
67
+ def event_url(event=direct_event, args={}, anchor=nil)
68
+ _event_url(event, args, anchor=nil) { |args|
69
+ unless(@session.is_crawler? || args.include?('state_id'))
70
+ args.unshift(:state_id, @session.state.object_id)
71
+ end
72
+ }
73
+ end
74
+ def _event_url(event=direct_event, args={}, anchor=nil, &block)
75
+ args = args.collect { |*pair| pair }.flatten
76
+ args = args.collect { |value| CGI.escape(value.to_s) }
77
+ if(block_given?)
78
+ yield(args)
79
+ end
80
+ url = [base_url(), event, args].compact.join('/')
81
+ if(anchor)
82
+ url << "#" << anchor.to_s
83
+ end
84
+ url
85
+ end
86
+ def languages
87
+ @languages ||= self::class::DICTIONARIES.keys.sort
88
+ end
89
+ def language_url(language)
90
+ [@session.http_protocol + ':/', @session.server_name, language, @flavor].compact.join("/")
91
+ end
92
+ def lookup(key, *args, &block)
93
+ _lookup(key, *args) || (block.call if block)
94
+ end
95
+ def _lookup(key, *args)
96
+ key = key.intern if key.is_a? String
97
+ if(args.size > 0)
98
+ result = ""
99
+ args.each_with_index { |text, index|
100
+ result << (lookup(key.to_s + index.to_s) || ' ')
101
+ result << text.to_s
102
+ }
103
+ tail = lookup(key.to_s + args.size.to_s)
104
+ result << tail if(tail)
105
+ else
106
+ result = @dictionary[key] if @dictionary
107
+ if(result.is_a? Proc)
108
+ result.call
109
+ elsif(result.nil?)
110
+ def_lan = @session.default_language
111
+ if(dict = self::class::DICTIONARIES[def_lan])
112
+ dict[key]
113
+ end
114
+ else
115
+ result
116
+ end
117
+ end
118
+ end
119
+ def format_date(date)
120
+ date.strftime(lookup(:date_format))
121
+ end
122
+ def format_price(price, currency=nil)
123
+ if(price.to_i > 0)
124
+ [currency, sprintf('%.2f', price.to_f/100.0)].compact.join(' ')
125
+ end
126
+ end
127
+ def format_time(time)
128
+ time.strftime(lookup(:time_format))
129
+ end
130
+ def navigation(filter=false)
131
+ @session.navigation
132
+ end
133
+ def resource(rname, rstr=nil)
134
+ collect_resource([ self::class::RESOURCE_BASE, @session.flavor ],
135
+ rname, rstr)
136
+ end
137
+ def resource_external(rname)
138
+ self::class::RESOURCES[rname]
139
+ end
140
+ def resource_global(rname, rstr=nil)
141
+ collect_resource(self::class::RESOURCE_BASE, rname, rstr)
142
+ end
143
+ def resource_localized(rname, rstr=nil, lang=@language)
144
+ result = resource([rname, lang].join('_').intern, rstr)
145
+ if(result.nil? && (lang != @session.default_language))
146
+ result = resource_localized(rname, rstr, @session.default_language)
147
+ end
148
+ result
149
+ end
150
+ def zone_navigation(filter=false)
151
+ @session.zone_navigation
152
+ end
153
+ def zones(filter=false)
154
+ @session.zones
155
+ end
156
+ private
157
+ def collect_resource(base, rname, rstr=nil)
158
+ varpart = self::class::RESOURCES[rname]
159
+ if(varpart.is_a?(Array))
160
+ varpart.collect { |part|
161
+ _collect_resource(base, part, rstr)
162
+ }
163
+ elsif(!varpart.nil?)
164
+ _collect_resource(base, varpart, rstr)
165
+ end
166
+ end
167
+ def _collect_resource(base, part, rstr)
168
+ [ @session.http_protocol + ':/',
169
+ @session.server_name,
170
+ base, part, rstr].flatten.compact.join('/')
171
+ end
172
+ def set_dictionary(language)
173
+ @dictionary = self::class::DICTIONARIES[language] || {}
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,50 @@
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
+ # LookandfeelFactory -- sbsm -- hwyss@ywesee.com
24
+
25
+ require 'sbsm/lookandfeel'
26
+
27
+ module SBSM
28
+ class LookandfeelFactory
29
+ WRAPPERS = {}
30
+ BASE = Lookandfeel
31
+ class << self
32
+ def create(session)
33
+ lnf = self::BASE.new(session)
34
+ if(wrappers = self::WRAPPERS[session.flavor])
35
+ lnf = wrappers.inject(lnf) { |lnf, klass|
36
+ klass.new(lnf)
37
+ }
38
+ end
39
+ lnf
40
+ rescue StandardError => e
41
+ puts e.class
42
+ puts e.message
43
+ puts e.backtrace
44
+ end
45
+ def include?(str)
46
+ self::WRAPPERS.include?(str)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,109 @@
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
+ # LookandfeelWrapper -- sbsm -- hwyss@ywesee.com
24
+
25
+ require "sbsm/lookandfeel"
26
+
27
+ module SBSM
28
+ class LookandfeelWrapper < Lookandfeel
29
+ def initialize(component)
30
+ @component = component
31
+ super(@component.session)
32
+ end
33
+ def method_missing(symbol, *args, &block)
34
+ @component.send(symbol, *args, &block)
35
+ end
36
+ def attributes(key)
37
+ self::class::HTML_ATTRIBUTES.fetch(key.to_sym) {
38
+ @component.attributes(key)
39
+ }
40
+ end
41
+ def disabled?(event)
42
+ self::class::DISABLED.include?(event) \
43
+ || @component.disabled?(event)
44
+ end
45
+ def enabled?(event, default=false)
46
+ self::class::ENABLED.include?(event) \
47
+ || @component.enabled?(event, default)
48
+ end
49
+ def languages
50
+ unless(@languages)
51
+ super
52
+ if(@languages.empty?)
53
+ @languages = @component.languages
54
+ end
55
+ end
56
+ @languages
57
+ end
58
+ def lookup(key, *args, &block)
59
+ _lookup(key, *args) || @component.lookup(key, *args, &block)
60
+ end
61
+ def navigation(filter=true)
62
+ nav = @component.navigation(false)
63
+ if(filter)
64
+ nav.select { |item|
65
+ key = (item.is_a? Symbol) ? item : item.direct_event
66
+ enabled?(key)
67
+ }
68
+ else
69
+ nav
70
+ end
71
+ end
72
+ def resource(rname, rstr=nil)
73
+ if(self::class::RESOURCES.include?(rname))
74
+ super
75
+ else
76
+ @component.resource(rname, rstr)
77
+ end
78
+ end
79
+ def resource_global(rname, rstr=nil)
80
+ if(self::class::RESOURCES.include?(rname))
81
+ super
82
+ else
83
+ @component.resource_global(rname, rstr)
84
+ end
85
+ end
86
+ def zone_navigation(filter=true)
87
+ nav = @component.zone_navigation(false)
88
+ if(filter)
89
+ nav.select { |item|
90
+ key = (item.is_a? Symbol) ? item : item.direct_event
91
+ enabled?(key)
92
+ }
93
+ else
94
+ nav
95
+ end
96
+ end
97
+ def zones(filter=true)
98
+ nav = @component.zones(false)
99
+ if(filter)
100
+ nav.select { |item|
101
+ key = (item.is_a? Symbol) ? item : item.direct_event
102
+ enabled?(key)
103
+ }
104
+ else
105
+ nav
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,4 @@
1
+ #class CGI
2
+ # remove_const 'Cookie'
3
+ #end
4
+ #load '/usr/lib/ruby19/1.9.1/cgi/cookie.rb'
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # Redirector -- sbsm -- 02.11.2006 -- hwyss@ywesee.com
3
+
4
+ module SBSM
5
+ module Redirector
6
+ def http_headers
7
+ if(redirect?)
8
+ @redirected = @state.redirected = true
9
+ event, *args = @state.direct_event
10
+ if(args.first.is_a? Hash)
11
+ args = args.first
12
+ end
13
+ {
14
+ "Location" => lookandfeel._event_url(event, args || {}),
15
+ }
16
+ else
17
+ @redirected = @state.redirected = false
18
+ super
19
+ end
20
+ end
21
+ def redirect?
22
+ direct = @state.direct_event
23
+ if(direct.is_a?(Array))
24
+ direct = direct.first
25
+ end
26
+ direct && (@request_method != 'GET' \
27
+ || ![direct, :sort].include?(event))
28
+ end
29
+ def to_html
30
+ if(redirect?)
31
+ ''
32
+ else
33
+ super
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,162 @@
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
+ # Request -- sbsm -- hwyss@ywesee.com
24
+
25
+ require 'sbsm/cgi'
26
+ require 'sbsm/drb'
27
+ require 'sbsm/redefine_19_cookie'
28
+ require 'cgi/session'
29
+ require 'cgi/drbsession'
30
+ require 'delegate'
31
+
32
+ module SBSM
33
+ class Request < SimpleDelegator
34
+ include DRbUndumped
35
+ CRAWLER_PATTERN = /archiver|slurp|bot|crawler|jeeves|spider|\.{6}/i
36
+ attr_reader :cgi
37
+ def initialize(drb_uri, html_version = "html4", cgiclass = CGI)
38
+ @cgi = cgiclass.new(html_version)
39
+ @drb_uri = drb_uri
40
+ @thread = nil
41
+ @request = Apache.request
42
+ super(@cgi)
43
+ end
44
+ def cookies
45
+ @cgi.cookies
46
+ end
47
+ def is_crawler?
48
+ !!CRAWLER_PATTERN.match(@cgi.user_agent)
49
+ end
50
+ def passthru(path, disposition='attachment')
51
+ @passthru = path
52
+ @disposition = disposition
53
+ ''
54
+ end
55
+ def process
56
+ begin
57
+ @cgi.params.store('default_flavor', ENV['DEFAULT_FLAVOR'])
58
+ @request.notes.each { |key, val|
59
+ @cgi.params.store(key, val)
60
+ }
61
+ drb_process()
62
+ #drb_request()
63
+ #drb_response()
64
+ rescue StandardError => e
65
+ handle_exception(e)
66
+ ensure
67
+ @session.close if @session.respond_to?(:close)
68
+ end
69
+ end
70
+ def remote_host(lookup_type=Apache::REMOTE_NOLOOKUP)
71
+ @request.remote_host(lookup_type)
72
+ end
73
+ def unparsed_uri
74
+ @request.unparsed_uri
75
+ end
76
+ private
77
+ def drb_process
78
+ args = {
79
+ 'database_manager' => CGI::Session::DRbSession,
80
+ 'drbsession_uri' => @drb_uri,
81
+ 'session_path' => '/',
82
+ }
83
+ if(is_crawler?)
84
+ sleep 0.5
85
+ sid = [ENV['DEFAULT_FLAVOR'], @cgi.params['language'], @cgi.user_agent].join('-')
86
+ args.store('session_id', sid)
87
+ end
88
+ @session = CGI::Session.new(@cgi, args)
89
+ @proxy = @session[:proxy]
90
+ res = @proxy.drb_process(self)
91
+ cookie_input = @proxy.cookie_input
92
+ # view.to_html can call passthru instead of sending data
93
+ if(@passthru)
94
+ unless(cookie_input.empty?)
95
+ cookie = generate_cookie(cookie_input)
96
+ @request.headers_out.add('Set-Cookie', cookie.to_s)
97
+ end
98
+ # the variable @passthru is set by a trusted source
99
+ basename = File.basename(@passthru)
100
+ fullpath = File.expand_path(@passthru,
101
+ @request.server.document_root)
102
+ fullpath.untaint
103
+ subreq = @request.lookup_file(fullpath)
104
+ @request.content_type = subreq.content_type
105
+ @request.headers_out.add('Content-Disposition',
106
+ "#@disposition; filename=#{basename}")
107
+ @request.headers_out.add('Content-Length',
108
+ File.size(fullpath).to_s)
109
+ begin
110
+ File.open(fullpath) { |fd| @request.send_fd(fd) }
111
+ rescue Errno::ENOENT, IOError => err
112
+ @request.log_reason(err.message, @passthru)
113
+ return Apache::NOT_FOUND
114
+ end
115
+ else
116
+ begin
117
+ headers = @proxy.http_headers
118
+ unless(cookie_input.empty?)
119
+ cookie = generate_cookie(cookie_input)
120
+ headers.store('Set-Cookie', [cookie])
121
+ end
122
+ @cgi.out(headers) {
123
+ (@cgi.params.has_key?("pretty")) ? CGI.pretty( res ) : res
124
+ }
125
+ rescue StandardError => e
126
+ handle_exception(e)
127
+ end
128
+ end
129
+ end
130
+ def generate_cookie(cookie_input)
131
+ cookie_pairs = cookie_input.collect { |pair|
132
+ pair.join('=')
133
+ }.join(';')
134
+ cookie_hash = {
135
+ "name" => @proxy.cookie_name || 'sbsm-persistent-cookie',
136
+ "value" => cookie_pairs,
137
+ "path" => "/",
138
+ "expires" => (Time.now + (60 * 60 * 24 * 365 * 10)),
139
+ }
140
+ CGI::Cookie.new(cookie_hash).to_s
141
+ end
142
+ def handle_exception(e)
143
+ if defined?(Apache)
144
+ msg = [
145
+ [Time.now, self.object_id].join(' - '),
146
+ e.class,
147
+ e.message,
148
+ ].join(" - ")
149
+ uri = unparsed_uri
150
+ @request.log_reason(msg, uri)
151
+ e.backtrace.each { |line|
152
+ @request.log_reason(line, uri)
153
+ }
154
+ end
155
+ hdrs = {
156
+ 'Status' => '302 Moved',
157
+ 'Location' => '/resources/errors/appdown.html',
158
+ }
159
+ @cgi.header(hdrs)
160
+ end
161
+ end
162
+ end