Potlatch Rails AMF stuff
[rails.git] / app / controllers / amf_controller.rb
1 class AmfController < ApplicationController
2   require 'stringio'
3
4   # Still to do:
5   # - all db interaction
6   # - user authentication
7   # - (also pass lat/lon through from view tab to edit tab)
8
9   # ====================================================================
10   # Main AMF handler
11   
12   # ---- talk   process AMF request
13
14   def talk
15         req=StringIO.new(request.raw_post)      # Get POST data as request
16         req.read(2)                                                     # Skip version indicator and client ID
17         results={}                                                      # Results of each body
18
19         # -------------
20         # Parse request
21
22         headers=getint(req)                                     # Read number of headers
23         for i in (1..headers)                           # Read each header
24                 name=getstring(req)                             #  |
25                 req.getc                                                #  | skip boolean
26                 value=getvalue(req)                             #  |
27                 header["name"]=value                    #  |
28         end
29
30         bodies=getint(req)                                      # Read number of bodies
31         for i in (1..bodies)                            # Read each body
32                 message=getstring(req)                  #  | get message name
33                 index=getstring(req)                    #  | get index in response sequence
34                 bytes=getlong(req)                              #  | get total size in bytes
35                 args=getvalue(req)                              #  | get response (probably an array)
36         
37                 case message
38                         when 'getpresets';      results[index]=putdata(index,getpresets)
39                         when 'whichways';       results[index]=putdata(index,whichways(args))
40                         when 'getway';          results[index]=putdata(index,getway(args))
41                         when 'putway';          results[index]=putdata(index,putway(args))
42                         when 'deleteway';       results[index]=putdata(index,deleteway(args))
43                 end
44         end
45
46         # ------------------
47         # Write out response
48
49         response.headers["Content-Type"]="application/x-amf"
50         a,b=results.length.divmod(256)
51         ans=0.chr+0.chr+0.chr+0.chr+a.chr+b.chr
52         results.each do |k,v|
53                 ans+=v
54         end
55         render :text=>ans
56
57   end
58
59
60         # ====================================================================
61         # Remote calls
62
63         def getpresets
64                 presets={}
65                 presetmenus={}; presetmenus['point']=[]; presetmenus['way']=[]
66                 presetnames={}; presetnames['point']={}; presetnames['way']={}
67                 presettype=''
68                 presetcategory=''
69                 
70                 File.open("config/potlatch/presets.txt") do |file|
71                         file.each_line {|line|
72                                 t=line.chomp
73                                 if (t=~/(\w+)\/(\w+)/) then
74                                         presettype=$1
75                                         presetcategory=$2
76                                         presetmenus[presettype].push(presetcategory)
77                                         presetnames[presettype][presetcategory]=["(no preset)"]
78                                 elsif (t=~/^(.+):\s?(.+)$/) then
79                                         pre=$1; kv=$2
80                                         presetnames[presettype][presetcategory].push(pre)
81                                         presets[pre]={}
82                                         kv.split(',').each {|a|
83                                                 if (a=~/^(.+)=(.*)$/) then presets[pre][$1]=$2 end
84                                         }
85                                 end
86                         }
87                 end
88                 [presets,presetmenus,presetnames]
89         end
90
91         def whichways(args)
92                 # to do
93         end
94         
95         def getway(args)
96                 # to do
97         end
98         
99         def putway(args)
100                 # to do
101         end
102         
103         def deleteway(args)
104                 # to do
105         end
106         
107         # need support functions here too:
108         #       database support functions (readwayquery, createuniquesegments)
109         #       tag2array, array2tag
110         #       getuserid
111
112         # ====================================================================
113         # AMF read subroutines
114         
115         # ----- getint          return two-byte integer
116         # ----- getlong         return four-byte long
117         # ----- getstring       return string with two-byte length
118         # ----- getdouble       return eight-byte double-precision float
119         # ----- getobject       return object/hash
120         # ----- getarray        return numeric array
121         
122         def getint(s)
123                 s.getc*256+s.getc
124         end
125         
126         def getlong(s)
127                 ((s.getc*256+s.getc)*256+s.getc)*256+s.getc
128         end
129         
130         def getstring(s)
131                 len=s.getc*256+s.getc
132                 s.read(len)
133         end
134         
135         def getdouble(s)
136                 a=s.read(8).unpack('G')                 # G big-endian, E little-endian
137                 a[0]
138         end
139         
140         def getarray(s)
141                 len=getlong(s)
142                 arr=[]
143                 for i in (0..len-1)
144                         arr[i]=getvalue(s)
145                 end
146                 arr
147         end
148         
149         def getobject(s)
150                 arr={}
151                 while (key=getstring(s))
152                         if (key=='') then break end
153                         arr[key]=getvalue(s)
154                 end
155                 s.getc          # skip the 9 'end of object' value
156                 arr
157         end
158         
159         # ----- getvalue        parse and get value
160         
161         def getvalue(s)
162                 case s.getc
163                         when 0; return getdouble(s)                     # number
164                         when 1; return s.getc                           # boolean
165                         when 2; return getstring(s)                     # string
166                         when 3; return getobject(s)                     # object/hash
167                         when 5; return nil                                      # null
168                         when 6; return nil                                      # undefined
169                         when 8; s.read(4)                                       # mixedArray
170                                         return getobject(s)                     #  |
171                         when 10;return getarray(s)                      # array
172                         else;   return nil                                      # error
173                 end
174         end
175
176         # ====================================================================
177         # AMF write subroutines
178         
179         # ----- putdata         envelope data into AMF writeable form
180         # ----- encodevalue     pack variables as AMF
181         
182         def putdata(index,n)
183                 d =encodestring(index+"/onResult")
184                 d+=encodestring("null")
185                 d+=[-1].pack("N")
186                 d+=encodevalue(n)
187         end
188         
189         def encodevalue(n)
190                 case n.class.to_s
191                         when 'Array'
192                                 a=10.chr+encodelong(n.length)
193                                 n.each do |b|
194                                         a+=encodevalue(b)
195                                 end
196                                 a
197                         when 'Hash'
198                                 a=3.chr
199                                 n.each do |k,v|
200                                         a+=encodestring(k)+encodevalue(v)
201                                 end
202                                 a+0.chr+0.chr+9.chr
203                         when 'String'
204                                 2.chr+encodestring(n)
205                         when 'Bignum','Fixnum','Float'
206                                 0.chr+encodedouble(n)
207                         when 'NilClass'
208                                 5.chr
209                 end
210         end
211         
212         # ----- encodestring    encode string with two-byte length
213         # ----- encodedouble    encode number as eight-byte double precision float
214         # ----- encodelong              encode number as four-byte long
215         
216         def encodestring(n)
217                 a,b=n.size.divmod(256)
218                 a.chr+b.chr+n
219         end
220         
221         def encodedouble(n)
222                 [n].pack('G')
223         end
224         
225         def encodelong(n)
226                 [n].pack('N')
227         end
228         
229         # ====================================================================
230         # Co-ordinate conversion
231         
232         def lat2coord(a)
233                 -(lat2y(a)-$basey)*$masterscale+250
234         end
235         
236         def long2coord(a)
237                 (a-$baselong)*$masterscale+350
238         end
239         
240         def lat2y(a)
241                 180/Math::PI * Math.log(Math.tan(Math::PI/4+a*(Math::PI/180)/2))
242         end
243         
244         def coord2lat(a)
245                 y2lat((a-250)/-$masterscale+$basey)
246         end
247         
248         def coord2long(a)
249                 (a-350)/$masterscale+$baselong
250         end
251         
252         def y2lat(a)
253                 180/Math::PI * (2*Math.atan(Math.exp(a*Math::PI/180))-Math::PI/2)
254         end
255
256
257 end