rwdschedule 0.92 → 0.93

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/Readme.txt +131 -120
  2. data/bin/rwdschedule +18 -18
  3. data/code/{00rwdtinkercore/aa0begin.rb → 01rwdcore/01rwdcore.rb} +20 -18
  4. data/code/01rwdcore/02helptexthashbegin.rb +4 -0
  5. data/code/01rwdcore/03helptexthash.rb +48 -0
  6. data/code/01rwdcore/04helptextend.rb +6 -0
  7. data/code/01rwdcore/openhelpwindow.rb +45 -0
  8. data/code/{00rwdtinkercore → 01rwdcore}/rwdtinkerversion.rb +15 -15
  9. data/code/01rwdcore/rwdwindowreturn.rb +11 -0
  10. data/code/superant.com.rwdcalendar/gh9calendar.rb +40 -16
  11. data/code/superant.com.rwdtinkerbackwindow/diagnostictab.rb +21 -21
  12. data/code/superant.com.rwdtinkerbackwindow/installapplet.rb +21 -21
  13. data/code/superant.com.rwdtinkerbackwindow/listinstalledfiles.rb +1 -1
  14. data/code/superant.com.rwdtinkerbackwindow/listzips.rb +6 -6
  15. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationrecord.rb +32 -32
  16. data/code/superant.com.rwdtinkerbackwindow/loadconfigurationvariables.rb +13 -13
  17. data/code/superant.com.rwdtinkerbackwindow/openappletname.rb +18 -18
  18. data/code/superant.com.rwdtinkerbackwindow/removeapplet.rb +28 -28
  19. data/code/{superant.com.rwdtinkercallable/callable.rb → superant.com.rwdtinkerbackwindow/runrwdtinkerbackwindow.rb} +11 -12
  20. data/code/superant.com.rwdtinkerbackwindow/rwdtinkerwin2version.rb +14 -14
  21. data/code/superant.com.rwdtinkerbackwindow/saveconfigurationrecord.rb +18 -18
  22. data/code/superant.com.schedule/archiveevent.rb +14 -0
  23. data/code/superant.com.schedule/cleareventscreendisplay.rb +15 -15
  24. data/code/superant.com.schedule/deleteeventrecord.rb +18 -18
  25. data/code/superant.com.schedule/deleterwdscheduleupdatefiles.rb +19 -19
  26. data/code/superant.com.schedule/downloadrwdschedulefiles.rb +34 -34
  27. data/code/superant.com.schedule/helptexthashrwdschedule.rb +52 -0
  28. data/code/superant.com.schedule/listeventdates.rb +18 -18
  29. data/code/superant.com.schedule/loadconfigurationrecord.rb +34 -0
  30. data/code/superant.com.schedule/loadconfigurationvariables.rb +13 -0
  31. data/code/superant.com.schedule/loadeventrecord.rb +29 -29
  32. data/code/superant.com.schedule/openhelpwindowrubyslippers.rb +25 -0
  33. data/code/superant.com.schedule/renameeventdata.rb +10 -10
  34. data/code/superant.com.schedule/runrwdschedulesyncbackwindow.rb +9 -8
  35. data/code/superant.com.schedule/rwdschedulehelpabout.rb +14 -14
  36. data/code/superant.com.schedule/saveconfigurationrecord.rb +18 -0
  37. data/code/superant.com.schedule/saveeventrecord.rb +18 -18
  38. data/code/superant.com.schedule/syncrwdschedule.rb +30 -30
  39. data/code/superant.com.schedule/uploadrwdschedulefiles.rb +30 -30
  40. data/code/superant.com.schedule/viewevent.rb +20 -20
  41. data/code/superant.com.schedule/viewrwdschedulesconfiguration.rb +21 -21
  42. data/code/zz0applicationend/zz0end.rb +4 -4
  43. data/configuration/language.cnf +5 -5
  44. data/configuration/rwdcalendar.cnf +4 -4
  45. data/configuration/rwdcalendarversion.cnf +4 -4
  46. data/configuration/rwdplayscriptsversion.cnf +2 -0
  47. data/configuration/rwdschedule.cnf +3 -3
  48. data/configuration/rwdscheduleversion.cnf +3 -3
  49. data/configuration/rwdtinker.cnf +3 -0
  50. data/configuration/rwdtinkerversion.cnf +2 -2
  51. data/configuration/tinkerwin2variables.cnf +8 -8
  52. data/configuration/tinkerwin2version.cnf +3 -3
  53. data/ev/browser.rb +109 -109
  54. data/ev/ftools.rb +170 -170
  55. data/ev/net.rb +750 -750
  56. data/ev/ruby.rb +819 -819
  57. data/ev/rwd.rb +1848 -1848
  58. data/ev/sgml.rb +236 -236
  59. data/ev/thread.rb +63 -63
  60. data/ev/tree.rb +343 -343
  61. data/ev/xml.rb +4 -4
  62. data/extras/cal.rb +158 -166
  63. data/gui/00coreguibegin/applicationguitop.rwd +4 -0
  64. data/gui/frontwindow0/superant.com.rwdschedules/gg0viewevent.rwd +27 -27
  65. data/gui/frontwindow0/superant.com.rwdschedules/gl6editrecord.rwd +36 -36
  66. data/gui/frontwindow0/superant.com.rwdschedules/gl8contactutilities.rwd +24 -24
  67. data/gui/frontwindow0/superant.com.rwdschedules/zviewconfiguration.rwd +38 -0
  68. data/gui/frontwindow0/superant.com.tinkercalendar/hl9calendar.rwd +27 -20
  69. data/gui/frontwindowselectionbegin/selectiontabbegin/selectiontabbegin.rwd +16 -16
  70. data/gui/frontwindowselections/superant.com.rwdscheduleselectiontab/rwdschedulessyncselectiontab.rwd +12 -12
  71. data/gui/frontwindowselections/superant.com.rwdtinkerwin2selectiontab/rwdwin2selectiontab.rwd +12 -12
  72. data/gui/frontwindowselectionzend/viewselectionzend/viewselectionend.rwd +3 -3
  73. data/gui/frontwindowtdocumentbegin/superant.com.documentsbegin/tt0documentbegin.rwd +6 -6
  74. data/gui/frontwindowtdocuments/superant.com.documents/uu5documents.rwd +15 -15
  75. data/gui/frontwindowtdocuments/superant.com.rwdscheduledocument/uu8doc_rwdschedule.rwd +4 -4
  76. data/gui/frontwindowtdocuments/superant.com.tinkerwin2documents/uu5documents.rwd +6 -6
  77. data/gui/frontwindowtdocumentzend/superant.com.documentsend/ww0documentend.rwd +12 -7
  78. data/gui/frontwindowz1end/frontwindowend/xx0rwdfirsttab.rwd +6 -6
  79. data/gui/helpaboutbegin/superant.com.helpaboutbegin/ya0helpscreenstart.rwd +3 -3
  80. data/gui/helpaboutinstalled/superant.com.rwdwin2helpabout/1appname.rwd +4 -4
  81. data/gui/helpaboutinstalled/superant.com.rwdwin2helpabout/3copyright.rwd +3 -3
  82. data/gui/helpaboutinstalled/superant.com.rwdwin2helpabout/5version.rwd +10 -10
  83. data/gui/helpaboutinstalled/superant.com.schedulehelpabout/1rwdschedule.rwd +6 -6
  84. data/gui/helpaboutinstalled/superant.com.schedulehelpabout/5version.rwd +9 -9
  85. data/gui/helpaboutinstalled/superant.com.tinkerhelpabout/1appname.rwd +4 -4
  86. data/gui/helpaboutinstalled/superant.com.tinkerhelpabout/3copyright.rwd +3 -3
  87. data/gui/helpaboutinstalled/superant.com.tinkerhelpabout/5version.rwd +10 -10
  88. data/gui/helpaboutzend/superant.com.helpaboutend/helpscreenend.rwd +2 -2
  89. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/1appname.rwd +5 -5
  90. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/20downloadftp.rwd +45 -44
  91. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/66viewconfiguration.rwd +16 -16
  92. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/70rwddiagnostics.rwd +33 -33
  93. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/80returntab.rwd +11 -11
  94. data/gui/tinkerbackwindows/superant.com.rwdschedulebackwindow/zvbackend.rwd +6 -6
  95. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/1appname.rwd +5 -5
  96. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistapplets.rwd +44 -44
  97. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistzips.rwd +35 -35
  98. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/{40editconfiguration.rwd → 60editconfiguration.rwd} +37 -36
  99. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/{3arwddiagnostics.rwd → 70rwddiagnostics.rwd} +33 -33
  100. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/80tab1.rwd +11 -11
  101. data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/9backend.rwd +6 -6
  102. data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/1appname.rwd +32 -0
  103. data/gui/tinkerbackwindows/{superant.com.tinercallablewindow → superant.com.tinkerhelpwindow}/9end.rwd +4 -4
  104. data/gui/tinkerbackwindows/superant.com.versionwindow/1appname.rwd +19 -19
  105. data/gui/zzcoreguiend/tinkerapplicationguiend/yy9rwdend.rwd +4 -4
  106. data/init.rb +233 -233
  107. data/installed/rwdtinkerwin2-0.5.inf +8 -8
  108. data/installed/rwdviewlogo-0.4.inf +4 -0
  109. data/lang/alanguagehashbegin.rb +4 -4
  110. data/lang/en/rwdcalendar/calendar-en.rb +6 -0
  111. data/lang/en/rwdcore/languagefile.rb +15 -15
  112. data/lang/es/rwdcalendar/calendar-nl.rb +6 -6
  113. data/lang/es/rwdcore/languagefile-es.rb +14 -14
  114. data/lang/jp/rwdcore/languagefile.rb +9 -9
  115. data/lang/languagehash.rb +4 -4
  116. data/lang/nl/rwdcalendar/calendar-nl.rb +6 -6
  117. data/lang/nl/rwdcore/languagefile.rb +19 -19
  118. data/lang/templangfile.rb +21 -21
  119. data/lang/vlanguagehashend.rb +6 -6
  120. data/lang/wlocallangstart.rb +5 -5
  121. data/lang/xlocallangfile.rb +21 -21
  122. data/lang/zlocallangend.rb +2 -2
  123. data/lib/temp.rb +1 -1
  124. data/rwd_files/HowTo_Schedule.txt +131 -122
  125. data/rwd_files/HowTo_Tinker.txt +351 -318
  126. data/rwd_files/HowTo_TinkerWin2.txt +202 -202
  127. data/rwd_files/Readme.txt +57 -57
  128. data/rwd_files/rdoc-style.css +174 -174
  129. data/rwd_files/rwdapplications.html +54 -54
  130. data/schedules/200501january23CharlotteB-Day.sch +8 -8
  131. data/updates/200502Februay01 Lynn back.sch +8 -0
  132. data/updates/200502Februay21 PCU Diner.sch +8 -0
  133. data/updates/200502february16a.sch +8 -0
  134. data/updates/200505may02a.sch +8 -0
  135. data/updates/200506june20a.sch +8 -0
  136. data/updates/200506june29a.sch +8 -0
  137. data/updates/200507july04a.sch +8 -0
  138. data/updates/200510october26a.sch +8 -0
  139. metadata +100 -82
  140. data/gui/00coreguibegin/rwdapplicationguitop.rwd +0 -4
  141. data/gui/tinkerbackwindows/superant.com.tinercallablewindow/1appname.rwd +0 -17
  142. data/installed/rwdcalendar-0.5.inf +0 -8
  143. data/installed/schedulesampledata1.inf +0 -3
  144. data/lang/en/rwdcalendar/calendar-nl.rb +0 -6
  145. data/tests/totranslate.lang +0 -93
  146. data/zips/rwdahelloworld-0.5.zip +0 -0
