frames 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. #!/usr/bin/ruby
  2. #
  3. # This file is part of centurio.work/ing/commands.
  4. #
  5. # centurio.work/ing/commands is free software: you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or (at your
  8. # option) any later version.
  9. #
  10. # centurio.work/ing/commands is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  13. # Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with
  16. # centurio.work/ing/commands (file COPYING in the main directory). If not, see
  17. # <http://www.gnu.org/licenses/>.
  18. require 'rubygems'
  19. require 'json'
  20. require 'xml/smart'
  21. require 'riddl/server'
  22. require 'fileutils'
  23. require 'typhoeus'
  24. class Get < Riddl::Implementation
  25. def response
  26. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  27. end
  28. end
  29. class Put < Riddl::Implementation
  30. def response
  31. Dir.mkdir(File.join('data',@r.last)) rescue nil
  32. File.write(File.join('data',@r.last,'num'),@p[0].value)
  33. File.write(File.join('data',@r.last,'total'),@p[1].value)
  34. File.write(File.join('data',@r.last,'style.url'),@p[2].value)
  35. image = Typhoeus.get(@p[3].value).response_body rescue '<image/>'
  36. File.write(File.join('data',@r.last,'image.xml'),image)
  37. if @p[4]&.name == 'errors_url'
  38. errors = Typhoeus.get(@p[4].value).response_body rescue '<reasons/>'
  39. File.write(File.join('data',@r.last,'errors.xml'),errors)
  40. end
  41. if @p[4]&.name == 'info'
  42. File.write(File.join('data',@r.last,'info.json'),@p[4].value)
  43. end
  44. if @p[5]&.name == 'info'
  45. File.write(File.join('data',@r.last,'info.json'),@p[5].value)
  46. end
  47. File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
  48. @a[0].send('new')
  49. nil
  50. end
  51. def headers
  52. Riddl::Header.new('CPEE-CALLBACK', 'true')
  53. end
  54. end
  55. class Delete < Riddl::Implementation
  56. def response
  57. if cbu = File.read(File.join('data',@r.last,'callback'))
  58. send = { 'operation' => @p[0].value }
  59. case send['operation']
  60. when 'jump'
  61. send['target'] = @p[1].value
  62. when 'error'
  63. send['reason'] = @p[1].value
  64. end
  65. cbu += '/' unless cbu[-1] == '/'
  66. Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
  67. end
  68. File.unlink(File.join('data',@r.last,'callback')) rescue nil
  69. File.unlink(File.join('data',@r.last,'num')) rescue nil
  70. File.unlink(File.join('data',@r.last,'total')) rescue nil
  71. File.unlink(File.join('data',@r.last,'style.url')) rescue nil
  72. File.unlink(File.join('data',@r.last,'image.xml')) rescue nil
  73. File.unlink(File.join('data',@r.last,'errors.xml')) rescue nil
  74. if @p[0].value == 'finish' || @p[0].value == 'error'
  75. @a[0].send('reset')
  76. end
  77. nil
  78. end
  79. end
  80. class GetInfo < Riddl::Implementation #{{{
  81. def response
  82. fname = File.join('data',@r[-2],'info.json')
  83. if File.exists? fname
  84. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  85. else
  86. @status = 404
  87. end
  88. end
  89. end #}}}
  90. class GetLangs < Riddl::Implementation #{{{
  91. def response
  92. fname = File.join('data',@r[-2],'image.xml')
  93. if File.exists? fname
  94. doc = XML::Smart.open_unprotected(fname)
  95. ndoc = XML::Smart.string('<languages/>')
  96. doc.find('//@lang').each do |e|
  97. ndoc.root.add('language',e.value)
  98. end
  99. Riddl::Parameter::Complex.new('value','text/xml',ndoc.to_s)
  100. else
  101. @status = 404
  102. end
  103. end
  104. end #}}}
  105. class GetStyle < Riddl::Implementation #{{{
  106. def response
  107. fname = File.join('data',@r[-2],'style.url')
  108. if File.exists? fname
  109. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  110. else
  111. @status = 404
  112. end
  113. end
  114. end #}}}
  115. class GetDocument < Riddl::Implementation #{{{
  116. def response
  117. fname = File.join('data',@r[-3],'image.xml')
  118. if File.exists? fname
  119. doc = XML::Smart.open_unprotected(fname)
  120. val = nil
  121. doc.find("//variant[@lang='#{@r[-1]}']").each do |e|
  122. val = e.text
  123. end
  124. if val
  125. Riddl::Parameter::Complex.new('url','text/plain',val.strip)
  126. else
  127. @status = 404
  128. end
  129. else
  130. @status = 404
  131. end
  132. end
  133. end #}}}
  134. class SSE < Riddl::SSEImplementation #{{{
  135. def onopen
  136. signals = @a[0]
  137. signals.add self
  138. send 'started'
  139. end
  140. def onclose
  141. signals = @a[0]
  142. signals.remove self
  143. nil
  144. end
  145. end #}}}
  146. class Signaling # {{{
  147. def initialize
  148. @binding = []
  149. end
  150. def add(binding)
  151. @binding << binding
  152. end
  153. def remove(binding)
  154. @binding.delete(binding)
  155. end
  156. def length
  157. @binding.length
  158. end
  159. def send(value)
  160. @binding.each do |b|
  161. b.send(value)
  162. end
  163. end
  164. end #}}}
  165. server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
  166. accessible_description true
  167. cross_site_xhr true
  168. opts[:signals] = {}
  169. parallel do
  170. loop do
  171. opts[:signals].each do |k,v|
  172. v.send('keepalive')
  173. end
  174. sleep 5
  175. end
  176. end
  177. on resource do
  178. on resource do |r|
  179. idx = r[:r][0]
  180. opts[:signals][idx] ||= Signaling.new
  181. run Get if get
  182. run Put, opts[:signals][idx] if put 'input'
  183. run Delete, opts[:signals][idx] if delete 'opa'
  184. run Delete, opts[:signals][idx] if delete 'opb'
  185. on resource 'sse' do
  186. run SSE, opts[:signals][idx] if sse
  187. end
  188. on resource 'languages' do
  189. run GetLangs if get
  190. end
  191. on resource 'style.url' do
  192. run GetStyle if get
  193. end
  194. on resource 'info.json' do
  195. run GetInfo if get
  196. end
  197. on resource 'documents' do
  198. on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do
  199. run GetDocument if get
  200. end
  201. end
  202. end
  203. end
  204. end.loop!