openstreetmap-website/lib/potlatch.rb
2012-11-12 20:19:07 +00:00

265 lines
7.7 KiB
Ruby

require 'stringio'
# The Potlatch module provides helper functions for potlatch and its communication with the server
module Potlatch
# The AMF class is a set of helper functions for encoding and decoding AMF.
class AMF
# Return two-byte integer
def self.getint(s)
s.getbyte*256+s.getbyte
end
# Return four-byte long
def self.getlong(s)
((s.getbyte*256+s.getbyte)*256+s.getbyte)*256+s.getbyte
end
# Return string with two-byte length
def self.getstring(s)
len=s.getbyte*256+s.getbyte
str=s.read(len)
str.force_encoding("UTF-8") if str.respond_to?("force_encoding")
str
end
# Return eight-byte double-precision float
def self.getdouble(s)
a=s.read(8).unpack('G') # G big-endian, E little-endian
a[0]
end
# Return numeric array
def self.getarray(s)
len=getlong(s)
arr=[]
for i in (0..len-1)
arr[i]=getvalue(s)
end
arr
end
# Return object/hash
def self.getobject(s)
arr={}
while (key=getstring(s))
if (key=='') then break end
arr[key]=getvalue(s)
end
s.getbyte # skip the 9 'end of object' value
arr
end
# Parse and get value
def self.getvalue(s)
case s.getbyte
when 0; return getdouble(s) # number
when 1; return s.getbyte # boolean
when 2; return getstring(s) # string
when 3; return getobject(s) # object/hash
when 5; return nil # null
when 6; return nil # undefined
when 8; s.read(4) # mixedArray
return getobject(s) # |
when 10; return getarray(s) # array
else; return nil # error
end
end
# Envelope data into AMF writeable form
def self.putdata(index,n)
d =encodestring(index+"/onResult")
d+=encodestring("null")
d+=[-1].pack("N")
d+=encodevalue(n)
end
# Pack variables as AMF
def self.encodevalue(n)
case n.class.to_s
when 'Array'
a=10.chr+encodelong(n.length)
n.each do |b|
a+=encodevalue(b)
end
a
when 'Hash'
a=3.chr
n.each do |k,v|
a+=encodestring(k.to_s)+encodevalue(v)
end
a+0.chr+0.chr+9.chr
when 'String'
2.chr+encodestring(n)
when 'Bignum','Fixnum','Float'
0.chr+encodedouble(n)
when 'NilClass'
5.chr
when 'TrueClass'
0.chr+encodedouble(1)
when 'FalseClass'
0.chr+encodedouble(0)
else
Rails.logger.error("Unexpected Ruby type for AMF conversion: "+n.class.to_s)
end
end
# Encode string with two-byte length
def self.encodestring(n)
n=n.dup.force_encoding("ASCII-8BIT") if n.respond_to?("force_encoding")
a,b=n.size.divmod(256)
a.chr+b.chr+n
end
# Encode number as eight-byte double precision float
def self.encodedouble(n)
[n].pack('G')
end
# Encode number as four-byte long
def self.encodelong(n)
[n].pack('N')
end
end
# The Dispatcher class handles decoding a series of RPC calls
# from the request, dispatching them, and encoding the response
class Dispatcher
def initialize(request, &block)
# Get stream for request data
@request = StringIO.new(request + 0.chr)
# Skip version indicator and client ID
@request.read(2)
# Skip headers
AMF.getint(@request).times do # Read number of headers and loop
AMF.getstring(@request) # | skip name
req.getbyte # | skip boolean
AMF.getvalue(@request) # | skip value
end
# Capture the dispatch routine
@dispatch = Proc.new
end
def each(&block)
# Read number of message bodies
bodies = AMF.getint(@request)
# Output response header
a,b = bodies.divmod(256)
yield 0.chr + 0.chr + 0.chr + 0.chr + a.chr + b.chr
# Process the bodies
bodies.times do # Read each body
name = AMF.getstring(@request) # | get message name
index = AMF.getstring(@request) # | get index in response sequence
bytes = AMF.getlong(@request) # | get total size in bytes
args = AMF.getvalue(@request) # | get response (probably an array)
result = @dispatch.call(name, *args)
yield AMF.putdata(index, result)
end
end
end
# The Potlatch class is a helper for Potlatch
class Potlatch
# ----- getpresets
# in: none
# does: reads tag preset menus, colours, and autocomplete config files
# out: [0] presets, [1] presetmenus, [2] presetnames,
# [3] colours, [4] casing, [5] areas, [6] autotags
# (all hashes)
def self.get_presets
Rails.logger.info(" Message: getpresets")
# Read preset menus
presets={}
presetmenus={}; presetmenus['point']=[]; presetmenus['way']=[]; presetmenus['POI']=[]
presetnames={}; presetnames['point']={}; presetnames['way']={}; presetnames['POI']={}
presettype=''
presetcategory=''
# StringIO.open(txt) do |file|
File.open("#{Rails.root}/config/potlatch/presets.txt") do |file|
file.each_line {|line|
t=line.chomp
if (t=~/(\w+)\/(\w+)/) then
presettype=$1
presetcategory=$2
presetmenus[presettype].push(presetcategory)
presetnames[presettype][presetcategory]=["(no preset)"]
elsif (t=~/^([\w\s]+):\s?(.+)$/) then
pre=$1; kv=$2
presetnames[presettype][presetcategory].push(pre)
presets[pre]={}
kv.split(',').each {|a|
if (a=~/^(.+)=(.*)$/) then presets[pre][$1]=$2 end
}
end
}
end
# Read colours/styling
colours={}; casing={}; areas={}
File.open("#{Rails.root}/config/potlatch/colours.txt") do |file|
file.each_line {|line|
t=line.chomp
if (t=~/(\w+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)/) then
tag=$1
if ($2!='-') then colours[tag]=$2.hex end
if ($3!='-') then casing[tag]=$3.hex end
if ($4!='-') then areas[tag]=$4.hex end
end
}
end
# Read relations colours/styling
relcolours={}; relalphas={}; relwidths={}
File.open("#{Rails.root}/config/potlatch/relation_colours.txt") do |file|
file.each_line {|line|
t=line.chomp
if (t=~/(\w+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)/) then
tag=$1
if ($2!='-') then relcolours[tag]=$2.hex end
if ($3!='-') then relalphas[tag]=$3.to_i end
if ($4!='-') then relwidths[tag]=$4.to_i end
end
}
end
# Read POI presets
icon_list=[]; icon_tags={};
File.open("#{Rails.root}/config/potlatch/icon_presets.txt") do |file|
file.each_line {|line|
(icon,tags)=line.chomp.split("\t")
icon_list.push(icon)
icon_tags[icon]=Hash[*tags.scan(/([^;=]+)=([^;=]+)/).flatten]
}
end
icon_list.reverse!
# Read auto-complete
autotags={}; autotags['point']={}; autotags['way']={}; autotags['POI']={};
File.open("#{Rails.root}/config/potlatch/autocomplete.txt") do |file|
file.each_line {|line|
t=line.chomp
if (t=~/^([\w:]+)\/(\w+)\s+(.+)$/) then
tag=$1; type=$2; values=$3
if values=='-' then autotags[type][tag]=[]
else autotags[type][tag]=values.split(',').sort.reverse end
end
}
end
[presets,presetmenus,presetnames,colours,casing,areas,autotags,relcolours,relalphas,relwidths,icon_list,{},icon_tags]
end
end
end