#!/usr/bin/env ruby # This is just a simple environment for remote processing of context # files. It's not a framework, nor an example of how that should be done. # Nowadays there are environments like Rails or Nitro. Maybe some day I'll # give one of them a try. # # # # we make limited use of cgi methods because we also need to handle webrick # %var% as well as $(var) are supported # paths need to be expanded before they enter apache, since .. is not # handled by default require 'base/variables' require 'ftools' require 'fileutils' require 'tempfile' require 'timeout' require 'md5' require 'digest/md5' require 'cgi' # we also need escaping for webrick (could move it here) # beware, namespaces have to match ! module XML def XML::element(tag,attributes=nil) if attributes.class == Hash then if block_given? then XML::element(tag,XML::attributes(attributes)) do yield end else XML::element(tag,XML::attributes(attributes)) end else if block_given? then "<#{tag}#{if attributes && ! attributes.empty? then ' ' + attributes end}>#{yield}" else "<#{tag}#{if attributes && ! attributes.empty? then ' ' + attributes end}/>" end end end def XML::attributes(hash) str = '' hash.each do |k,v| str << ' ' unless str.empty? if v =~ /\'/ then str << "#{k}=\"#{v}\"" else str << "#{k}=\'#{v}\'" end end return str end def XML::create(version='1.0') "#{yield || ''}" end def XML::line "\n" end end # str = # XML::create do # XML::element('test') do # XML::element('test') do # 'text a' # end + # XML::element('test',XML::attributes({'a'=>'b'})) do # XML::element('nested',XML::attributes({'a'=>'b'})) do # 'text b-1' # end + # XML::element('nested',XML::attributes({'a'=>'b'})) do # 'text b-2' # end # end + # XML::element('nested',{'a'=>'b'}) do # 'text c' # end # end # end class ExtendedHash DEFAULT = 'default' @@re_default = /^(default|)$/i def default?(key) self[key] =~ @@re_default rescue true # unset, empty or 'default' end def default(key) self[key] = DEFAULT end def match?(key,value) value == '*' || value == self[key] end end class WWW @@session_prefix = '' @@data_file = 'example.cfg' @@session_max_age = 60*60 @@watch_delay = 30 @@send_threshold = 2*1024*1024 @@admin_refresh = 10 @@namespace = "http://www.pragma-ade.com/schemas/example.rng" @@re_bar = /\s*\|\s*/ @@re_lst = /\s*\,\s*/ @@re_var_a = /\%(.*?)\%/ @@re_var_b = /\$\((.*?)\)/ attr_reader :variables attr_writer :variables @@paths = [ 'configurations', 'data', 'distributions', 'documents', 'interfaces', 'logs', 'resources', 'runners', 'scripts', 'templates', 'work'] @@re_true = /^\s*(YES|ON|TRUE|1)\s*$/io @@re_false = /^\s*(NO|OFF|FALSE|0)\s*$/io def initialize(webrick_daemon=nil,webrick_request=nil,webrick_response=nil) @session_id, @session_file = '', '' @cgi, @cgi_cookie = nil, nil @webrick_daemon, @webrick_request, @webrick_response = webrick_daemon, webrick_request, webrick_response @interface = ExtendedHash.new @variables = ExtendedHash.new @session = ExtendedHash.new @checked = false analyze_request() update_interface() @interface.set('template:message' , 'exalogin-template.htm') @interface.set('template:status' , 'exalogin-template.htm') @interface.set('template:login' , 'exalogin.htm') @interface.set('process:timeout' , @@session_max_age) @interface.set('process:threshold' , @@send_threshold) @interface.set('process:background', 'yes') # this demands a watchdog being active @interface.set('process:indirect' , 'no') # indirect download, no direct feed @interface.set('process:autologin' , 'yes') # provide default interface when applicable @interface.set('process:exaurl' , '') # this one will be used as replacement in templates @interface.set('trace:run' , 'no') @interface.set('trace:errors' , 'no') @interface.set('process:os' , platform) @interface.set('process:texos' , 'texmf-' + platform) @interface.set('trace:run' , 'yes') if (ENV['EXA_TRACE_RUN'] || '') =~ @@re_true @interface.set('trace:errors' , 'yes') if (ENV['EXA_TRACE_ERRORS'] || '') =~ @@re_true yield self if block_given? end def set(key,value) @interface.set(key,value) end def get(key) @interface.get(key,value) end def platform case RUBY_PLATFORM when /(mswin|bccwin|mingw|cygwin)/i then 'mswin' when /(linux)/i then 'linux' when /(netbsd|unix)/i then 'unix' when /(darwin|rhapsody|nextstep)/i then 'macosx' else 'unix' end end def check_cgi # when mod_ruby is used, we need to close # the cgi session explicitly unless @webrick_request then unless @cgi then @cgi = CGI.new('html4') at_exit do begin @cgi.close rescue end end end end end def request_variable(key) begin if @webrick_request then [@webrick_request.query[key]].flatten.first.to_s else check_cgi [@cgi.params[key]].flatten.first.to_s end rescue '' end end def request_cookie(key) begin if @cgi then if str = @cgi.cookies[key] then return str.first || '' end elsif @webrick_request then @webrick_request.cookies.flatten.each do |cookie| if cookie.name == key then return cookie.value unless cookie.value.empty? end end end rescue end return '' end def analyze_request if @webrick_request then @interface.set('path:docroot', @webrick_daemon.config[:DocumentRoot] || './documents') @interface.set('process:uri', @webrick_request.request_uri.to_s) # @interface.set('process.url', [@webrick_request.host,@webrick_request.request_port].join(':')) @cgi = nil @webrick_request.query.each do |key, value| # todo: filename @variables.set(key, [value].flatten.first) end else @interface.set('path:docroot', ENV['DOCUMENT_ROOT'] || './documents') @interface.set('process:uri', ENV['REQUEST_URI'] || '') # @interface.set('process.url', [ENV['SERVER_NAME'],ENV['SERVER:PORT']].join(':')) ARGV[0] = '' # get rid of terminal mode check_cgi # quite fragile, due to changes between 1.6 and 1.8 @cgi.params.keys.each do |p| if @cgi[p].respond_to?(:original_filename) then @interface.set('log:method','post') if @cgi[p].original_filename && ! @cgi[p].original_filename.empty? then @variables.set(p, File.basename(@cgi[p].original_filename)) else case @cgi.params[p].class when StringIO.class then @variables.set(p, @cgi[p].read) when Array.class then @variables.set(p, @cgi[p].first.to_s) when String.class then @variables.set(p, @cgi[p]) when Tempfile.class then @variables.set(p, '[data blob]') end end else @interface.set('log:method','get') unless @interface.get('log:method') == 'post' @variables.set(p, [@cgi.params[p]].flatten.first.to_s) end end end @interface.set('path:root', File.dirname(@interface.get('path:docroot'))) end # name in calling script takes precedence # one can set template:whatever as well # todo: in config def check_template_file(tag='',filename='exalogin-template.htm') @interface.set('file:template', filename) if @interface.get('file:template').empty? @interface.set('tag:template', tag) @interface.set('file:template', @interface.get('tag:template')) unless @interface.get('tag:template').empty? end def update_interface() root = @interface.get('path:docroot') @interface.set('path:docroot', File.expand_path("#{root}")) @@paths.each do |path| @interface.set("path:#{path}", File.expand_path("#{root}/../#{path}")) end @interface.set('file:template', @interface.get('tag:template')) unless @interface.get('tag:template').empty? end def indirect?(result) size = FileTest.size?(result) || 0 @interface.true?('trace:errors') || @interface.true?('trace:run') || @interface.true?('process:indirect') || ((! @interface.empty?('process:threshold')) && (size > @interface.get('process:threshold').to_i)) || ((! @session.empty?('threshold')) && (size > @session.get('threshold').to_i)) end end # files class WWW def sesname File.basename(@session_file) end def dirname File.basename(@session_file.sub(/ses$/,'dir')) end def lckname File.basename(@session_file.sub(/ses$/,'lck')) end def work_root(expand=true) p = if expand then File.expand_path(@interface.get('path:work')) else @interface.get('path:work') end if @interface.true?('process:background') then File.join(@interface.get('path:work'),'watch') else File.join(@interface.get('path:work'),'direct') end end def work_roots(expand=true) p = if expand then File.expand_path(@interface.get('path:work')) else @interface.get('path:work') end [File.join(@interface.get('path:work'),'watch'),File.join(@interface.get('path:work'),'direct')] end def cache_root(expand=true) p = if expand then File.expand_path(@interface.get('path:work')) else @interface.get('path:work') end File.join(@interface.get('path:work'),'cache') end def cleanup_path(dir) FileUtils::rm_r(pth) rescue false end def tmp_path(dir) @interface.set('path:templates', File.expand_path(@interface.get('path:templates'))) # to be sure; here ? ? ? pth = File.join(work_root,dir) File.makedirs(pth) rescue false pth end def locked?(lck) FileTest.file?(lck) end end # sessions class WWW @@session_tags = ['id','domain','project','username','password','gui','path','process','command','filename','action','status', 'starttime','endtime','runtime','task','option','threshold','url'].sort @@session_keep = ['id','domain','project','username','password','process'].sort @@session_reset = @@session_tags - @@session_keep def new_session() if @variables.empty?('exa:session') then @session_id = new_session_id else @session_id = @variables.get('exa:session') end if @session_id == 'default' then # ??? @session_id = new_session_id end @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses") register_session return @session_id end def reset_session(all=false) (if all then @@session_tags else @@session_reset end).each do |k| @session.set(k) end end def valid_session @session_id = request_variable('id') if @session_id.empty? then begin if @cgi then if @session_id = @cgi.cookies['session_id'] then @session_id = @session_id.first || '' else @session_id = '' end elsif @webrick_request then @webrick_request.cookies.flatten.each do |cookie| if cookie.name == 'session_id' then unless cookie.value.empty? then @session_id = cookie.value # break end end end else @session_id = '' end rescue @interface.set('log:session',"[error in request #{$!}]") return false end end if @session_id.empty? then @interface.set('log:session','[no id, check work dir permissions]') return false else @interface.set('log:session',@session_id) load_session if ! @session.empty?('domain') && ! @session.empty?('project') && ! @session.empty?('username') then register_session return @session_id else return false end end end def touch_session(id=nil) begin t = Time.now File.utime(t,t,File.join(work_root,"#{@@session_prefix}#{id || @session_id}.ses")) rescue false rescue false end end def forced_session @session_id = new_session if @session_id.empty? then @interface.set('log:session','[no id, check work dir permissions]') return false else return check_session end end def client_session request, done = @variables.get('exa:request'), false request.sub!(/(^.*]*>.*?)\s*\s*(.*)\s*<\/exa:client>\s*(.*?<\/exa:request>.*$)/mio) do pre, client, post = $1, $2, $3 client.scan(/(.*?)<\/exa:\1>/mio) do @variables.set($1, $2) end done = true pre + post end if done then return forced_session else return nil end end def register_session if @cgi then @cgi_cookie = CGI::Cookie::new( 'name' => 'session_id', 'value' => @session_id, 'expires' => Time.now + @interface.get('process:timeout').to_i ) # @cgi_cookie = CGI::Cookie::new('session_id',@session_id) elsif @webrick_response then if cookie = WEBrick::Cookie.new('session_id', @session_id) then cookie.expires = Time.now + @interface.get('process:timeout').to_i cookie.max_age = @interface.get('process:timeout').to_i cookie.comment = 'exa identifier' @webrick_response.cookies.clear @webrick_response.cookies << cookie end end end def new_session_id # taken from cgi md5 = Digest::MD5::new now = Time::now md5.update(now.to_s) md5.update(String(now.usec)) md5.update(String(rand(0))) md5.update(String($$)) md5.update('foobar') @new_session = true md5.hexdigest[0,32] # was 16 end @@hide_passwords = true HIDDEN = 'hidden' def same_passwords(password) # password in cfg file if @@hide_passwords && (@session.get('password') == HIDDEN) && (@session_id == @session.get('id')) then # this condition is only true when a same session id is found and # the password is checked once and set to HIDDEN same = true elsif password =~ /^MD5:/ then # so, one cannot send a known encrypted password since it will be # encrypted twice then same = (password == "MD5:" + MD5.new(@session.get('password')).hexdigest.upcase) else if (@session.default?('domain') && @session.default?('project') && @session.default?('username')) then @session.default('password') # is this safe enough? end same = (password == @session.get('password')) end if @@hide_passwords && same then @session.set('password', HIDDEN) save_session # next time this session is ok anyway end return same end @@session_line = /^\s*(?![\#\%])(.*?)\s*\=\s*(.*?)\s*$/o @@session_begin = 'begin exa session' @@session_end = 'end exa session' def loaded_session_data(filename) begin if data = IO.readlines(filename) then return data if (data.first =~ /^[\#\%]\s*#{@@session_begin}/o) && (data.last =~ /^[\#\%]\s*#{@@session_end}/o) end rescue end return nil end def load_session() begin @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses") if data = loaded_session_data(@session_file) then data.each do |line| if line =~ @@session_line then @session.set($1, $2 || '') end end else return false end rescue return false else return true end end def load_session_file(filename) begin if data = loaded_session_data(filename) then session = Hash.new data.each do |line| if line =~ @@session_line then session[$1] = $2 || '' end end else Hash.new end rescue Hash.new else session end end def save_session begin unless @session_id.empty? then @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses") @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses") File.open(@session_file,'w') do |f| f << "\# #{@@session_begin}\n" @@session_tags.each do |tag| if @session && @session.key?(tag) then if ! @session.get(tag).empty? then # no one liner, fails f << "#{tag}=#{@session.get(tag)}\n" end elsif @variables.key?(tag) && ! @variables.empty?(key) then f << "#{tag}=#{@variables.get(tag)}\n" end end @session.subset("ENV").keys.each do |tag| f << "#{tag}=#{@session.get(tag)}\n" end f << "\# #{@@session_end}\n" end end rescue return false else return true end end def logged_in_session(force_default=false) if force_default || (@variables.default?('domain') && @variables.default?('project') && @variables.default?('username')) then id = default_session else id = check_session end end def default_session if @interface.true?('process:autologin') then @variables.default('domain') @variables.default('project') @variables.default('username') @variables.default('password') check_session else @session_id = nil end end def check_session @session.set('domain', @variables.get('domain').downcase) @session.set('project', @variables.get('project').downcase) @session.set('username', @variables.get('username').downcase) @session.set('password', @variables.get('password').downcase) new_session @session.set('id', @session_id) save_session return @session_id end def delete_session(id=nil) File.delete(work_root,"#{@@session_prefix}#{id || @session_id}.ses") rescue false end def cleanup_sessions(max_age=nil) begin now, age = Time.now, (max_age||@interface.get('process:timeout')).to_i Dir.glob("{#{work_root},#{cache_root}/#{@@session_prefix}*").each do |s| begin if (now - File.mtime(s)) > age then if FileTest.directory?(s) then FileUtils::rm_r(s) else File.delete(s) end end rescue # maybe purged in the meantime end end rescue # maybe another process is busy end end end # templates class WWW def filled_template(title,text,showtime=false,refresh=0,refreshurl=nil) template = @interface.get("template:#{@interface.get('tag:template')}") template = @interface.get("template:status") if template.empty? fullname = File.join(@interface.get('path:templates'),template) @interface.set('log:templatename',template) @interface.set('log:templatefile',fullname) append_status(text) htmreply = '' if FileTest.file?(fullname) then begin htmreply = IO.read(fullname) rescue htmreply = '' end end if refresh>0 then if refreshurl then metadata = "" else metadata = "" end else metadata = '' end if ! htmreply || htmreply.empty? then # in head: htmreply = <<-EOD #{metadata} #{title}

