]> git.openstreetmap.org Git - rails.git/blobdiff - app/controllers/amf_controller.rb
Strip illegal characters from member roles in AMF uploads
[rails.git] / app / controllers / amf_controller.rb
index b472ae05c67ac58583c2c7dfac482b1c6816e3c0..5e57fde5bf191aee5c8fd546cddb8a0056c5d355 100644 (file)
@@ -38,9 +38,6 @@
 class AmfController < ApplicationController
   include Potlatch
 
-  # Help methods for checking boundary sanity and area size
-  include MapBoundary
-
   skip_before_filter :verify_authenticity_token
   before_filter :check_api_writable
 
@@ -48,66 +45,58 @@ class AmfController < ApplicationController
   # calls each action (private method) accordingly.
   
   def amf_read
-    if request.post?
-      self.status = :ok
-      self.content_type = Mime::AMF
-      self.response_body = Dispatcher.new(request.raw_post) do |message,*args|
-        logger.info("Executing AMF #{message}(#{args.join(',')})")
-
-        case message
-          when 'getpresets';        result = getpresets(*args)
-          when 'whichways';         result = whichways(*args)
-          when 'whichways_deleted'; result = whichways_deleted(*args)
-          when 'getway';            result = getway(args[0].to_i)
-          when 'getrelation';       result = getrelation(args[0].to_i)
-          when 'getway_old';        result = getway_old(args[0].to_i,args[1])
-          when 'getway_history';    result = getway_history(args[0].to_i)
-          when 'getnode_history';   result = getnode_history(args[0].to_i)
-          when 'findgpx';           result = findgpx(*args)
-          when 'findrelations';     result = findrelations(*args)
-          when 'getpoi';            result = getpoi(*args)
-        end
-        
-        result
+    self.status = :ok
+    self.content_type = Mime::AMF
+    self.response_body = Dispatcher.new(request.raw_post) do |message,*args|
+      logger.info("Executing AMF #{message}(#{args.join(',')})")
+
+      case message
+        when 'getpresets';        result = getpresets(*args)
+        when 'whichways';         result = whichways(*args)
+        when 'whichways_deleted'; result = whichways_deleted(*args)
+        when 'getway';            result = getway(args[0].to_i)
+        when 'getrelation';       result = getrelation(args[0].to_i)
+        when 'getway_old';        result = getway_old(args[0].to_i,args[1])
+        when 'getway_history';    result = getway_history(args[0].to_i)
+        when 'getnode_history';   result = getnode_history(args[0].to_i)
+        when 'findgpx';           result = findgpx(*args)
+        when 'findrelations';     result = findrelations(*args)
+        when 'getpoi';            result = getpoi(*args)
       end
-    else
-      render :nothing => true, :status => :method_not_allowed
+        
+      result
     end
   end
 
   def amf_write
-    if request.post?
-      renumberednodes = {}              # Shared across repeated putways
-      renumberedways = {}               # Shared across repeated putways
-      err = false                       # Abort batch on error
-
-      self.status = :ok
-      self.content_type = Mime::AMF
-      self.response_body = Dispatcher.new(request.raw_post) do |message,*args|
-        logger.info("Executing AMF #{message}")
-
-        if err
-          result = [-5, nil]
-        else
-          case message
-            when 'putway';         orn = renumberednodes.dup
-                                   result = putway(renumberednodes, *args)
-                                   result[4] = renumberednodes.reject { |k,v| orn.has_key?(k) }
-                                   if result[0] == 0 and result[2] != result[3] then renumberedways[result[2]] = result[3] end
-            when 'putrelation';    result = putrelation(renumberednodes, renumberedways, *args)
-            when 'deleteway';      result = deleteway(*args)
-            when 'putpoi';         result = putpoi(*args)
-                                   if result[0] == 0 and result[2] != result[3] then renumberednodes[result[2]] = result[3] end
-            when 'startchangeset'; result = startchangeset(*args)
-          end
+    renumberednodes = {}              # Shared across repeated putways
+    renumberedways = {}               # Shared across repeated putways
+    err = false                       # Abort batch on error
 