data/ev/net.rb CHANGED
@@ -1,750 +1,750 @@
1
- require "ev/ruby"
2
- require "ev/ftools"
3
- require "net/http"
4
- require "socket"
5
- require "uri"
6
- require "cgi"
7
- require "md5"
8
- require "thread"
9
-
10
- $proxy = ENV["PROXY"] if $proxy.nil?
11
-
12
- file = "#{home}/.evnet"
13
- if File.file?(file)
14
- Hash.file(file).each do |k, v|
15
- eval "$#{k} = '#{v}'" unless k=~ /^\#/
16
- #$proxy_auth = [$proxy_auth].pack("m").chomp if k == "proxy_auth"
17
- end
18
- end
19
-
20
- def uri2txt(s)
21
- i = s.index(/%[[:digit:]]{2}/)
22
- while not i.nil?
23
- s = s[0..(i-1)] + s[(i+1)..(i+2)].unpack('H2').shift.to_i.chr + s[(i+3)..-1]
24
- i = s.index(/%[[:digit:]]{2}/)
25
- end
26
- s
27
- end
28
-
29
- class TCPServer
30
- def self.freeport(from, to, remote=false)
31
- if windows? or cygwin?
32
- TCPServer.freeport_windows(from, to, remote)
33
- else
34
- TCPServer.freeport_linux(from, to, remote)
35
- end
36
- end
37
-
38
- def self.freeport_linux(from, to, remote)
39
- ports = (from..to).to_a
40
- port = nil
41
- res = nil
42
-
43
- while res.nil? and not ports.empty?
44
- begin
45
- port = ports[0]
46
- ports.delete(port)
47
-
48
- io = TCPServer.new(remote ? "0.0.0.0" : "localhost", port)
49
-
50
- res = [port, io]
51
- rescue
52
- end
53
- end
54
-
55
- res = [nil, nil] if res.nil?
56
-
57
- port, io = res
58
-
59
- return port, io
60
- end
61
-
62
- def self.freeport_windows(from, to, remote)
63
- ports = (from..to).to_a
64
- port = nil
65
- res = nil
66
-
67
- while res.nil? and not ports.empty?
68
- begin
69
- port = ports.any
70
- ports.delete(port)
71
-
72
- io = TCPSocket.new("localhost", port)
73
- io.close
74
- rescue
75
- res = port
76
- end
77
- end
78
-
79
- port, io = res
80
-
81
- return port, io
82
- end
83
-
84
- def self.freeport_windows2(from, to, remote)
85
- res = nil
86
- port = from
87
-
88
- while res.nil? and port <= to
89
- begin
90
- io = TCPSocket.new("localhost", port)
91
- io.close
92
-
93
- port += 1
94
- rescue
95
- res = port
96
- end
97
- end
98
-
99
- return res
100
- end
101
-
102
- def self.usedports(from, to)
103
- threads = []
104
- res = []
105
-
106
- from.upto(to) do |port|
107
- threads << Thread.new do
108
- begin
109
- io = TCPSocket.new("localhost", port)
110
- io.close
111
-
112
- port
113
- rescue
114
- nil
115
- end
116
- end
117
- end
118
-
119
- threads.each do |thread|
120
- port = thread.value
121
- res << port unless port.nil?
122
- end
123
-
124
- return res
125
- end
126
- end
127
-
128
- class EVURI
129
- attr_reader :protocol
130
- attr_writer :protocol
131
- attr_reader :userpass
132
- attr_writer :userpass
133
- attr_reader :host
134
- attr_writer :host
135
- attr_reader :port
136
- attr_writer :port
137
- attr_reader :path
138
- attr_writer :path
139
- attr_reader :vars
140
- attr_writer :vars
141
- attr_reader :anchor
142
- attr_writer :anchor
143
-
144
- def initialize(url)
145
- begin
146
- @protocol, @userpass, @host, @port, d1, @path, d2, @vars, @anchor = URI.split(url.to_s)
147
- rescue
148
- end
149
-
150
- @protocol = "" if @protocol.nil?
151
- @userpass = "" if @userpass.nil?
152
- @host = "" if @host.nil?
153
- @port = 0 if @port.nil?
154
- @path = "" if @path.nil?
155
- @vars = "" if @vars.nil?
156
- @anchor = "" if @anchor.nil?
157
-
158
- res = {}
159
- @varsvolgorde = []
160
- @vars.split(/&/).each{|var| k, v = var.split(/=/) ; res[k] = v ; @varsvolgorde << k}
161
- @vars = res
162
-
163
- @port = @port.to_i
164
- end
165
-
166
- def + (url2)
167
- url1 = self.to_s
168
- url2 = url2.to_s if url2.kind_of?(self.class)
169
-
170
- return EVURI.new((URI::Generic.new(*URI.split(url1)) + URI::Generic.new(*URI.split(url2))).to_s) rescue nil
171
- end
172
-
173
- def to_s
174
- protocol = @protocol
175
- userpass = @userpass
176
- host = @host
177
- port = @port.to_s
178
- path = @path
179
- vars = varstring
180
- anchor = @anchor
181
-
182
- protocol = nil if @protocol.empty?
183
- userpass = nil if @userpass.empty?
184
- host = nil if @host.empty?
185
- port = nil if @port.zero?
186
- path = nil if @path.empty?
187
- vars = nil if @vars.empty?
188
- anchor = nil if @anchor.empty?
189
-
190
- res = URI::HTTP.new(@protocol, @userpass, @host, port, nil, @path, nil, vars, @anchor).to_s.from_html
191
-
192
- res.gsub!(/@/, "") if (@userpass.nil? or @userpass.empty?)
193
-
194
- res.gsub!(/\#$/, "")
195
-
196
- return res
197
- end
198
-
199
- def varstring
200
- res = []
201
- vars = @vars.dup
202
-
203
- @varsvolgorde.each do |k|
204
- if vars.include?(k)
205
- v = vars[k]
206
- vars.delete(k)
207
-
208
- res << (v.nil? ? k : "#{k}=#{v}")
209
- end
210
- end
211
-
212
- res.concat(vars.collect{|k, v| v.nil? ? k : "#{k}=#{v}"})
213
-
214
- return res.join("&")
215
- end
216
- end
217
-
218
- class HTTPClient
219
- @@versie = 1
220
- @@mutex = Mutex.new
221
- @@hosts = {}
222
-
223
- class Header
224
- attr_reader :header
225
- attr_reader :protocol
226
- attr_reader :code
227
- attr_reader :text
228
-
229
- def initialize(header)
230
- @header = {}
231
-
232
- if not header.nil?
233
- firstline, rest = header.split(/\r*\n/, 2)
234
-
235
- @protocol, @code, @text = firstline.split(/ */, 3)
236
-
237
- @code = @code.to_i
238
-
239
- if not rest.nil?
240
- rest.split(/\r*\n/).each do |line|
241
- key, value = line.split(/ /, 2)
242
- @header[key.sub(/:$/, "").downcase] = value
243
- end
244
- end
245
- end
246
- end
247
-
248
- def to_s
249
- res = ""
250
-
251
- res << "%s %s %s\n" % [@protocol, @code, @text]
252
-
253
- @header.each do |k, v|
254
- res << "%s=%s\n" % [k, v]
255
- end
256
-
257
- return res
258
- end
259
- end
260
-
261
- class Chunk
262
- def initialize(data)
263
- @data = ""
264
- line, data = data.split(/\r*\n/, 2)
265
- size, ext = line.split(/;/, 2)
266
- size = size.hex
267
- while not size.zero? and not data.nil?
268
- @data += data[0..(size-1)]
269
- data = data[size..-1]
270
- if not data.nil?
271
- data.gsub!(/^\r*\n/, "")
272
- line, data = data.split(/\r*\n/, 2)
273
- size, ext = line.split(/;/, 2)
274
- size = size.hex
275
- end
276
- end
277
- end
278
-
279
- def to_s
280
- @data
281
- end
282
- end
283
-
284
- def self.head(uri, form={}, recursive=true)
285
- header = Header.new(nil)
286
-
287
- begin
288
- while not uri.nil?
289
- if $proxy.nil? or $proxy.empty?
290
- uri = EVURI.new(uri) if uri.kind_of? String
291
- host = uri.host
292
- port = uri.port
293
- io = nil
294
-
295
- @@mutex.synchronize do
296
- @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
297
- io = TCPSocket.new(@@hosts[host], port.zero? ? 80 : port)
298
- end
299
-
300
- io.write("HEAD #{uri.path or '/'}#{uri.varstring.empty? ? '' : '?' + uri.varstring} HTTP/1.0\r\nHost: #{host}\r\n\r\n")
301
- else
302
- proxy = EVURI.new($proxy)
303
- host = proxy.host
304
- port = proxy.port
305
-
306
- io = TCPSocket.new(host, port.zero? ? 8080 : port)
307
-
308
- io.write("HEAD #{uri} HTTP/1.0\r\n#{"Proxy-Authorization: Basic "+$proxy_auth+"\r\n" if not $proxy_auth.nil?}\r\n\r\n")
309
- end
310
-
311
- io.close_write
312
-
313
- res = io.read
314
- header, data = nil, nil
315
- header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil?
316
- header = Header.new(header)
317
-
318
- if recursive and header.header["location"] != uri.to_s
319
- uri = EVURI.new(uri) + header.header["location"]
320
- else
321
- uri = nil
322
- end
323
- end
324
- rescue
325
- header = Header.new(nil)
326
- end
327
-
328
- return header
329
- end
330
-
331
- def self.get(uri, form={})
332
- post = Array.new
333
- form.each_pair do |var, value|
334
- post << "#{var.to_html}=#{value.to_html}"
335
- end
336
- post = post.join("?")
337
-
338
- data = nil
339
-
340
- begin
341
- while not uri.nil?
342
- if $proxy.nil? or $proxy.empty?
343
- uri = EVURI.new(uri) if uri.kind_of? String
344
- host = uri.host
345
- port = uri.port
346
-
347
- io = nil
348
- @@mutex.synchronize do
349
- @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
350
- io = TCPSocket.new(@@hosts[host], port.zero? ? 80 : port)
351
- end
352
-
353
- if post.empty?
354
- io.write "GET %s%s HTTP/1.0\r\n" % [(uri.path or '/'), (uri.varstring.empty? ? '' : '?' + uri.varstring)]
355
- else
356
- io.write "POST %s%s HTTP/1.0\r\n" % [(uri.path or '/'), (uri.varstring.empty? ? '' : '?' + uri.varstring)]
357
- end
358
- else
359
- proxy = EVURI.new($proxy)
360
- host = proxy.host
361
- port = proxy.port
362
-
363
- io = TCPSocket.new(host, port.zero? ? 8080 : port)
364
-
365
- if post.empty?
366
- io.write "GET %s HTTP/1.0\r\n" % uri
367
- else
368
- io.write "POST %s HTTP/1.0\r\n" % uri
369
- end
370
- end
371
-
372
- io.write "Host: %s\r\n" % host
373
- io.write "User-Agent: evwget\r\n"
374
- io.write "Proxy-Authorization: Basic %s\r\n" % $proxy_auth unless $proxy_auth.nil?
375
- #io.write "Accept-Encoding: deflate\r\n"
376
- #io.write "Connection: close\r\n"
377
- io.write "Content-Type: application/x-www-form-urlencoded\r\n" unless post.empty?
378
- io.write "Content-Length: %s\r\n" % post.length unless post.empty?
379
- io.write "\r\n"
380
- io.write post unless post.empty?
381
-
382
- io.close_write
383
-
384
- res = io.read
385
- header, data = nil, nil
386
- header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil?
387
-
388
- header = Header.new(header)
389
-
390
- if header.header["location"] != uri.to_s
391
- uri = EVURI.new(uri) + header.header["location"]
392
- else
393
- uri = nil
394
- end
395
-
396
- if header.header["transfer-encoding"] == "chunked"
397
- data = Chunk.new(data).to_s if not data.nil?
398
- end
399
-
400
- data = nil unless header.code == 200
401
- end
402
- rescue
403
- data = nil
404
- end
405
-
406
- return data
407
- end
408
-
409
- def self.head_from_cache(uri, form={})
410
- from_cache("head", uri, form)
411
- end
412
-
413
- def self.get_from_cache(uri, form={})
414
- from_cache("get", uri, form)
415
- end
416
-
417
- def self.from_cache(action, uri, form)
418
- loc = uri.to_s + form.sort.inspect
419
- hash = MD5.new("#{@@versie} #{loc}")
420
-
421
- dir = "#{temp}/evcache.#{user}/httpclient.#{action}"
422
- file = "#{dir}/#{hash}"
423
- data = nil
424
-
425
- Dir.mkdirrec(dir)
426
-
427
- expire = 356*24*60*60
428
-
429
- if File.file?(file) and (Time.new.to_f - File.stat(file).mtime.to_f < expire)
430
- @@mutex.synchronize do
431
- File.open(file, "rb") {|f| data = f.read}
432
- end
433
- else
434
- data = method(action).call(uri, form)
435
-
436
- if not data.nil?
437
- @@mutex.synchronize do
438
- File.open(file, "wb") {|f| f.write data}
439
- end
440
- end
441
- end
442
-
443
- return data
444
- end
445
- end
446
-
447
- class RequestGet < Hash
448
- def initialize(data)
449
- CGI.parse(data).each do |k, v|
450
- self[k] = v.join(" ")
451
- end
452
- end
453
- end
454
-
455
- class RequestPost < Hash
456
- def initialize(data)
457
- CGI.parse(data).each do |k, v|
458
- self[k] = v.join(" ")
459
- end
460
- end
461
- end
462
-
463
- class RequestRequest
464
- attr_reader :method
465
- attr_reader :uri
466
- attr_reader :path
467
- attr_reader :data
468
- attr_reader :protocol
469
-
470
- def initialize(firstline)
471
- @method, @uri, @protocol = firstline.split(/ /)
472
- @path, @data = @uri.split(/\?/)
473
- @data = "" if @data.nil? # TODO
474
-
475
- # i = @path.index(/%[[:digit:]]{2}/)
476
- # while not i.nil?
477
- # @path = @path[0..(i-1)] + @path[(i+1)..(i+2)].unpack('H2').shift.to_i.chr + @path[(i+3)..-1]
478
- # i = @path.index(/%[[:digit:]]{2}/)
479
- # end
480
- end
481
-
482
- def to_s
483
- "#{@method} #{@uri} #{@protocol}\r\n"
484
- end
485
-
486
- def inspect
487
- "(RequestRequest: %s)" % [@method, @path, @data, @protocol].join(", ")
488
- end
489
- end
490
-
491
- class Request < Hash
492
- attr_reader :peeraddr
493
- attr_reader :request
494
- attr_reader :cookies
495
- attr_reader :vars
496
-
497
- def initialize(io)
498
- @io = io
499
-
500
- firstline = @io.gets
501
-
502
- return if firstline.nil?
503
-
504
- @request = RequestRequest.new(firstline.strip)
505
-
506
- line = @io.gets
507
- line = line.strip unless line.nil?
508
- while not line.nil? and not line.empty?
509
- key, value = line.split(" ", 2)
510
- self[key.sub(/:$/, "").downcase] = value
511
-
512
- line = @io.gets
513
- line = line.strip unless line.nil?
514
- end
515
-
516
- cookie = self["cookie"]
517
- cookie = "" if cookie.nil?
518
- @cookies = {}
519
- cookie.split(/;/).each do |s|
520
- k, v = s.strip.split(/=/, 2)
521
- @cookies[k] = v
522
- end
523
-
524
- if not @request.method.nil?
525
- case @request.method.upcase
526
- when "HEAD"
527
- when "GET"
528
- @vars = RequestGet.new(@request.data.nil? ? "" : @request.data)
529
- when "POST"
530
- data = (@io.read(self["content-length"].to_i) or "")
531
- @vars = RequestPost.new((self["content-type"] == "application/x-www-form-urlencoded") ? data : "")
532
- else
533
- $stderr.puts "Unknown request ('#{firstline}')."
534
- end
535
- end
536
-
537
- @peeraddr = @io.peeraddr
538
-
539
- @pda = false
540
- @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("windows ce"))
541
- @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("handhttp"))
542
-
543
- @io.close_read
544
- end
545
-
546
- def pda?
547
- @pda
548
- end
549
-
550
- def to_s
551
- res = @request.to_s
552
- self.each do |k, v|
553
- res << "#{k}: #{v}\r\n"
554
- end
555
- res
556
- end
557
-
558
- def inspect
559
- "(Request: %s)" % [@peeraddr, @request.inspect, @vars.inspect, @cookies.inspect, super].join(", ")
560
- end
561
- end
562
-
563
- class Response < Hash
564
- attr_writer :response
565
- attr_reader :cookies
566
- attr_reader :stop
567
- attr_reader :at_stop
568
-
569
- def initialize(io)
570
- @io = io
571
- @response = "HTTP/1.0 200 OK"
572
- @cookies = {}
573
- @data = ""
574
- @syncd = false
575
- @stop = false
576
- @at_stop = lambda{}
577
- end
578
-
579
- def flush
580
- sync
581
- @io.close
582
- end
583
-
584
- def to_s
585
- res = "#{@response}\r\n"
586
- self.each do |k, v|
587
- res << "#{k}: #{v}\r\n"
588
- end
589
-
590
- @cookies.each do |k, v|
591
- res << "Set-Cookie: %s=%s;\r\n" % [k, v]
592
- end
593
-
594
- res
595
- end
596
-
597
- def sync
598
- @io.write("#{to_s}\r\n") unless @syncd
599
- @io.write(@data)
600
- @data = ""
601
- @syncd = true
602
- end
603
-
604
- def << (s)
605
- @data << s
606
- end
607
-
608
- def inspect
609
- "(Response: %s)" % [@response, @data].join(", ")
610
- end
611
-
612
- def stop(&block)
613
- @stop = true
614
- @at_stop = block unless block.nil?
615
- end
616
-
617
- def stop?
618
- @stop
619
- end
620
- end
621
-
622
- class HTTPServerException < Exception
623
- end
624
-
625
- class HTTPServer
626
- def self.serve(portio=80, remote=false, auth=nil, realm="ev/net")
627
- port, server = portio
628
-
629
- begin
630
- server = TCPServer.new(remote ? "0.0.0.0" : "localhost", port) if server.nil?
631
-
632
- $stderr.puts "Just point your browser to http://localhost:#{port}/ ..."
633
- rescue
634
- server = nil
635
-
636
- $stderr.puts "Port #{port} is in use."
637
- end
638
-
639
- if not server.nil?
640
- count = 0
641
-
642
- at_exit do
643
- $stderr.puts "Received #{count} requests"
644
- end
645
-
646
- serverthread =
647
- Thread.new do
648
- mutex = Mutex.new
649
-
650
- Thread.current["threads"] = []
651
-
652
- every(1, Thread.current) do |thread|
653
- mutex.synchronize do
654
- thread["threads"].delete_if{|t| (not t.alive?)}
655
- end
656
- end
657
-
658
- loop do
659
- io = server.accept
660
- count += 1
661
-
662
- thread =
663
- Thread.new(Thread.current, count) do |parentthread, count2|
664
- stop = false
665
-
666
- begin
667
- begin
668
- req = Request.new(io)
669
- resp = Response.new(io)
670
- rescue
671
- raise HTTPServerException
672
- end
673
-
674
- begin
675
- ip = req.peeraddr[3]
676
- rescue NameError
677
- raise HTTPServerException
678
- end
679
-
680
- if (not remote) or (remote and (auth.nil? or auth.empty? or authenticate(auth, realm, req, resp)))
681
- $stderr.puts "#{count2}, #{Time.new.strftime("%Y-%m-%d.%H:%M:%S")}, #{ip}, #{req.request.to_s.strip}"
682
-
683
- begin
684
- yield(req, resp)
685
- rescue Exception => e
686
- mutex.synchronize do
687
- $stderr.puts e.class.to_s + ": " + e.message
688
- $stderr.puts e.backtrace.collect{|s| "\t"+s}.join("\n")
689
- end
690
- end
691
-
692
- stop = true if resp.stop?
693
- end
694
-
695
- begin
696
- resp.flush
697
- rescue
698
- raise HTTPServerException
699
- end
700
- rescue HTTPServerException
701
- end
702
-
703
- parentthread["stop"] = resp if stop
704
- end
705
-
706
- mutex.synchronize do
707
- Thread.current["threads"] << thread
708
- end
709
- end
710
- end
711
-
712
- sleep 0.1 while not serverthread["stop"]
713
-
714
- serverthread["threads"].each {|t| t.join}
715
-
716
- serverthread["stop"].at_stop.call
717
-
718
- serverthread.kill
719
- end
720
- end
721
-
722
- def self.authenticate(auth, realm, req, resp)
723
- if auth.kind_of? String
724
- file = "#{home}/#{auth}"
725
- auths = {}
726
- auths = Hash.file(file) if File.file?(file)
727
- else
728
- auths = auth
729
- end
730
-
731
- authuserpassword = req["authorization"]
732
- if not authuserpassword.nil?
733
- authtype, userpassword = authuserpassword.split(/ /)
734
- if authtype == "Basic" and not userpassword.nil?
735
- u, p = userpassword.unpack("m").shift.split(/:/)
736
- end
737
- end
738
-
739
- ok = (auths.include?(u) and auths[u] == p)
740
-
741
- if ok
742
-
743
- else
744
- resp["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
745
- resp.response = "HTTP/1.0 401 Unauthorized"
746
- end
747
-
748
- return ok
749
- end
750
- end
1
+ require "ev/ruby"
2
+ require "ev/ftools"
3
+ require "net/http"
4
+ require "socket"
5
+ require "uri"
6
+ require "cgi"
7
+ require "md5"
8
+ require "thread"
9
+
10
+ $proxy = ENV["PROXY"] if $proxy.nil?
11
+
12
+ file = "#{home}/.evnet"
13
+ if File.file?(file)
14
+ Hash.file(file).each do |k, v|
15
+ eval "$#{k} = '#{v}'" unless k=~ /^\#/
16
+ #$proxy_auth = [$proxy_auth].pack("m").chomp if k == "proxy_auth"
17
+ end
18
+ end
19
+
20
+ def uri2txt(s)
21
+ i = s.index(/%[[:digit:]]{2}/)
22
+ while not i.nil?
23
+ s = s[0..(i-1)] + s[(i+1)..(i+2)].unpack('H2').shift.to_i.chr + s[(i+3)..-1]
24
+ i = s.index(/%[[:digit:]]{2}/)
25
+ end
26
+ s
27
+ end
28
+
29
+ class TCPServer
30
+ def self.freeport(from, to, remote=false)
31
+ if windows? or cygwin?
32
+ TCPServer.freeport_windows(from, to, remote)
33
+ else
34
+ TCPServer.freeport_linux(from, to, remote)
35
+ end
36
+ end
37
+
38
+ def self.freeport_linux(from, to, remote)
39
+ ports = (from..to).to_a
40
+ port = nil
41
+ res = nil
42
+
43
+ while res.nil? and not ports.empty?
44
+ begin
45
+ port = ports[0]
46
+ ports.delete(port)
47
+
48
+ io = TCPServer.new(remote ? "0.0.0.0" : "localhost", port)
49
+
50
+ res = [port, io]
51
+ rescue
52
+ end
53
+ end
54
+
55
+ res = [nil, nil] if res.nil?
56
+
57
+ port, io = res
58
+
59
+ return port, io
60
+ end
61
+
62
+ def self.freeport_windows(from, to, remote)
63
+ ports = (from..to).to_a
64
+ port = nil
65
+ res = nil
66
+
67
+ while res.nil? and not ports.empty?
68
+ begin
69
+ port = ports.any
70
+ ports.delete(port)
71
+
72
+ io = TCPSocket.new("localhost", port)
73
+ io.close
74
+ rescue
75
+ res = port
76
+ end
77
+ end
78
+
79
+ port, io = res
80
+
81
+ return port, io
82
+ end
83
+
84
+ def self.freeport_windows2(from, to, remote)
85
+ res = nil
86
+ port = from
87
+
88
+ while res.nil? and port <= to
89
+ begin
90
+ io = TCPSocket.new("localhost", port)
91
+ io.close
92
+
93
+ port += 1
94
+ rescue
95
+ res = port
96
+ end
97
+ end
98
+
99
+ return res
100
+ end
101
+
102
+ def self.usedports(from, to)
103
+ threads = []
104
+ res = []
105
+
106
+ from.upto(to) do |port|
107
+ threads << Thread.new do
108
+ begin
109
+ io = TCPSocket.new("localhost", port)
110
+ io.close
111
+
112
+ port
113
+ rescue
114
+ nil
115
+ end
116
+ end
117
+ end
118
+
119
+ threads.each do |thread|
120
+ port = thread.value
121
+ res << port unless port.nil?
122
+ end
123
+
124
+ return res
125
+ end
126
+ end
127
+
128
+ class EVURI
129
+ attr_reader :protocol
130
+ attr_writer :protocol
131
+ attr_reader :userpass
132
+ attr_writer :userpass
133
+ attr_reader :host
134
+ attr_writer :host
135
+ attr_reader :port
136
+ attr_writer :port
137
+ attr_reader :path
138
+ attr_writer :path
139
+ attr_reader :vars
140
+ attr_writer :vars
141
+ attr_reader :anchor
142
+ attr_writer :anchor
143
+
144
+ def initialize(url)
145
+ begin
146
+ @protocol, @userpass, @host, @port, d1, @path, d2, @vars, @anchor = URI.split(url.to_s)
147
+ rescue
148
+ end
149
+
150
+ @protocol = "" if @protocol.nil?
151
+ @userpass = "" if @userpass.nil?
152
+ @host = "" if @host.nil?
153
+ @port = 0 if @port.nil?
154
+ @path = "" if @path.nil?
155
+ @vars = "" if @vars.nil?
156
+ @anchor = "" if @anchor.nil?
157
+
158
+ res = {}
159
+ @varsvolgorde = []
160
+ @vars.split(/&/).each{|var| k, v = var.split(/=/) ; res[k] = v ; @varsvolgorde << k}
161
+ @vars = res
162
+
163
+ @port = @port.to_i
164
+ end
165
+
166
+ def + (url2)
167
+ url1 = self.to_s
168
+ url2 = url2.to_s if url2.kind_of?(self.class)
169
+
170
+ return EVURI.new((URI::Generic.new(*URI.split(url1)) + URI::Generic.new(*URI.split(url2))).to_s) rescue nil
171
+ end
172
+
173
+ def to_s
174
+ protocol = @protocol
175
+ userpass = @userpass
176
+ host = @host
177
+ port = @port.to_s
178
+ path = @path
179
+ vars = varstring
180
+ anchor = @anchor
181
+
182
+ protocol = nil if @protocol.empty?
183
+ userpass = nil if @userpass.empty?
184
+ host = nil if @host.empty?
185
+ port = nil if @port.zero?
186
+ path = nil if @path.empty?
187
+ vars = nil if @vars.empty?
188
+ anchor = nil if @anchor.empty?
189
+
190
+ res = URI::HTTP.new(@protocol, @userpass, @host, port, nil, @path, nil, vars, @anchor).to_s.from_html
191
+
192
+ res.gsub!(/@/, "") if (@userpass.nil? or @userpass.empty?)
193
+
194
+ res.gsub!(/\#$/, "")
195
+
196
+ return res
197
+ end
198
+
199
+ def varstring
200
+ res = []
201
+ vars = @vars.dup
202
+
203
+ @varsvolgorde.each do |k|
204
+ if vars.include?(k)
205
+ v = vars[k]
206
+ vars.delete(k)
207
+
208
+ res << (v.nil? ? k : "#{k}=#{v}")
209
+ end
210
+ end
211
+
212
+ res.concat(vars.collect{|k, v| v.nil? ? k : "#{k}=#{v}"})
213
+
214
+ return res.join("&")
215
+ end
216
+ end
217
+
218
+ class HTTPClient
219
+ @@versie = 1
220
+ @@mutex = Mutex.new
221
+ @@hosts = {}
222
+
223
+ class Header
224
+ attr_reader :header
225
+ attr_reader :protocol
226
+ attr_reader :code
227
+ attr_reader :text
228
+
229
+ def initialize(header)
230
+ @header = {}
231
+
232
+ if not header.nil?
233
+ firstline, rest = header.split(/\r*\n/, 2)
234
+
235
+ @protocol, @code, @text = firstline.split(/ */, 3)
236
+
237
+ @code = @code.to_i
238
+
239
+ if not rest.nil?
240
+ rest.split(/\r*\n/).each do |line|
241
+ key, value = line.split(/ /, 2)
242
+ @header[key.sub(/:$/, "").downcase] = value
243
+ end
244
+ end
245
+ end
246
+ end
247
+
248
+ def to_s
249
+ res = ""
250
+
251
+ res << "%s %s %s\n" % [@protocol, @code, @text]
252
+
253
+ @header.each do |k, v|
254
+ res << "%s=%s\n" % [k, v]
255
+ end
256
+
257
+ return res
258
+ end
259
+ end
260
+
261
+ class Chunk
262
+ def initialize(data)
263
+ @data = ""
264
+ line, data = data.split(/\r*\n/, 2)
265
+ size, ext = line.split(/;/, 2)
266
+ size = size.hex
267
+ while not size.zero? and not data.nil?
268
+ @data += data[0..(size-1)]
269
+ data = data[size..-1]
270
+ if not data.nil?
271
+ data.gsub!(/^\r*\n/, "")
272
+ line, data = data.split(/\r*\n/, 2)
273
+ size, ext = line.split(/;/, 2)
274
+ size = size.hex
275
+ end
276
+ end
277
+ end
278
+
279
+ def to_s
280
+ @data
281
+ end
282
+ end
283
+
284
+ def self.head(uri, form={}, recursive=true)
285
+ header = Header.new(nil)
286
+
287
+ begin
288
+ while not uri.nil?
289
+ if $proxy.nil? or $proxy.empty?
290
+ uri = EVURI.new(uri) if uri.kind_of? String
291
+ host = uri.host
292
+ port = uri.port
293
+ io = nil
294
+
295
+ @@mutex.synchronize do
296
+ @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
297
+ io = TCPSocket.new(@@hosts[host], port.zero? ? 80 : port)
298
+ end
299
+
300
+ io.write("HEAD #{uri.path or '/'}#{uri.varstring.empty? ? '' : '?' + uri.varstring} HTTP/1.0\r\nHost: #{host}\r\n\r\n")
301
+ else
302
+ proxy = EVURI.new($proxy)
303
+ host = proxy.host
304
+ port = proxy.port
305
+
306
+ io = TCPSocket.new(host, port.zero? ? 8080 : port)
307
+
308
+ io.write("HEAD #{uri} HTTP/1.0\r\n#{"Proxy-Authorization: Basic "+$proxy_auth+"\r\n" if not $proxy_auth.nil?}\r\n\r\n")
309
+ end
310
+
311
+ io.close_write
312
+
313
+ res = io.read
314
+ header, data = nil, nil
315
+ header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil?
316
+ header = Header.new(header)
317
+
318
+ if recursive and header.header["location"] != uri.to_s
319
+ uri = EVURI.new(uri) + header.header["location"]
320
+ else
321
+ uri = nil
322
+ end
323
+ end
324
+ rescue
325
+ header = Header.new(nil)
326
+ end
327
+
328
+ return header
329
+ end
330
+
331
+ def self.get(uri, form={})
332
+ post = Array.new
333
+ form.each_pair do |var, value|
334
+ post << "#{var.to_html}=#{value.to_html}"
335
+ end
336
+ post = post.join("?")
337
+
338
+ data = nil
339
+
340
+ begin
341
+ while not uri.nil?
342
+ if $proxy.nil? or $proxy.empty?
343
+ uri = EVURI.new(uri) if uri.kind_of? String
344
+ host = uri.host
345
+ port = uri.port
346
+
347
+ io = nil
348
+ @@mutex.synchronize do
349
+ @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
350
+ io = TCPSocket.new(@@hosts[host], port.zero? ? 80 : port)
351
+ end
352
+
353
+ if post.empty?
354
+ io.write "GET %s%s HTTP/1.0\r\n" % [(uri.path or '/'), (uri.varstring.empty? ? '' : '?' + uri.varstring)]
355
+ else
356
+ io.write "POST %s%s HTTP/1.0\r\n" % [(uri.path or '/'), (uri.varstring.empty? ? '' : '?' + uri.varstring)]
357
+ end
358
+ else
359
+ proxy = EVURI.new($proxy)
360
+ host = proxy.host
361
+ port = proxy.port
362
+
363
+ io = TCPSocket.new(host, port.zero? ? 8080 : port)
364
+
365
+ if post.empty?
366
+ io.write "GET %s HTTP/1.0\r\n" % uri
367
+ else
368
+ io.write "POST %s HTTP/1.0\r\n" % uri
369
+ end
370
+ end
371
+
372
+ io.write "Host: %s\r\n" % host
373
+ io.write "User-Agent: evwget\r\n"
374
+ io.write "Proxy-Authorization: Basic %s\r\n" % $proxy_auth unless $proxy_auth.nil?
375
+ #io.write "Accept-Encoding: deflate\r\n"
376
+ #io.write "Connection: close\r\n"
377
+ io.write "Content-Type: application/x-www-form-urlencoded\r\n" unless post.empty?
378
+ io.write "Content-Length: %s\r\n" % post.length unless post.empty?
379
+ io.write "\r\n"
380
+ io.write post unless post.empty?
381
+
382
+ io.close_write
383
+
384
+ res = io.read
385
+ header, data = nil, nil
386
+ header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil?
387
+
388
+ header = Header.new(header)
389
+
390
+ if header.header["location"] != uri.to_s
391
+ uri = EVURI.new(uri) + header.header["location"]
392
+ else
393
+ uri = nil
394
+ end
395
+
396
+ if header.header["transfer-encoding"] == "chunked"
397
+ data = Chunk.new(data).to_s if not data.nil?
398
+ end
399
+
400
+ data = nil unless header.code == 200
401
+ end
402
+ rescue
403
+ data = nil
404
+ end
405
+
406
+ return data
407
+ end
408
+
409
+ def self.head_from_cache(uri, form={})
410
+ from_cache("head", uri, form)
411
+ end
412
+
413
+ def self.get_from_cache(uri, form={})
414
+ from_cache("get", uri, form)
415
+ end
416
+
417
+ def self.from_cache(action, uri, form)
418
+ loc = uri.to_s + form.sort.inspect
419
+ hash = MD5.new("#{@@versie} #{loc}")
420
+
421
+ dir = "#{temp}/evcache.#{user}/httpclient.#{action}"
422
+ file = "#{dir}/#{hash}"
423
+ data = nil
424
+
425
+ Dir.mkdirrec(dir)
426
+
427
+ expire = 356*24*60*60
428
+
429
+ if File.file?(file) and (Time.new.to_f - File.stat(file).mtime.to_f < expire)
430
+ @@mutex.synchronize do
431
+ File.open(file, "rb") {|f| data = f.read}
432
+ end
433
+ else
434
+ data = method(action).call(uri, form)
435
+
436
+ if not data.nil?
437
+ @@mutex.synchronize do
438
+ File.open(file, "wb") {|f| f.write data}
439
+ end
440
+ end
441
+ end
442
+
443
+ return data
444
+ end
445
+ end
446
+
447
+ class RequestGet < Hash
448
+ def initialize(data)
449
+ CGI.parse(data).each do |k, v|
450
+ self[k] = v.join(" ")
451
+ end
452
+ end
453
+ end
454
+
455
+ class RequestPost < Hash
456
+ def initialize(data)
457
+ CGI.parse(data).each do |k, v|
458
+ self[k] = v.join(" ")
459
+ end
460
+ end
461
+ end
462
+
463
+ class RequestRequest
464
+ attr_reader :method
465
+ attr_reader :uri
466
+ attr_reader :path
467
+ attr_reader :data
468
+ attr_reader :protocol
469
+
470
+ def initialize(firstline)
471
+ @method, @uri, @protocol = firstline.split(/ /)
472
+ @path, @data = @uri.split(/\?/)
473
+ @data = "" if @data.nil? # TODO
474
+
475
+ # i = @path.index(/%[[:digit:]]{2}/)
476
+ # while not i.nil?
477
+ # @path = @path[0..(i-1)] + @path[(i+1)..(i+2)].unpack('H2').shift.to_i.chr + @path[(i+3)..-1]
478
+ # i = @path.index(/%[[:digit:]]{2}/)
479
+ # end
480
+ end
481
+
482
+ def to_s
483
+ "#{@method} #{@uri} #{@protocol}\r\n"
484
+ end
485
+
486
+ def inspect
487
+ "(RequestRequest: %s)" % [@method, @path, @data, @protocol].join(", ")
488
+ end
489
+ end
490
+
491
+ class Request < Hash
492
+ attr_reader :peeraddr
493
+ attr_reader :request
494
+ attr_reader :cookies
495
+ attr_reader :vars
496
+
497
+ def initialize(io)
498
+ @io = io
499
+
500
+ firstline = @io.gets
501
+
502
+ return if firstline.nil?
503
+
504
+ @request = RequestRequest.new(firstline.strip)
505
+
506
+ line = @io.gets
507
+ line = line.strip unless line.nil?
508
+ while not line.nil? and not line.empty?
509
+ key, value = line.split(" ", 2)
510
+ self[key.sub(/:$/, "").downcase] = value
511
+
512
+ line = @io.gets
513
+ line = line.strip unless line.nil?
514
+ end
515
+
516
+ cookie = self["cookie"]
517
+ cookie = "" if cookie.nil?
518
+ @cookies = {}
519
+ cookie.split(/;/).each do |s|
520
+ k, v = s.strip.split(/=/, 2)
521
+ @cookies[k] = v
522
+ end
523
+
524
+ if not @request.method.nil?
525
+ case @request.method.upcase
526
+ when "HEAD"
527
+ when "GET"
528
+ @vars = RequestGet.new(@request.data.nil? ? "" : @request.data)
529
+ when "POST"
530
+ data = (@io.read(self["content-length"].to_i) or "")
531
+ @vars = RequestPost.new((self["content-type"] == "application/x-www-form-urlencoded") ? data : "")
532
+ else
533
+ $stderr.puts "Unknown request ('#{firstline}')."
534
+ end
535
+ end
536
+
537
+ @peeraddr = @io.peeraddr
538
+
539
+ @pda = false
540
+ @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("windows ce"))
541
+ @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("handhttp"))
542
+
543
+ @io.close_read
544
+ end
545
+
546
+ def pda?
547
+ @pda
548
+ end
549
+
550
+ def to_s
551
+ res = @request.to_s
552
+ self.each do |k, v|
553
+ res << "#{k}: #{v}\r\n"
554
+ end
555
+ res
556
+ end
557
+
558
+ def inspect
559
+ "(Request: %s)" % [@peeraddr, @request.inspect, @vars.inspect, @cookies.inspect, super].join(", ")
560
+ end
561
+ end
562
+
563
+ class Response < Hash
564
+ attr_writer :response
565
+ attr_reader :cookies
566
+ attr_reader :stop
567
+ attr_reader :at_stop
568
+
569
+ def initialize(io)
570
+ @io = io
571
+ @response = "HTTP/1.0 200 OK"
572
+ @cookies = {}
573
+ @data = ""
574
+ @syncd = false
575
+ @stop = false
576
+ @at_stop = lambda{}
577
+ end
578
+
579
+ def flush
580
+ sync
581
+ @io.close
582
+ end
583
+
584
+ def to_s
585
+ res = "#{@response}\r\n"
586
+ self.each do |k, v|
587
+ res << "#{k}: #{v}\r\n"
588
+ end
589
+
590
+ @cookies.each do |k, v|
591
+ res << "Set-Cookie: %s=%s;\r\n" % [k, v]
592
+ end
593
+
594
+ res
595
+ end
596
+
597
+ def sync
598
+ @io.write("#{to_s}\r\n") unless @syncd
599
+ @io.write(@data)
600
+ @data = ""
601
+ @syncd = true
602
+ end
603
+
604
+ def << (s)
605
+ @data << s
606
+ end
607
+
608
+ def inspect
609
+ "(Response: %s)" % [@response, @data].join(", ")
610
+ end
611
+
612
+ def stop(&block)
613
+ @stop = true
614
+ @at_stop = block unless block.nil?
615
+ end
616
+
617
+ def stop?
618
+ @stop
619
+ end
620
+ end
621
+
622
+ class HTTPServerException < Exception
623
+ end
624
+
625
+ class HTTPServer
626
+ def self.serve(portio=80, remote=false, auth=nil, realm="ev/net")
627
+ port, server = portio
628
+
629
+ begin
630
+ server = TCPServer.new(remote ? "0.0.0.0" : "localhost", port) if server.nil?
631
+
632
+ $stderr.puts "Just point your browser to http://localhost:#{port}/ ..."
633
+ rescue
634
+ server = nil
635
+
636
+ $stderr.puts "Port #{port} is in use."
637
+ end
638
+
639
+ if not server.nil?
640
+ count = 0
641
+
642
+ at_exit do
643
+ $stderr.puts "Received #{count} requests"
644
+ end
645
+
646
+ serverthread =
647
+ Thread.new do
648
+ mutex = Mutex.new
649
+
650
+ Thread.current["threads"] = []
651
+
652
+ every(1, Thread.current) do |thread|
653
+ mutex.synchronize do
654
+ thread["threads"].delete_if{|t| (not t.alive?)}
655
+ end
656
+ end
657
+
658
+ loop do
659
+ io = server.accept
660
+ count += 1
661
+
662
+ thread =
663
+ Thread.new(Thread.current, count) do |parentthread, count2|
664
+ stop = false
665
+
666
+ begin
667
+ begin
668
+ req = Request.new(io)
669
+ resp = Response.new(io)
670
+ rescue
671
+ raise HTTPServerException
672
+ end
673
+
674
+ begin
675
+ ip = req.peeraddr[3]
676
+ rescue NameError
677
+ raise HTTPServerException
678
+ end
679
+
680
+ if (not remote) or (remote and (auth.nil? or auth.empty? or authenticate(auth, realm, req, resp)))
681
+ $stderr.puts "#{count2}, #{Time.new.strftime("%Y-%m-%d.%H:%M:%S")}, #{ip}, #{req.request.to_s.strip}"
682
+
683
+ begin
684
+ yield(req, resp)
685
+ rescue Exception => e
686
+ mutex.synchronize do
687
+ $stderr.puts e.class.to_s + ": " + e.message
688
+ $stderr.puts e.backtrace.collect{|s| "\t"+s}.join("\n")
689
+ end
690
+ end
691
+
692
+ stop = true if resp.stop?
693
+ end
694
+
695
+ begin
696
+ resp.flush
697
+ rescue
698
+ raise HTTPServerException
699
+ end
700
+ rescue HTTPServerException
701
+ end
702
+
703
+ parentthread["stop"] = resp if stop
704
+ end
705
+
706
+ mutex.synchronize do
707
+ Thread.current["threads"] << thread
708
+ end
709
+ end
710
+ end
711
+
712
+ sleep 0.1 while not serverthread["stop"]
713
+
714
+ serverthread["threads"].each {|t| t.join}
715
+
716
+ serverthread["stop"].at_stop.call
717
+
718
+ serverthread.kill
719
+ end
720
+ end
721
+
722
+ def self.authenticate(auth, realm, req, resp)
723
+ if auth.kind_of? String
724
+ file = "#{home}/#{auth}"
725
+ auths = {}
726
+ auths = Hash.file(file) if File.file?(file)
727
+ else
728
+ auths = auth
729
+ end
730
+
731
+ authuserpassword = req["authorization"]
732
+ if not authuserpassword.nil?
733
+ authtype, userpassword = authuserpassword.split(/ /)
734
+ if authtype == "Basic" and not userpassword.nil?
735
+ u, p = userpassword.unpack("m").shift.split(/:/)
736
+ end
737
+ end
738
+
739
+ ok = (auths.include?(u) and auths[u] == p)
740
+
741
+ if ok
742
+
743
+ else
744
+ resp["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
745
+ resp.response = "HTTP/1.0 401 Unauthorized"
746
+ end
747
+
748
+ return ok
749
+ end
750
+ end