#{title}

#{Time.now}

#{text} EOD else if showtime then exa_template = "

#{title}

\n

#{Time.now}

\n#{text}\n" else exa_template = "

#{title}

#{text}\n" end htmreply = replace_template_placeholder(htmreply,exa_template,metadata) end htmreply end def message(title,str='',showtime=false,refresh=0,refreshurl=nil) if @cgi then @cgi.out("cookie"=>[@cgi_cookie]) do filled_template(title,str,showtime,refresh,refreshurl) end elsif @webrick_response then @webrick_response['content-type'] = 'text/html' @webrick_response.body = filled_template(title,str,showtime,refresh,refreshurl) else filled_template(title,str,showtime,refresh,refreshurl) end end def plaintext(str) if @cgi then @cgi.out('cookie'=>[@cgi_cookie],'content-type'=>'text/plain') do str end elsif @webrick_response then @webrick_response['content-type'] = 'text/plain' @webrick_response.body = str else str end end def exareply(status='',url='',size='',comment='') exaurl = @interface.get('process:exaurl') str = "\n\n" str << "\n" str << " #{@session_id}\n" unless @session_id.empty? str << " #{status}\n" unless (status || '').empty? str << " #{exaurl}/#{url}\n" unless (url || '').empty? str << " #{size}\n" unless (size || '').empty? str << " #{comment}\n" unless (comment|| '').empty? str << "\n" return str end def append_status(str='') if @interface.true?('trace:errors') then if $! && $@ then str << "


