summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/context/ruby/wwwclient.rb
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/scripts/context/ruby/wwwclient.rb')
-rw-r--r--Master/texmf-dist/scripts/context/ruby/wwwclient.rb677
1 files changed, 677 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/context/ruby/wwwclient.rb b/Master/texmf-dist/scripts/context/ruby/wwwclient.rb
new file mode 100644
index 00000000000..8f5451a8dcf
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/wwwclient.rb
@@ -0,0 +1,677 @@
+#!/usr/bin/env ruby
+
+# a direct request is just passed on
+#
+# exaclient --direct --request=somerequest.exa --result=somefile.pdf
+#
+# in an extended request the filename in the template file is replaced by the filename
+# given on the command line; templates are located on the current path and at parent
+# directories (two levels); the filename is expanded to a full path
+#
+# exaclient --extend --template=tmicare-l-h.exa --file=somefile.xml --result=somefile.pdf
+#
+# a constructed request is build out of the provided filename and action; the filename is
+# expanded to a full path
+#
+# exaclient --construct --action=tmicare-s-h.exa --file=somefile.xml --result=somefile.pdf
+#
+# in all cases, the result is either determined by a switch or taken from a reply file
+
+banner = ['WWWClient', 'version 1.0.0', '2003-2006', 'PRAGMA ADE/POD']
+
+$: << File.dirname(File.expand_path($0))
+
+require 'base/switch'
+require 'base/logger'
+
+require 'timeout'
+require 'thread'
+require 'rexml/document'
+require 'net/http'
+
+class File
+
+ def File.backtracked(filename,level=3)
+ if level > 0 && filename && ! filename.empty? then
+ if FileTest.file?(filename) then
+ filename
+ else
+ File.backtracked('../'+filename,level-1)
+ end
+ else
+ filename
+ end
+ end
+
+ def File.expanded(filename)
+ File.expand_path(filename)
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+end
+
+class Commands
+
+ @@namespace = "xmlns:exa='http://www.pragma-ade.com/schemas/example.rng'"
+ @@randchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "abcdefghijklmnopqrstuvwxyz"
+
+ def traceback
+ "(error: #{$!})" + "\n -- " + $@.join("\n >>")
+ end
+
+ def pdf(action,filename,enabled)
+ if enabled && FileTest.file?(filename) then
+ begin
+ report("pdf action #{action} on #{filename}")
+ case action
+ when 'close' then system("pdfclose --all")
+ when 'open' then system("pdfopen --file #{filename}")
+ end
+ rescue
+ # forget about it
+ end
+ end
+ end
+
+ def status(replyfile,str) # when block, then ok
+ begin
+ # def status(*whatever)
+ # end
+ File.open(replyfile,'w') do |f|
+ report("saving reply info in '#{replyfile}'")
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<exa:reply #{@@namespace}>\n")
+ if block_given? then
+ f.puts(" <exa:status>ok</exa:status>\n")
+ f.puts(" #{yield}\n")
+ else
+ f.puts(" <exa:status>error</exa:status>\n")
+ end
+ f.puts(" <exa:comment>" + str + "</exa:comment>\n")
+ f.puts("</exa:reply>\n")
+ f.close
+ report("saving status: #{str}")
+ end
+ rescue
+ report("saving reply info in '#{replyfile}' fails")
+ ensure
+ exit
+ end
+ exit # to be real sure
+ end
+
+
+ def boundary_string (length) # copied from webrick/utils
+ rand_max = @@randchars.size
+ ret = ""
+ length.times do
+ ret << @@randchars[rand(rand_max)]
+ end
+ ret.upcase
+ end
+
+end
+
+class Commands
+
+ @@connecttimeout = 10*60 # ten minutes
+ @@processtimeout = 60*60 # an hour
+ @@polldelay = 5 # 5 seconds
+
+ def main
+
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ dataaction = @commandline.option('action')
+
+ if ! datatemplate.empty? then
+ report("template '#{datatemplate}' specified without --construct")
+ report("aborting")
+ elsif ! dataaction.empty? then
+ report("action data '#{dataaction}' specified without --construct or --extend")
+ report("aborting")
+ elsif ! datafile.empty? then
+ report("action file '#{datafile}' specified without --construct or --extend")
+ report("aborting")
+ else
+ report("assuming --direct")
+ direct()
+ end
+
+ end
+
+ def construct
+
+ requestfile = @commandline.option('request')
+ replyfile = @commandline.option('reply')
+
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ dataaction = @commandline.option('action')
+
+ domain = @commandline.option('domain')
+ project = @commandline.option('project')
+ username = @commandline.option('username')
+ password = @commandline.option('password')
+
+ threshold = @commandline.option('threshold')
+
+ datablob = ''
+
+ begin
+ datablob = IO.read(datatemplate)
+ rescue
+ datablob = ''
+ else
+ begin
+ request = REXML::Document.new(datablob)
+ if e = REXML::XPath.match(request.root,"/exa:request/exa:data") then
+ datablob = e.to_s.chomp
+ end
+ rescue
+ datablob = ''
+ end
+ end
+
+ begin
+ File.open(requestfile,'w') do |f|
+ f.puts "<?xml version='1.0'?>\n"
+ f.puts "<exa:request #{@@namespace}>\n"
+ f.puts " <exa:application>\n"
+ f.puts " <exa:action>#{dataaction}</exa:action>\n" unless dataaction.empty?
+ f.puts " <exa:filename>#{datafile}</exa:filename>\n" unless datafile.empty?
+ f.puts " <exa:threshold>#{threshold}</exa:threshold>\n" unless threshold.empty?
+ f.puts " </exa:application>\n"
+ f.puts " <exa:client>\n"
+ f.puts " <exa:domain>#{domain}</exa:domain>\n"
+ f.puts " <exa:project>#{project}</exa:project>\n"
+ f.puts " <exa:username>#{username}</exa:username>\n"
+ f.puts " <exa:password>#{password}</exa:password>\n"
+ f.puts " </exa:client>\n"
+ if datablob.empty? then
+ f.puts " <exa:data/>\n"
+ else
+ f.puts " #{datablob.chomp}\n"
+ end
+ f.puts "</exa:request>"
+ end
+ rescue
+ status(replyfile,"unable to create '#{requestfile}'")
+ end
+
+ direct()
+
+ end
+
+ def extend
+
+ requestfile = @commandline.option('request')
+ replyfile = @commandline.option('reply')
+
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ dataaction = @commandline.option('action')
+
+ threshold = @commandline.option('threshold')
+
+ if datatemplate.empty? then
+ status(replyfile,"invalid data template '#{datatemplate}'")
+ else
+ begin
+ if FileTest.file?(datatemplate) && oldrequest = IO.read(datatemplate) then
+ request, done = REXML::Document.new(oldrequest), false
+ if ! threshold.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application/exa:threshold") then
+ e.text, done = threshold, true
+ end
+ if ! dataaction.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application/exa:action") then
+ e.text, done = dataaction, true
+ end
+ if ! datafile.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application/exa:filename") then
+ e.text, done = datafile, true
+ end
+ #
+ if ! threshold.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application") then
+ e = e.add_element('exa:threshold')
+ e.add_text(threshold.to_s)
+ done = true
+ end
+ #
+ report("nothing replaced in template file") unless done
+ begin
+ File.open(requestfile,'w') do |f|
+ f.puts(newrequest.to_s)
+ end
+ rescue
+ status(replyfile,"unable to create '#{requestfile}'")
+ end
+ else
+ status(replyfile,"unable to read data template '#{datatemplate}'")
+ end
+ rescue
+ status(replyfile,"unable to handle data template '#{datatemplate}'")
+ end
+ end
+
+ direct()
+
+ end
+
+ def direct
+
+ requestpath = @commandline.option('path')
+ requestfile = @commandline.option('request')
+ replyfile = @commandline.option('reply')
+ resultfile = @commandline.option('result')
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ threshold = @commandline.option('threshold')
+ address = @commandline.option('address')
+ port = @commandline.option('port')
+ session_id = @commandline.option('session')
+ exaurl = @commandline.option('exaurl')
+
+ exaurl = "/#{exaurl}" unless exaurl =~ /^\//
+
+ address.sub!(/^http\:\/\//io) do
+ ''
+ end
+ address.sub!(/\:(\d+)$/io) do
+ port = $1
+ ''
+ end
+
+ autopdf = @commandline.option('autopdf')
+
+ dialogue = nil
+
+ resultfile.sub!(/\.[a-z]+?$/, '') # don't overwrite the source
+
+ unless requestpath.empty? then
+ begin
+ if FileTest.directory?(requestpath) then
+ if Dir.chdir(requestpath) then
+ report("gone to path '#{requestpath}'")
+ else
+ status(replyfile,"unable to go to path '#{requestpath}")
+ end
+ else
+ status(replyfile,"unable to locate '#{requestpath}'")
+ end
+ rescue
+ status(replyfile,"unable to handle '#{requestpath}'")
+ end
+ end
+
+ datafile = File.expand_path(datafile) unless datafile.empty?
+ datatemplate = File.backtracked(datatemplate,3) unless datatemplate.empty?
+
+ # request must be valid
+
+ status(replyfile,'no request file') if requestfile.empty?
+ status(replyfile,"invalid request file '#{requestfile}'") unless FileTest.file?(requestfile)
+
+ begin
+ request = IO.readlines(requestfile).join('')
+ request = REXML::Document.new(request)
+ status(replyfile,'invalid request (no request)') unless request.root.fully_expanded_name=='exa:request'
+ status(replyfile,'invalid request (no application block)') unless request.elements['exa:request'].elements['exa.application'] == nil # explicit nil test needed
+ rescue REXML::ParseException
+ status(replyfile,'invalid request (invalid xml file)')
+ rescue
+ status(replyfile,'invalid request (invalid file)')
+ else
+ report("using request file '#{requestfile}'")
+ end
+
+ # request can force session_id
+
+ if session_id && session_id.empty? then
+ begin
+ id = request.elements['exa:request'].elements['exa:application'].elements['exa:session'].text
+ rescue Exception
+ id = ''
+ ensure
+ if id && ! id.empty? then
+ session_id = id
+ end
+ end
+ end
+
+ # request can overload reply name
+
+ begin
+ rreplyfile = request.elements['exa:request'].elements['exa:application'].elements['exa:output'].text
+ rescue Exception
+ rreplyfile = nil
+ ensure
+ if rreplyfile && ! rreplyfile.empty? then
+ replyfile = rreplyfile
+ report("reply file '#{replyfile} set by request'")
+ else
+ report("using reply file '#{replyfile}'")
+ end
+ end
+
+ # request can overload result name
+
+ begin
+ rresultfile = request.elements['exa:request'].elements['exa:application'].elements['exa:result']
+ rescue Exception
+ rresultfile = nil
+ ensure
+ if rresultfile && ! rresultfile.empty? then
+ resultfile = rresultfile
+ report("result file '#{resultfile}' set by request")
+ else
+ report("using result file '#{resultfile}'")
+ end
+ end
+
+ # try to connect to server
+
+ start_time = Time.now
+
+ processtimeout = begin @commandline.option('timeout').to_i rescue @@processtimeout end
+ processtimeout = @@processtimeout if processtimeout == 0 # 'xx'.to_i => 0
+
+ dialogue = start_dialogue(address, port, processtimeout)
+
+ if dialogue then
+ # continue
+ else
+ status(replyfile,'no connection')
+ end
+
+ # post request
+
+ timeout (@@processtimeout-10) do # -10 so that we run into this one first
+ begin
+ report("posting request of type '#{exaurl}'")
+ report("using session id '#{session_id}'") if session_id && ! session_id.empty?
+ firstline, chunks, total = nil, 0, 0
+ body, boundary, crlf = '', boundary_string(32), "\x0d\x0a"
+ body << '--' + boundary + crlf
+ body << "Content-Disposition: form-data; name=\"exa:request\""
+ body << crlf
+ body << "Content-Type: text/plain"
+ body << crlf + crlf
+ body << request.to_s
+ body << crlf + '--' + boundary + crlf
+if session_id && ! session_id.empty? then
+ body << "Content-Disposition: form-data; name=\"exa:session\""
+ body << "Content-Type: text/plain"
+ body << crlf + crlf
+ body << session_id
+ body << crlf + '--' + boundary + crlf
+end
+ begin
+ File.open(datafile,'rb') do |df|
+ body << "Content-Disposition: form-data; name=\"filename\""
+ body << "Content-Type: text/plain"
+ body << crlf + crlf
+ body << datafile
+ body << crlf + '--' + boundary + crlf
+ body << "Content-Disposition: form-data; name=\"fakename\" ; filename=\"#{datafile}\""
+ body << "Content-Type: application/octetstream"
+ body << "Content-Transfer-Encoding: binary"
+ body << crlf + crlf
+ body << df.read
+ body << crlf + '--' + boundary + '--' + crlf
+ end
+ rescue
+ # skip
+ end
+ headers = Hash.new
+ headers['content-type'] = "multipart/form-data; boundary=#{boundary}"
+ headers['content-length'] = body.length.to_s
+ begin
+ File.open(resultfile,'wb') do |rf|
+ begin
+ # firstline is max 1024 but ok for reply
+ dialogue.post(exaurl,body,headers) do |str|
+ if ! firstline || firstline.empty? then
+ report('receiving result') if total == 0
+ firstline = str
+ end
+ total += 1
+ rf.write(str)
+ end
+ rescue
+ report("forced close #{traceback}")
+ end
+ end
+ rescue
+ status(replyfile,'cannot open file')
+ end
+ begin
+ File.delete(resultfile) if File.zero?(resultfile)
+ rescue
+ end
+ unless FileTest.file?(resultfile) then
+ report("deleting empty resultfile")
+ begin
+ File.delete(resultfile)
+ rescue
+ # nice try, an error anyway
+ end
+ status(replyfile,'empty file')
+ else
+ n, id, status = 0, '', ''
+ loop do
+ again = false
+ if ! dialogue then
+ again = true
+ elsif firstline =~ /(\<exa:reply)/moi then
+ begin
+ reply = REXML::Document.new(firstline)
+ id = (REXML::XPath.match(reply.root,"/exa:reply/exa:session/text()") || '').to_s
+ status = (REXML::XPath.match(reply.root,"/exa:reply/exa:status/text()") || '').to_s
+ rescue
+ report("error in parsing reply #{traceback}")
+ break
+ else
+ report("status: #{status}")
+ if (status =~ /^running\s*\:\s*(background|busy)$/i) && (! id.empty?) then
+ report("waiting for status reply (#{n*@@polldelay})")
+ again = true
+ end
+ end
+ end
+ if again then
+ n += 1
+ sleep(@@polldelay) # todo: duplicate when n > 1
+ unless dialogue then
+ report('reestablishing connection')
+ dialogue = start_dialogue(address, port, processtimeout)
+ end
+ if dialogue then
+ begin
+ File.open(resultfile,'wb') do |rf|
+ begin
+ body = "id=#{id}"
+ headers = Hash.new
+ headers['content-type'] = "application/x-www-form-urlencoded"
+ headers['content-length'] = body.length.to_s
+ total, firstline = 0, ''
+ dialogue.post("/exastatus",body,headers) do |str|
+ if ! firstline || firstline.empty? then
+ firstline = str
+ end
+ total += 1
+ rf.write(str)
+ end
+ rescue
+ report("forced close #{traceback}")
+ dialogue = nil
+ again = true
+ end
+ end
+ begin
+ File.delete(resultfile) if File.zero?(resultfile)
+ rescue
+ end
+ rescue
+ report("error in opening file #{traceback}")
+ status(replyfile,'cannot open file')
+ end
+ else
+ report("unable to make a connection")
+ status(replyfile,'unable to make a connection') # exit
+ end
+ else
+ break
+ end
+ end
+ case firstline
+ when /<\?xml\s*version=.*?\?>\s*<exa:reply/moi then
+ begin
+ File.delete(replyfile) if FileTest.file?(replyfile)
+ resultfile = replyfile if File.rename(resultfile,replyfile)
+ rescue
+ end
+ report("reply saved in '#{resultfile}'")
+ when /\%PDF\-/io then
+ report("done, file #{resultfile}, type pdf, #{total} chunks, #{File.size? rescue 0} bytes")
+ if resultfile =~ /\.pdf$/i then
+ report("file identified as 'pdf'")
+ elsif resultfile =~ /\..*$/o
+ report("result file suffix should be 'pdf'")
+ else
+ newresultfile = resultfile + '.pdf'
+ newresultfile.sub!(/\.pdf\.pdf/io, '.pdf')
+ pdf('close',newresultfile,autopdf)
+ begin
+ File.delete(newresultfile) if FileTest.file?(newresultfile)
+ resultfile = newresultfile if File.rename(resultfile,newresultfile)
+ rescue
+ report("adding 'pdf' suffix to result name failed")
+ else
+ report("'pdf' suffix added to result name")
+ end
+ end
+ report("result saved in '#{resultfile}'")
+ pdf('open',resultfile,autopdf)
+ status(replyfile,'ok') do
+ "<exa:filename>#{resultfile}</exa:filename>"
+ end
+ when /html/io then
+ report("done, file #{resultfile}, type html, #{total} chunks, #{File.size? rescue 0} bytes")
+ if resultfile =~ /\.(htm|html)$/i then
+ report("file identified as 'html'")
+ elsif resultfile =~ /\..*$/o
+ report("result file suffix should be 'htm'")
+ else
+ newresultfile = resultfile + '.htm'
+ begin
+ File.delete(newresultfile) if FileTest.file?(newresultfile)
+ resultfile = newresultfile if File.rename(resultfile,newresultfile)
+ rescue
+ report("adding 'htm' suffix to result name failed")
+ else
+ report("'htm' suffix added to result name")
+ end
+ end
+ report("result saved in '#{resultfile}'")
+ status(replyfile,'ok') do
+ "<exa:filename>#{resultfile}</exa:filename>"
+ end
+ else
+ report("no result file, first line #{firstline}")
+ status(replyfile,'no result file')
+ end
+ end
+ rescue TimeoutError
+ report("aborted due to time out")
+ status(replyfile,'time out')
+ rescue
+ report("aborted due to some problem #{traceback}")
+ status(replyfile,"no answer #{traceback}")
+ end
+ end
+
+ begin
+ report("run time: #{Time.now-start_time} seconds")
+ rescue
+ end
+
+ end
+
+ def start_dialogue(address, port, processtimeout)
+ timeout(@@connecttimeout) do
+ report("trying to connect to #{address}:#{port}")
+ begin
+ begin
+ if dialogue = Net::HTTP.new(address, port) then
+ # dialogue.set_debug_output $stderr
+ dialogue.read_timeout = processtimeout # set this before start
+ if dialogue.start then
+ report("connected to #{address}:#{port}, timeout: #{processtimeout}")
+ else
+ retry
+ end
+ else
+ retry
+ end
+ rescue
+ sleep(2)
+ retry
+ else
+ return dialogue
+ end
+ rescue TimeoutError
+ return nil
+ rescue
+ return nil
+ end
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registerflag('autopdf')
+
+commandline.registervalue('path' , '')
+
+commandline.registervalue('request' , 'request.exa')
+commandline.registervalue('reply' , 'reply.exa')
+commandline.registervalue('result' , 'result')
+
+commandline.registervalue('template' , '')
+commandline.registervalue('file' , '')
+commandline.registervalue('action' , '')
+commandline.registervalue('timeout' , '')
+
+commandline.registervalue('domain' , 'default')
+commandline.registervalue('project' , 'default')
+commandline.registervalue('username' , 'guest')
+commandline.registervalue('password' , 'anonymous')
+commandline.registervalue('exaurl' , 'exarequest')
+commandline.registervalue('threshold' , '0')
+commandline.registervalue('session' , '')
+
+commandline.registervalue('address' , 'localhost')
+commandline.registervalue('port' , '80')
+
+commandline.registeraction('direct' , '[--path --request --reply --result --autopdf]')
+commandline.registeraction('construct', '[--path --request --reply --result --autopdf] --file --action')
+commandline.registeraction('extend' , '[--path --request --reply --result --autopdf] --file --action --template')
+
+commandline.registeraction('direct')
+commandline.registeraction('construct')
+commandline.registeraction('extend')
+
+commandline.registerflag('verbose')
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')