-          err = true if result[0] == -3  # If a conflict is detected, don't execute any more writes
+    self.status = :ok
+    self.content_type = Mime::AMF
+    self.response_body = Dispatcher.new(request.raw_post) do |message,*args|
+      logger.info("Executing AMF #{message}")
+
+      if err
+        result = [-5, nil]
+      else
+        case message
+          when 'putway';         orn = renumberednodes.dup
+                                 result = putway(renumberednodes, *args)
+                                 result[4] = renumberednodes.reject { |k,v| orn.has_key?(k) }
+                                 if result[0] == 0 and result[2] != result[3] then renumberedways[result[2]] = result[3] end
+          when 'putrelation';    result = putrelation(renumberednodes, renumberedways, *args)
+          when 'deleteway';      result = deleteway(*args)
+          when 'putpoi';         result = putpoi(*args)
+                                 if result[0] == 0 and result[2] != result[3] then renumberednodes[result[2]] = result[3] end
+          when 'startchangeset'; result = startchangeset(*args)
         end
 
-        result
+        err = true if result[0] == -3  # If a conflict is detected, don't execute any more writes
       end
-    else
-      render :nothing => true, :status => :method_not_allowed
+
+      result
     end
   end
 
@@ -131,7 +120,7 @@ class AmfController < ApplicationController
 
   def amf_handle_error_with_timeout(call,rootobj,rootid)
     amf_handle_error(call,rootobj,rootid) do
-      Timeout::timeout(API_TIMEOUT, OSM::APITimeoutError) do
+      OSM::Timer.timeout(API_TIMEOUT, OSM::APITimeoutError) do
         yield
       end
     end
@@ -144,7 +133,7 @@ class AmfController < ApplicationController
     amf_handle_error("'startchangeset'",nil,nil) do
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so Potlatch can't write any changes to the database." end
-      unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
       if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
 
       if cstags
@@ -196,10 +185,10 @@ class AmfController < ApplicationController
     user = getuser(usertoken)
 
     if user && !user.languages.empty?
-      request.user_preferred_languages = user.languages
+      http_accept_language.user_preferred_languages = user.languages
     end
 
-    lang = request.compatible_language_from(getlocales)
+    lang = http_accept_language.compatible_language_from(getlocales)
     (real_lang, localised) = getlocalized(lang)
 
     # Tell Potlatch what language it's using
@@ -265,15 +254,17 @@ class AmfController < ApplicationController
 
       # check boundary is sane and area within defined
       # see /config/application.yml
-      check_boundaries(xmin, ymin, xmax, ymax)
+      bbox = BoundingBox.new(xmin, ymin, xmax, ymax)
+      bbox.check_boundaries
+      bbox.check_size
 
       if POTLATCH_USE_SQL then
-        ways = sql_find_ways_in_area(xmin, ymin, xmax, ymax)
-        points = sql_find_pois_in_area(xmin, ymin, xmax, ymax)
-        relations = sql_find_relations_in_area_and_ways(xmin, ymin, xmax, ymax, ways.collect {|x| x[0]})
+        ways = sql_find_ways_in_area(bbox)
+        points = sql_find_pois_in_area(bbox)
+        relations = sql_find_relations_in_area_and_ways(bbox, ways.collect {|x| x[0]})
       else
         # find the way ids in an area
