bijou 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/ChangeLog.txt +4 -0
  2. data/LICENSE.txt +58 -0
  3. data/README.txt +48 -0
  4. data/Rakefile +105 -0
  5. data/doc/INSTALL.rdoc +260 -0
  6. data/doc/README.rdoc +314 -0
  7. data/doc/releases/bijou-0.1.0.rdoc +60 -0
  8. data/examples/birthday/birthday.rb +34 -0
  9. data/examples/holiday/holiday.rb +61 -0
  10. data/examples/holiday/letterhead.txt +4 -0
  11. data/examples/holiday/signature.txt +9 -0
  12. data/examples/phishing/letter.txt +29 -0
  13. data/examples/phishing/letterhead.txt +4 -0
  14. data/examples/phishing/phishing.rb +21 -0
  15. data/examples/phishing/signature.txt +9 -0
  16. data/examples/profile/profile.rb +46 -0
  17. data/lib/bijou.rb +15 -0
  18. data/lib/bijou/backend.rb +542 -0
  19. data/lib/bijou/cgi/adapter.rb +201 -0
  20. data/lib/bijou/cgi/handler.rb +5 -0
  21. data/lib/bijou/cgi/request.rb +37 -0
  22. data/lib/bijou/common.rb +12 -0
  23. data/lib/bijou/component.rb +108 -0
  24. data/lib/bijou/config.rb +60 -0
  25. data/lib/bijou/console/adapter.rb +167 -0
  26. data/lib/bijou/console/handler.rb +4 -0
  27. data/lib/bijou/console/request.rb +26 -0
  28. data/lib/bijou/context.rb +431 -0
  29. data/lib/bijou/diagnostics.rb +87 -0
  30. data/lib/bijou/errorformatter.rb +322 -0
  31. data/lib/bijou/exception.rb +39 -0
  32. data/lib/bijou/filters.rb +107 -0
  33. data/lib/bijou/httprequest.rb +108 -0
  34. data/lib/bijou/httpresponse.rb +268 -0
  35. data/lib/bijou/lexer.rb +513 -0
  36. data/lib/bijou/minicgi.rb +159 -0
  37. data/lib/bijou/parser.rb +1026 -0
  38. data/lib/bijou/processor.rb +404 -0
  39. data/lib/bijou/prstringio.rb +400 -0
  40. data/lib/bijou/webrick/adapter.rb +174 -0
  41. data/lib/bijou/webrick/handler.rb +32 -0
  42. data/lib/bijou/webrick/request.rb +45 -0
  43. data/script/cgi.rb +25 -0
  44. data/script/console.rb +7 -0
  45. data/script/server.rb +7 -0
  46. data/test/t1.cfg +5 -0
  47. data/test/tc_config.rb +26 -0
  48. data/test/tc_filter.rb +25 -0
  49. data/test/tc_lexer.rb +120 -0
  50. data/test/tc_response.rb +103 -0
  51. data/test/tc_ruby.rb +62 -0
  52. data/test/tc_stack.rb +50 -0
  53. metadata +121 -0