Error:
#{$!}
"
                str << $@.join("\n")
            end
            str << '


' str << status_data str << 'Paths
' str << '
'
            @interface.subset('path:').each do |k,v|
                if FileTest.directory?(v) then
                    if FileTest.writable?(v) then
                        str << "#{v} exists and is writable\n"
                    else
                        str << "#{v} is not writable\n"
                    end
                else
                    str << "#{v} does not exist\n"
                end
            end
            str << '
' end str end def simpleurl(url) if url then url.sub(/(:80|:443)$/,'') else '' end end def replace_exa_placeholders(data) data.gsub(/([\"\'])\@exa\_([a-zA-Z0-9\-\_]+)\1/) do quot, key, value = $1, $2, '' begin value = @variables.get(key) rescue value = '' end quot + value + quot end end def replace_url_placeholder(data) data.gsub!(/(http:\/\/|\/+)*\@exa\_main\_url/, @interface.get('process:exaurl')) replace_exa_placeholders(data) end def replace_template_placeholder(data,template='',metadata='') data.gsub!(/(http:\/\/|\/+)*\@exa\_main\_url/, @interface.get('process:exaurl')) data.gsub!(/\@exa\_template/, template) data.gsub!(/\@exa\_metadata/, metadata) replace_exa_placeholders(data) end def escaped(str) str end end # send files class WWW def send_file(filename,parse=false) # this can take a lot of memory, look for alternative (fastcgi ?) begin if filename =~ /\.pdf$/ then mimetype, parse = 'application/pdf', false elsif filename =~ /\.(html|htm)$/ then mimetype, parse = 'text/html', true else mimetype, parse = 'text/plain', false end if FileTest.file?(filename) then if @webrick_response then begin @webrick_response['content-type'] = mimetype @webrick_response['content-length'] = FileTest.size?(filename) if parse then File.open(filename, 'rb') do |f| @webrick_response.body = replace_url_placeholder(f.read) end else @webrick_response.body = File.open(filename, 'rb') end rescue else return end elsif @cgi then begin # the following works ok, but stores the whole file in memory (see @cgi.out) # # File.open(filename, 'rb') do |f| # @cgi.out('cookie'=>[@cgi_cookie],'connection'=>'close', 'length'=>File.size(filename), 'type'=>mimetype) do # if parse then replace_url_placeholder(f.read) else f.read end # end # end if parse then File.open(filename, 'rb') do |f| @cgi.out('cookie'=>[@cgi_cookie],'connection'=>'close', 'length'=>File.size(filename), 'type'=>mimetype) do replace_url_placeholder(f.read) end end else @cgi.print(@cgi.header('cookie'=>[@cgi_cookie],'connection'=>'close', 'length'=>File.size(filename), 'type'=>mimetype)) File.open(filename, 'rb') do |f| while str = f.gets do @cgi.print(str) end end end rescue else return end end end rescue end message('Error', "There is a problem with sending file #{File.basename(filename)}.") end def send_htmlfile(filename,parse=false) send_file(filename,parse) end def send_pdffile(filename) # this can take a lot of memory, look for alternative (fastcgi ?) send_file(filename,false) end end # tracing class WWW def show_vars(a=@variables,title='') if a && a.length > 0 then if title.empty? then str = '' else str = "#{title}" end str << "
\n"
            a.keys.sort.each do |k|
                if k && a[k] && ! a[k].empty? then
                    if k == 'password' then
                        val = if a[k] == 'default' then 'default' else '******' end
                    else
                        # str << "#{k} => #{a[k].sub(/^\s+/moi,'').sub(/\s+$/moi,'')}\n"
                        val = a[k].to_s.strip
                        val.gsub!("&","&")
                        val.gsub!("<","<")
                        val.gsub!(">",">")
                        val.gsub!("\n","\n    ")
                    end
                    str << "#{k} => #{val}\n"
                end
            end
            str << "