-        nodes_in_area = Node.bbox(ymin, xmin, ymax, xmax).visible.includes(:ways)
+        nodes_in_area = Node.bbox(bbox).visible.includes(:ways)
         ways = nodes_in_area.inject([]) { |sum, node| 
           visible_ways = node.ways.select { |w| w.visible? }
           sum + visible_ways.collect { |w| [w.id,w.version] }
@@ -305,9 +296,11 @@ class AmfController < ApplicationController
 
       # check boundary is sane and area within defined
       # see /config/application.yml
-      check_boundaries(xmin, ymin, xmax, ymax)
+      bbox = BoundingBox.new(xmin, ymin, xmax, ymax)
+      bbox.check_boundaries
+      bbox.check_size
 
-      nodes_in_area = Node.bbox(ymin, xmin, ymax, xmax).joins(:ways_via_history).where(:current_ways => { :visible => false })
+      nodes_in_area = Node.bbox(bbox).joins(:ways_via_history).where(:current_ways => { :visible => false })
       way_ids = nodes_in_area.collect { |node| node.ways_via_history.invisible.collect { |way| way.id } }.flatten.uniq
 
       [0,'',way_ids]
@@ -366,13 +359,13 @@ class AmfController < ApplicationController
     amf_handle_error_with_timeout("'getway_old' #{id}, #{timestamp}", 'way',id) do
       if timestamp == ''
         # undelete
-        old_way = OldWay.where(:visible => true, :way_id => id).order("version DESC").first
+        old_way = OldWay.where(:visible => true, :way_id => id).unredacted.order("version DESC").first
         points = old_way.get_nodes_undelete unless old_way.nil?
       else
         begin
           # revert
           timestamp = DateTime.strptime(timestamp.to_s, "%d %b %Y, %H:%M:%S")
-          old_way = OldWay.where("way_id = ? AND timestamp <= ?", id, timestamp).order("timestamp DESC").first
+          old_way = OldWay.where("way_id = ? AND timestamp <= ?", id, timestamp).unredacted.order("timestamp DESC").first
           unless old_way.nil?
             points = old_way.get_nodes_revert(timestamp)
             if !old_way.visible
@@ -410,11 +403,11 @@ class AmfController < ApplicationController
       # Find list of revision dates for way and all constituent nodes
       revdates=[]
       revusers={}
-      Way.find(wayid).old_ways.collect do |a|
+      Way.find(wayid).old_ways.unredacted.collect do |a|
         revdates.push(a.timestamp)
         unless revusers.has_key?(a.timestamp.to_i) then revusers[a.timestamp.to_i]=change_user(a) end
         a.nds.each do |n|
-          Node.find(n).old_nodes.collect do |o|
+          Node.find(n).old_nodes.unredacted.collect do |o|
             revdates.push(o.timestamp)
             unless revusers.has_key?(o.timestamp.to_i) then revusers[o.timestamp.to_i]=change_user(o) end
           end
@@ -430,7 +423,7 @@ class AmfController < ApplicationController
       # Remove any elements where 2 seconds doesn't elapse before next one
       revdates.delete_if { |d| revdates.include?(d+1) or revdates.include?(d+2) }
       # Collect all in one nested array
-      revdates.collect! {|d| [d.succ.strftime("%d %b %Y, %H:%M:%S")] + revusers[d.to_i] }
+      revdates.collect! {|d| [(d + 1).strftime("%d %b %Y, %H:%M:%S")] + revusers[d.to_i] }
       revdates.uniq!
 
       return ['way', wayid, revdates]
@@ -443,8 +436,8 @@ class AmfController < ApplicationController
 
   def getnode_history(nodeid) #:doc:
     begin 
-      history = Node.find(nodeid).old_nodes.reverse.collect do |old_node|
-        [old_node.timestamp.succ.strftime("%d %b %Y, %H:%M:%S")] + change_user(old_node)
+      history = Node.find(nodeid).old_nodes.unredacted.reverse.collect do |old_node|
+        [(old_node.timestamp + 1).strftime("%d %b %Y, %H:%M:%S")] + change_user(old_node)
       end
       return ['node', nodeid, history]
     rescue ActiveRecord::RecordNotFound
@@ -466,7 +459,7 @@ class AmfController < ApplicationController
     amf_handle_error_with_timeout("'findgpx'" ,nil,nil) do
       user = getuser(usertoken)
       if !user then return -1,"You must be logged in to search for GPX traces." end
-      unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
 
       query = Trace.visible_to(user)
       if searchterm.to_i > 0 then
@@ -530,7 +523,7 @@ class AmfController < ApplicationController
     amf_handle_error("'putrelation' #{relid}" ,'relation',relid)  do
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so the relation could not be saved." end
-      unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
       if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
 
       if !tags_ok(tags) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
@@ -558,7 +551,7 @@ class AmfController < ApplicationController
             mid = renumberedways[mid] if m[0] == 'Way'
           end
           if mid
-            typedmembers << [m[0], mid, m[2]]
+            typedmembers << [m[0], mid, strip_non_xml_chars(m[2])]
           end
         end
 
@@ -620,7 +613,7 @@ class AmfController < ApplicationController
   
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so the way could not be saved." end
-      unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
       if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
 
       if pointlist.length < 2 then return -2,"Server error - way is only #{points.length} points long." end
@@ -729,7 +722,7 @@ class AmfController < ApplicationController
     amf_handle_error("'putpoi' #{id}", 'node',id) do
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so the point could not be saved." end
-      unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
       if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
 
       if !tags_ok(tags) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
@@ -789,7 +782,7 @@ class AmfController < ApplicationController
       n = Node.find(id)
       v = n.version
       unless timestamp == ''
-        n = OldNode.where("id = ? AND timestamp <= ?", id, timestamp).order("timestamp DESC").first
+        n = OldNode.where("node_id = ? AND timestamp <= ?", id, timestamp).unredacted.order("timestamp DESC").first
       end
 
       if n
@@ -814,7 +807,7 @@ class AmfController < ApplicationController
     amf_handle_error("'deleteway' #{way_id}" ,'way', way_id) do
       user = getuser(usertoken)
       unless user then return -1,"You are not logged in, so the way could not be deleted." end
-      unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
       if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
       
       way_id = way_id.to_i
@@ -904,7 +897,7 @@ class AmfController < ApplicationController
   # ====================================================================
   # Alternative SQL queries for getway/whichways
 
-  def sql_find_ways_in_area(xmin,ymin,xmax,ymax)
+  def sql_find_ways_in_area(bbox)
     sql=<<-EOF
     SELECT DISTINCT current_ways.id AS wayid,current_ways.version AS version
       FROM current_way_nodes
@@ -912,12 +905,12 @@ class AmfController < ApplicationController
     INNER JOIN current_ways  ON current_ways.id =current_way_nodes.id
        WHERE current_nodes.visible=TRUE 
        AND current_ways.visible=TRUE 
-       AND #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "current_nodes.")}
+       AND #{OSM.sql_for_area(bbox, "current_nodes.")}
     EOF
     return ActiveRecord::Base.connection.select_all(sql).collect { |a| [a['wayid'].to_i,a['version'].to_i] }
   end
   
-  def sql_find_pois_in_area(xmin,ymin,xmax,ymax)
+  def sql_find_pois_in_area(bbox)
     pois=[]
     sql=<<-EOF
       SELECT current_nodes.id,current_nodes.latitude*0.0000001 AS lat,current_nodes.longitude*0.0000001 AS lon,current_nodes.version 
@@ -925,7 +918,7 @@ class AmfController < ApplicationController
        LEFT OUTER JOIN current_way_nodes cwn ON cwn.node_id=current_nodes.id 
        WHERE current_nodes.visible=TRUE
        AND cwn.id IS NULL
-       AND #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "current_nodes.")}
+       AND #{OSM.sql_for_area(bbox, "current_nodes.")}
     EOF
     ActiveRecord::Base.connection.select_all(sql).each do |row|
       poitags={}
@@ -937,7 +930,7 @@ class AmfController < ApplicationController
     pois
   end
   
-  def sql_find_relations_in_area_and_ways(xmin,ymin,xmax,ymax,way_ids)
+  def sql_find_relations_in_area_and_ways(bbox,way_ids)
     # ** It would be more Potlatchy to get relations for nodes within ways
     #    during 'getway', not here
     sql=<<-EOF
@@ -945,7 +938,7 @@ class AmfController < ApplicationController
       FROM current_relations cr
       INNER JOIN current_relation_members crm ON crm.id=cr.id 
       INNER JOIN current_nodes cn ON crm.member_id=cn.id AND crm.member_type='Node' 
-       WHERE #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "cn.")}
+       WHERE #{OSM.sql_for_area(bbox, "cn.")}
       EOF
     unless way_ids.empty?
       sql+=<<-EOF