@@ -0,0 +1,107 @@
1
+ #
2
+ # Copyright (c) 2007-2008 Todd Lucas. All rights reserved.
3
+ #
4
+ # filters.rb - Contains built-in filter definitions
5
+ #
6
+ require 'cgi'
7
+ require 'bijou/common'
8
+
9
+ module Bijou
10
+ #
11
+ # A filter may be used to modify text from an output tag prior to rendering.
12
+ # For example, the output tag:
13
+ #
14
+ # <%= user.first_name | h %>
15
+ #
16
+ # Will pipe the text returned from the first_name method through the +h+
17
+ # filter. The <tt>h</tt> filter represents Bijou::EncodeHTML and serves to
18
+ # escape the text for HTML presentation.
19
+ #
20
+ # Note that filters may be chained by listing them one after the other
21
+ # following the pipe separator. For example, the following output tag and
22
+ # filter sequence:
23
+ #
24
+ # <%= user.first_name | th %>
25
+ #
26
+ # first invokes the +t+ filter, which trims text using EncodeTrim, and then
27
+ # pipes the result of that to the +h+ filter, for final HTML presentation.
28
+ #
29
+ class Filter
30
+ # Used during code generation to produce the code that will be used to
31
+ # call the apply method.
32
+ def render(input)
33
+ "Bijou::Filter.apply(#{input})"
34
+ end
35
+
36
+ # The base filter is a no-op, returning the original string, unaltered.
37
+ def self.apply(input)
38
+ input # no-op
39
+ end
40
+ end
41
+
42
+ # The +h+ filter escapes the input for HTML presentation.
43
+ class EncodeHTML < Filter
44
+ def render(input)
45
+ "Bijou::EncodeHTML.apply(#{input})"
46
+ end
47
+
48
+ def self.apply(input)
49
+ ::CGI::escapeHTML(input)
50
+ end
51
+ end
52
+
53
+ # The +u+ filter escapes the input, replacing characters that are unsafe for
54
+ # passing as a URL parameter, with their escaped equivalents.
55
+ class EncodeURL < Filter
56
+ def render(input)
57
+ "Bijou::EncodeURL.apply(#{input})"
58
+ end
59
+
60
+ def self.apply(input)
61
+ ::CGI::escape(input)
62
+ end
63
+ end
64
+
65
+ # The +a+ filter escapes text intended for HTML tag attribute values.
66
+ class EncodeAttributeValue < Filter
67
+ def render(input)
68
+ "Bijou::EncodeAttribute.apply(#{input})"
69
+ end
70
+
71
+ def self.apply(input)
72
+ input.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/\'/n, '&apos;').gsub(/</n, '&lt;')
73
+ end
74
+ end
75
+
76
+ # Removes
77
+ class EncodeTrim < Filter
78
+ def render(input)
79
+ "Bijou::EncodeTrim.apply(#{input})"
80
+ end
81
+
82
+ def self.apply(input)
83
+ #s = input.dup
84
+ #s.sub!(/^\s*/, "")
85
+ #s.sub!(/\s*$/, "")
86
+ #s
87
+ input.strip
88
+ end
89
+ end
90
+
91
+ #--
92
+ # TODO: Move this into an installable filter.
93
+ #++
94
+ #
95
+ # This filter doesn't really do anything. It's a basic example of a filter
96
+ # that might take Wiki-ized text and display as HTML.
97
+ #
98
+ class EncodeWiki < Filter
99
+ def render(input)
100
+ "Bijou::EncodeWiki.apply(#{input})"
101
+ end
102
+
103
+ def self.apply(input)
104
+ input.gsub /\n/, "<br/>\n";
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,108 @@
1
+ #
2
+ # Copyright (c) 2007-2008 Todd Lucas. All rights reserved.
3
+ #
4
+ # httprequest.rb - The base HttpRequest class
5
+ #
6
+ require 'bijou/common'
7
+
8
+ module Bijou
9
+ #
10
+ # This class represents an incoming HTTP request, presenting a common
11
+ # interface to the Bijou component regardless of the server environment.
12
+ #
13
+ class HttpRequest
14
+ def initialize
15
+ @params = {}
16
+ @query_string = {}
17
+ @form = {}
18
+ @server_variables = {}
19
+ @cookies = {}
20
+ @http_method = ''
21
+ end
22
+
23
+ # Returns a hash containing items passed via the HTTP query string.
24
+ # Synonym for #get.
25
+ attr_reader :query_string
26
+
27
+ # Returns a hash containing form data passed via the HTTP POST action.
28
+ # Synonym for #post.
29
+ attr_reader :form
30
+
31
+ # Returns a hash containing the combined values of the query string,
32
+ # post data, and cookies.
33
+ attr_reader :params
34
+
35
+ #
36
+ # A hash containing standard CGI environment variables.
37
+ #
38
+ # AUTH_TYPE HTTP_HOST REMOTE_IDENT
39
+ # CONTENT_LENGTH HTTP_NEGOTIATE REMOTE_USER
40
+ # CONTENT_TYPE HTTP_PRAGMA REQUEST_METHOD
41
+ # GATEWAY_INTERFACE HTTP_REFERER SCRIPT_NAME
42
+ # HTTP_ACCEPT HTTP_USER_AGENT SERVER_NAME
43
+ # HTTP_ACCEPT_CHARSET PATH_INFO SERVER_PORT
44
+ # HTTP_ACCEPT_ENCODING PATH_TRANSLATED SERVER_PROTOCOL
45
+ # HTTP_ACCEPT_LANGUAGE QUERY_STRING SERVER_SOFTWARE
46
+ # HTTP_CACHE_CONTROL REMOTE_ADDR
47
+ # HTTP_FROM REMOTE_HOST
48
+ #
49
+ # As well as additions that aren't present in cgi.rb.
50
+ #
51
+ # REQUEST_URI
52
+ #
53
+ attr_reader :server_variables
54
+
55
+ # A hash containing cookies sent by the client.
56
+ attr_reader :cookies
57
+
58
+ # A string representing the HTTP request method (e.g., GET, POST, or HEAD).
59
+ attr_reader :http_method
60
+
61
+ # Returns a hash containing items passed via the HTTP query string.
62
+ # Synonym for #query_string.
63
+ def get
64
+ @query_string
65
+ end
66
+
67
+ # Returns a hash containing form data passed via the HTTP POST action.
68
+ # Synonym for #form.
69
+ def post
70
+ @form
71
+ end
72
+
73
+ # TODO: These should be moved to CGI::Request
74
+ def referrer
75
+ @server_variables['HTTP_REFERER']
76
+ end
77
+
78
+ def referer
79
+ @server_variables['HTTP_REFERER']
80
+ end
81
+
82
+ #--
83
+ # ASP.NET has:
84
+ # path, path_info, url
85
+ # which don't directly correspond to Apache CGI variables.
86
+ # def url
87
+ # @server_variables['REQUEST_URI']
88
+ # end
89
+ #++
90
+
91
+ def virtual_path
92
+ # @server_variables['REQUEST_URI']
93
+ @server_variables['PATH_INFO']
94
+ end
95
+
96
+ def physical_path
97
+ @server_variables['PATH_TRANSLATED']
98
+ end
99
+
100
+ def request_method
101
+ @server_variables['REQUEST_METHOD']
102
+ end
103
+
104
+ def user_agent
105
+ @server_variables['HTTP_USER_AGENT']
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,268 @@
1
+ #
2
+ # Copyright (c) 2007-2008 Todd Lucas. All rights reserved.
3
+ #
4
+ # httpresponse.rb - The base HttpResponse class
5
+ #
6
+ require 'cgi' # for encoding functions
7
+ require 'bijou/common'
8
+
9
+ module Bijou
10
+ class HttpResponse
11
+ ContentType = 'Content-Type'
12
+ SetCookie = 'Set-Cookie'
13
+ Location = 'Location'
14
+ DefaultContentType = 'text/html'
15
+ DefaultCharset = 'iso-8859-1'
16
+
17
+ CR = "\015"
18
+ LF = "\012"
19
+ EOL = CR + LF
20
+
21
+ attr_accessor :buffer_output
22
+
23
+ # Some environments generate their own headers. Headers should be
24
+ # suppressed in such cases. Note, however that response header attributes,
25
+ # such as content_type, will have no effect.
26
+ attr_accessor :suppress_headers
27
+
28
+ # A flag indicating whether to suppress content rendering to the client.
29
+ attr_accessor :suppress_content
30
+
31
+ attr_accessor :status
32
+
33
+ def initialize
34
+ @buffer_output = true
35
+ @content_type = DefaultContentType
36
+ @charset = DefaultCharset
37
+ @headers = {}
38
+ @error_headers = {}
39
+ @cookies = []
40
+ @output = ''
41
+ @log = ''
42
+ @suppress_headers = false
43
+ @suppress_content = false
44
+
45
+ @status = 200
46
+ update_content_type_header
47
+ end
48
+
49
+ @@status_map = {
50
+ 200 => "200 OK",
51
+ 206 => "206 Partial Content",
52
+ 300 => "300 Multiple Choices",
53
+ 301 => "301 Moved Permanently",
54
+ 302 => "302 Found",
55
+ 304 => "304 Not Modified",
56
+ 400 => "400 Bad Request",
57
+ 401 => "401 Authorization Required",
58
+ 403 => "403 Forbidden",
59
+ 404 => "404 Not Found",
60
+ 405 => "405 Method Not Allowed",
61
+ 406 => "406 Not Acceptable",
62
+ 411 => "411 Length Required",
63
+ 412 => "412 Precondition Failed",
64
+ 500 => "500 Internal Server Error",
65
+ 501 => "501 Method Not Implemented",
66
+ 502 => "502 Bad Gateway",
67
+ 506 => "506 Variant Also Negotiates",
68
+ }
69
+
70
+ def clear_headers
71
+ @headers = {}
72
+ @content_type = nil
73
+ @charset = nil
74
+ end
75
+
76
+ def clear_content
77
+ @output = ''
78
+ end
79
+
80
+ def clear
81
+ clear_headers
82
+ clear_content
83
+ end
84
+
85
+ # Sets the HTTP MIME type of the response. The default
86
+ # is <tt>"text/html"</tt>
87
+ def content_type=(str)
88
+ @content_type = str
89
+ update_content_type_header
90
+ end
91
+
92
+ # Gets the HTTP MIME type of the response. The default
93
+ # is <tt>"text/html"</tt>
94
+ def content_type
95
+ @content_type
96
+ end
97
+
98
+ def charset=(str)
99
+ @charset = str
100
+ update_content_type_header
101
+ end
102
+
103
+ def charset
104
+ @charset
105
+ end
106
+
107
+ def append_cookie(cookie)
108
+ @cookies.push(cookie)
109
+ end
110
+
111
+ # Sets the value of the specified header. Case is significant so, for
112
+ # example +Content-type+ is not valid, whereas +Content-Type+ is valid.
113
+ def set_header(header, value)
114
+ if header.downcase == ContentType.downcase
115
+ set_header_content_type(value)
116
+ elsif header.downcase == Location.downcase
117
+ @error_headers[header] = value
118
+ elsif header.downcase == SetCookie.downcase
119
+ # We include cookies for 302 Found (redirect)
120
+ @error_headers[header] = value
121
+ end
122
+ @headers[header] = value
123
+ end
124
+
125
+ # Returns the specified header value. Case is significant so, for
126
+ # example +Content-type+ is not valid, whereas +Content-Type+ is valid.
127
+ def get_header(header)
128
+ @headers[header]
129
+ end
130
+
131
+ # Removes a header having the specified name. Case is significant.
132
+ def clear_header(header)
133
+ if header.downcase == ContentType.downcase
134
+ @content_type = nil
135
+ end
136
+ @headers.delete(header)
137
+ end
138
+
139
+ def render_headers
140
+ result = '';
141
+
142
+ if @@status_map.has_key? @status
143
+ status = @@status_map[@status]
144
+ else
145
+ status = @@status_map[200]
146
+ end
147
+
148
+ # status line, e.g.: "Status: 200 OK\r\n"
149
+ result = "Status: " + status + EOL
150
+
151
+ if @status < 300
152
+ headers = @headers
153
+ else
154
+ headers = @error_headers
155
+ end
156
+
157
+ headers.each { |key,value|
158
+ if value && !value.empty?
159
+ #result << "#{key}: #{value}" + EOL
160
+ result << "#{key}: #{value}\n"
161
+ end
162
+ }
163
+ if @cookies
164
+ @cookies.each {|cookie|
165
+ result << "Set-Cookie: " + cookie.to_s + EOL
166
+ }
167
+ end
168
+
169
+ # BUGBUG: The output contains \n and after print stdio translates to
170
+ # \r\n, which increases length. We can't use content-length until fixed.
171
+ #result << "Content-Length: #{@output.length}\n"
172
+
173
+ result << EOL
174
+
175
+ #print "Content-type: text/plain; charset=iso-8859-1\n\n"
176
+ result
177
+ end
178
+
179
+ # Called to terminate a request before the render cycle is complete.
180
+ # This is useful for redirecting from init or render handlers, for example.
181
+ def end_request
182
+ raise EndRequest
183
+ end
184
+
185
+ def location(value)
186
+ set_header("Location", value)
187
+ end
188
+
189
+ def redirect(value, status = 302)
190
+ @status = status
191
+ @suppress_content = true
192
+ location(value)
193
+ end_request
194
+ end
195
+
196
+ def render
197
+ result = ''
198
+ result << render_headers unless @suppress_headers
199
+ result << @output
200
+ result
201
+ end
202
+
203
+ def url_encode(string)
204
+ ::CGI::escape(string)
205
+ end
206
+
207
+ def html_encode(string)
208
+ ::CGI::escapeHTML(string)
209
+ end
210
+
211
+ def write(str)
212
+ @output << str
213
+ end
214
+
215
+ def writeline(str)
216
+ @output << str + "\n"
217
+ end
218
+
219
+ def log(text)
220
+ @log << text
221
+ end
222
+
223
+ def get_log
224
+ @log
225
+ end
226
+
227
+ private
228
+
229
+ def update_content_type_header
230
+ if @content_type && !@content_type.empty?
231
+ if @charset && !@charset.empty?
232
+ @headers[ContentType] = "#{@content_type}; charset=#{@charset}"
233
+ else
234
+ @headers[ContentType] = @content_type
235
+ end
236
+ else
237
+ @headers.delete(ContentType)
238
+ end
239
+ end
240
+
241
+ # If the caller manually sets the content type via the headers hash, then
242
+ # we make an effort to set the two special content type attributes from it.
243
+ def set_header_content_type(value)
244
+ @content_type = nil
245
+ @charset = nil
246
+
247
+ if value && !value.empty?
248
+ ct = value.split(/\s*;\s*/)
249
+ if ct.length > 0
250
+ # Look for Content-Type
251
+ if ct[0] =~ /^\s*([^\s]+)\s*$/
252
+ @content_type = $1
253
+ end
254
+
255
+ ct.shift
256
+
257
+ # Look for charset
258
+ ct.each { |param|
259
+ if param =~ /^\s*charset\s*=\s*(.+?)\s*$/
260
+ @charset = $1
261
+ end
262
+ }
263
+ end
264
+ end
265
+ end
266
+
267
+ end
268
+ end