\n" return str else return '' end end def status_data show_vars(@session , 'Session' ) + show_vars(@variables, 'Variables' ) + show_vars(@interface, 'Interface' ) + show_vars(ENV , 'Environment') end def report_status check_template_file('status') message('Status',status_data) end end # attachments class WWW def extract_sent_files(dir) files = Array.new if @cgi then @cgi.params.keys.each do |tag| begin if filename = @cgi[tag].original_filename then files << extract_file_content(dir,filename,@cgi[tag]) unless filename.empty? end rescue end end elsif @webrick_request then @webrick_request.query.keys.each do |tag| begin if filename = @webrick_request.query[tag].filename then files << extract_file_content(dir,filename,@webrick_request.query[tag]) unless filename.empty? end rescue end end end @interface.set('log:attachments', files.compact.uniq.join('|')) end def extract_file_content(dir,filename,data) filename = File.join(dir,File.basename(filename)) begin @interface.set('log:attachclass', data.class.inspect) if data.class == Tempfile then begin File.copy(data.path,filename) rescue begin File.open(filename,'wb') do |f| File.open(data.path,'rb') do |g| while str = g.gets do f.write(str) end end end rescue @interface.set('log:attachstate', "saving tempfile #{filename} failed (#{$!})") else @interface.set('log:attachstate', "tempfile #{filename} has been saved") end else @interface.set('log:attachstate', "#{data.path} copied to #{filename}") end elsif data.class == String then begin File.open(filename,'wb') do |f| f.write(data) end rescue @interface.set('log:attachstate', "saving string #{filename} failed (#{$!})") else @interface.set('log:attachstate', "string #{filename} has been saved") end elsif data.class == StringIO then begin File.open(filename,'wb') do |f| f.write(data.read) end rescue @interface.set('log:attachstate', "saving stringio #{filename} failed (#{$!})") else @interface.set('log:attachstate', "stringio #{filename} has been saved") end else @interface.set('log:attachstate', "unknown attachment class #{data.class.to_s}") end rescue begin File.delete(filename) ; rescue ; end else begin File.delete(filename) if FileTest.size(filename) == 0 ; rescue ; end end return File.basename(filename) end end # configuration class WWW def interface_base_name(str) str.sub(/\.(pdf|htm|html)$/, '') end def located_interface_file(filename) ['configurations', 'runners', 'scripts'].each do |tag| datafile = File.join(@interface.get("path:#{tag}"),filename) if FileTest.file?(datafile+'.encrypted') then return datafile + '.encrypted' elsif FileTest.file?(datafile) then return datafile end end return nil end def load_interface_file(filename=@@data_file) reset_session() # no save yet if datafile = located_interface_file(filename) then nestedfiles = Array.new begin data = IO.read(datafile) || '' unless data.empty? then loop do # we need to load them recursively done = false data.gsub!(/^include\s*:\s*(.*?)\s*$/) do includedname, done = $1, true if nestedname = located_interface_file(includedname) then begin str = ("\n" + IO.read(nestedname) + "\n") || '' rescue nestedfiles << File.basename('-'+includedname) '' else nestedfiles << File.basename('+'+includedname) str end else nestedfiles << File.basename('-'+includedname) '' end end break unless done end end @interface.set('log:configurationfile', datafile + ' [' + nestedfiles.join(' ') + ']') return data rescue end end @interface.set('log:configurationfile', filename + ' [not loaded]') return nil end def fetch_session_interface_variables(data) data.scan(/^variable\s*:\s*(.*?)\s*\=\s*(.*?)\s*$/) do @interface.set($1, $2) end return true end def fetch_session_project_list(data) projectlist, permitted = Array.new, false data.scan(/^user\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*\,\s*(.*?)\s*$/) do domain, username, password, projects = $1, $2, $3, $4 if @session.match?('domain',domain) && @session.match?('username',username) then if same_passwords(password) then projectlist, permitted = @interface.resolved(projects).split(@@re_bar), true break end end end if permitted then @interface.set('log:projectlist', '['+projectlist.join(' ')+']') if projectlist.length == 0 then return nil else return projectlist end else @interface.set('log:projectlist', '[no projects]') return nil end end def fetch_session_command(data) data.scan(/^process\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*$/) do domain, process, command = $1, $2, $3 if @session.match?('domain',domain) && @session.match?('process',process) then @session.set('command', @interface.resolved(command)) end end return @session.get('command') end def fetch_session_settings(data) data.scan(/^setting\s*:\s*(.*?)\s*\,\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*$/) do domain, process, variable, value = $1, $2, $3, $4 if @session.match?('domain',domain) && @session.match?('process',process) then @interface.set(variable,value) end end end def get_command(action) # @session.set('action', action) # if @session.get('process') == 'none' then # @interface.set('log:child','yes') # @session.set('process', action) # end if data = load_interface_file() then fetch_session_interface_variables(data) if projectlist = fetch_session_project_list(data) then data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do domain, project, gui, path, process = $1, $2, $3, $4, $5 if @session.match?('domain',domain) then if @session.match?('project',project) then if projectlist.include?(project) then @session.set('process', @interface.resolved(process)) # break # no, else we end up in the parent (e.g. examplap instead of impose) end elsif ! action.empty? && project == action then if projectlist.include?(action) then @session.set('process', @interface.resolved(process)) # break # no, else we end up in the parent (e.g. examplap instead of impose) end end end end fetch_session_command(data) fetch_session_settings(data) end end return ! @session.nothing?('command') end def get_file(filename) @session.set('filename', filename) if data = load_interface_file() then fetch_session_interface_variables(data) if projectlist = fetch_session_project_list(data) then data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do domain, project, gui, path, process = $1, $2, $3, $4, $5 if @session.match?('domain',domain) then guilist = @interface.resolved(gui).split(@@re_bar) guilist.each do |g| if /#{filename}$/ =~ g then @session.set('gui', File.expand_path(@interface.resolved(g))) @session.set('path', File.expand_path(@interface.resolved(path))) @session.set('process', process) break # take first matching interface end end end end end end return ! (@session.nothing?('gui') && @session.nothing?('path') && @session.nothing?('process')) end def get_path(url='') if data = load_interface_file() then fetch_session_interface_variables(data) if projectlist = fetch_session_project_list(data) then data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do domain, project, gui, path, process = $1, $2, $3, $4, $5 if @session.match?('domain',domain) && @session.match?('project',project) then @session.set('url', url) @session.set('gui', '') @session.set('path', File.expand_path(@interface.resolved(path))) @session.set('process', '') end end end end return ! @session.nothing?('path') end def get_gui() if data = load_interface_file() then fetch_session_interface_variables(data) if projectlist = fetch_session_project_list(data) then data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do domain, project, gui, path, process = $1, $2, $3, $4, $5 if @session.match?('domain',domain) && @session.match?('project',project) && projectlist.include?(project) then @session.set('gui', File.expand_path(@interface.resolved(gui))) @session.set('path', File.expand_path(@interface.resolved(path))) @session.set('process', process) unless process == 'none' break # take first matching interface end end data.scan(/^admin\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*\,\s*(.*?)\s*$/) do domain, project, task, option = $1, $2, $3, $4 if @session.match?('domain',domain) && @session.match?('project',project) && projectlist.include?(project) then @session.set('task', task) @session.set('option', option) break # take first matching task end end end end return ! (@session.nothing?('gui') && @session.nothing?('path') && @session.nothing?('process')) end def get_cfg() if data = load_interface_file() then fetch_session_interface_variables(data) end end end class WWW def send_reply(logdata='') if @interface.true?('trace:run') then send_result(logdata) else dir, tmp = dirname, tmp_path(dirname) case @session.get('status') when 'running: finished' then resultname, replyname = 'result.pdf', 'reply.exa' replyfile = File.join(tmp,replyname) if FileTest.file?(replyfile) then begin data = IO.read(replyfile) resultname = if data =~ /(.*?)<\/exa:output>/ then $1 else resultname end rescue plaintext(exareply('error in reply')) return end end resultfile = File.join(tmp,resultname) if FileTest.file?(resultfile) then if indirect?(resultfile) then begin File.makedirs(File.join(cache_root,dir)) FileUtils::mv(resultfile,File.join(cache_root,dir,resultname)) rescue plaintext(exareply('unable to access cache')) else plaintext(exareply('big file', "cache/#{dir}/#{resultname}", "#{File.size?(resultfile)}")) end else send_file(resultfile) end else plaintext(exareply('no result')) end else # background, running, aborted plaintext(exareply(@session.get('status'))) end end end def send_url(fullname) dir, tmp = dirname, tmp_path(dirname) resultname, replyname = 'result.pdf', 'reply.exa' replyfile = File.join(tmp,replyname) resultfile = File.join(tmp,resultname) targetname = File.join(cache_root,dir,resultname) # make sure that there is no target left in case of an # error; needed in case of given session name if FileTest.directory?(File.join(cache_root,dir)) then File.delete(targetname) rescue false end # now try to locate the file if FileTest.file?(fullname) then if indirect?(fullname) then begin # check if directory exists and (if so) delete left overs File.makedirs(File.join(cache_root,dir)) File.delete(targetname) rescue false File.symlink(fullname,targetname) rescue message('Status',$!) unless FileTest.file?(targetname) then FileUtils::cp(fullname,targetname) rescue false end rescue plaintext(exareply('unable to access cache')) else plaintext(exareply('big file', "cache/#{dir}/#{resultname}", "#{File.size?(fullname)}")) end else send_file(fullname) end else message('Status', 'The file is not found') end end def send_result(logdata='') check_template_file('exalogin','exalogin-template.htm') dir, tmp = dirname, tmp_path(dirname) resultname, replyname, logname = 'result.pdf', 'reply.exa', 'log.htm' case @session.get('status') when 'running: background' then if st = @session.get('starttime') then # fuzzy st = Time.now.to_i if st.empty? if (Time.now.to_i - st.to_i) > @interface.get('process:timeout').to_i then message('Status', 'Your request has been aborted (timeout)',true) else message('Status', 'Your request is queued',true,5,'exastatus') end end when 'running: busy' then if st = @session.get('starttime') then # fuzzy st = Time.now.to_i if st.empty? if (Time.now.to_i - st.to_i) > @interface.get('process:timeout').to_i then message('Status', 'Your request has been aborted (timeout)',true) else message('Status', 'Your request is being processed',true,5,'exastatus') end end when 'running: aborted' then message('Status', 'Your request has been aborted (timeout)',true) when 'running: finished' then if @interface.true?('trace:run') then logfile = File.join(tmp,logname) begin if f = File.open(logname,'w') then if logdata.empty? then begin logdata = IO.read('www-watch.out') rescue logdata = 'no log data' end end f << filled_template('Log',"
#{CGI::escapeHTML(logdata)}
") f.close end rescue message('Error', '') end if FileTest.file?(logfile) then begin File.makedirs(File.join(cache_root,dir)) FileUtils::mv(logfile,File.join(cache_root,dir,logname)) rescue logdata = "

unable to access cache" else logdata = "

#{logname}" end else logdata = '' end else logdata = '' end # todo: generate reply.exa if no reply replyfile = File.join(tmp,replyname) if FileTest.file?(replyfile) then begin data = IO.read(replyfile) resultname = if data =~ /(.*?)<\/exa:output>/ then $1 else resultname end rescue message('Error','There is a problem in handling this request (invalid reply).') return end end resultfile = File.join(tmp,resultname) if FileTest.file?(resultfile) then if indirect?(resultfile) then begin File.makedirs(File.join(cache_root,dir)) FileUtils::mv(resultfile,File.join(cache_root,dir,resultname)) rescue str = "

unable to access cache" else str = "

#{resultname}  (#{File.size?(resultname)} bytes)" end message('Result', 'You can pick up the result here:' + str + logdata) else send_file(resultfile) end else message('Error', 'There is a problem in handling this request (no result file).' + logdata) end end end end