From c2a9403a4c9be0db36b86b63e70a990374b5ff10 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Sat, 28 Sep 2013 21:11:34 +0100 Subject: [PATCH] Trying to get push notifications working - and failing so far --- lib/tapatalker.rb | 125 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 17 deletions(-) diff --git a/lib/tapatalker.rb b/lib/tapatalker.rb index 06383de..99b08ad 100644 --- a/lib/tapatalker.rb +++ b/lib/tapatalker.rb @@ -4,6 +4,9 @@ require 'discourse_api' require 'json' require 'pp' require 'chronic' +require 'base64' +require 'net/https' + PUSH_TYPES = "ann,conv,pm,sub,like,thank,quote,newtopic,tag" @@ -58,8 +61,22 @@ helpers do method, arguments = XMLRPC::Marshal.load_call(xml) method = "xmlrpc_#{method.gsub(/([A-Z])/, '_ ').downcase}" - puts "method = #{method}, args = #{ method =~ /login/ ? "*REDACTED*" : arguments.inspect }" + # FIXME: this is pretty evil + filtered_args = if method == "xmlrpc_login" + tmp = arguments.dup + tmp[1] = "[FILTERED]" + tmp + elsif method == "xmlrpc_update_push_status" + tmp = arguments.dup + tmp[2] = "[FILTERED]" + tmp + else + arguments + end + + puts "method = #{method}, args = #{filtered_args.inspect}" + # Check if method exists if(respond_to?(method)) content_type("text/xml", :charset => "utf-8") @@ -158,7 +175,7 @@ helpers do result end - + # Try to keep this instance around for as long as possible... # TODO: To keep it around, push into Rack::Session, or cookies? Some sort of # long-lived store. Can go away with process restarts, of course. @@ -175,13 +192,13 @@ helpers do def respond_xmlrpc( rsp ) data = XMLRPC::Marshal.dump_response( rsp ) - + data.gsub!( '', '' ) data.tr!("\n", "") - + data end - + ## http://tapatalk.com/api/api_section.php ## @@ -225,7 +242,7 @@ helpers do "min_search_length" => 3, # Boring default "alert" => "0", # TODO "direct_unsubscribe" => "0", # TODO - # "push_type" => PUSH_TYPES, + "push_type" => PUSH_TYPES, # "ban_delete_type" => "none", # TODO "inappreg" => "0", # TODO @@ -263,7 +280,7 @@ helpers do # These are returned by some other forums but aren't documented "key" => settings.tapatalk_api_key, # tapatalk API key, at a guess? - "push" => "0", + "push" => "1", # "allow_moderate", @@ -313,12 +330,12 @@ helpers do set_cookies = discourse.cookies.each.collect {|c| c.set_cookie_value } headers( "Set-Cookie" => set_cookies ) if set_cookies.size > 0 - # user = user_info.fetch("user") + user = user_info.fetch("user") respond_xmlrpc( 'result' => true, - # 'user_id' => user["id"], # api level 4 - # 'username' => binary_xmlrpc( "username" ), # api level 4 + 'user_id' => user["id"], # api level 4 + 'username' => binary_xmlrpc( user["username"] ), # api level 4 # 'usergroup_id' => [], # api level 4 # 'email' => binary_xmlrpc( "" ) # api level 4 # not sent by discourse # 'icon_url' => make_avatar_url( user["avatar_template"] ), # api level 4 @@ -331,12 +348,23 @@ helpers do # can_profile => true, # api level 4 'can_upload_avatar' => false, # TODO - # Don't enable push if the user has disabled it - # FIXME: bool or string 1/0 ? - # 'push_type' => PUSH_TYPES.split(",").collect {|type| { 'name' => type, 'value' => push } } + # phpBB3 returns the values as booleans + 'push_type' => PUSH_TYPES.split(",").collect {|type| + { 'name' => type, 'value' => ( push == "1" ) } + } ) end + # Unsure what this does just yet, trying to get push notifications working. + # Pokes directory.tapatalk.com, as does update_push_status... glue? + def xmlrpc_sign_in( token, code, unknown, username, password ) + pp [ "xmlrpc_sign_in args:", args ] + + pp [ "rack environment:", request.env ] + + respond_xmlrpc( 'result' => false ) + end + def xmlrpc_logout_user discourse.session_destroy({}) respond_xmlrpc( 'result' => true ) @@ -566,7 +594,7 @@ helpers do def xmlrpc_reply_post( forum_id, topic_id, subject, text_body, attachment_id_array = nil, return_html = false ) csrf_token! rsp = discourse.post_create( :topic_id => topic_id, :title => subject, :raw => text_body ) - + result = { 'result' => true, 'post_id' => rsp["id"].to_s, @@ -577,13 +605,76 @@ helpers do # 'post_time' => make_xmlrpc_datetime( rsp['updated_at'] ) # api level 4 # attachments # api level 4 } - + respond_xmlrpc( result ) end - + # def get_smilies # TODO - not a priority # end - + + # Grr. Bad tapatalk, bad. + def php_serialize( hash ) + # PHP's array serialize format is like this: + # a::{s::"...";i:;...;} + + raise ArgumentError.new( "Not a hash" ) unless hash.is_a?(Hash ) + + elems = hash.collect {|k, v| + raise ArgumentError.new( "key #{k} not a string") unless k.is_a?( String ) + raise ArgumentError.new( "value #{v} not Fixnum") unless v.is_a?( Fixnum ) + "s:#{k.size}:\"#{k}\";i:#{v}" + }.join(";") + + "a:#{hash.size}:{#{elems};}" + end + + def xmlrpc_update_push_status( push_status, username, password ) + user = discourse.user( :username => username ).fetch( "user" ) + + # FIXME: TODO: CHECK THE PASSWORD IS VALID! + if !username || !password || password == "" || username == "" || + !user.has_key?("id") || !user.has_key?("username") + puts "Returning false - problems validating user" + pp user + return respond_xmlrpc( 'result' => false ) + end + + # push_status is a hash: {"pm"=>1, "conversation"=>1, "newtopic"=>1, "sub"=>1, "like"=>1, "tag"=>1, "quote"=>1} + # We have to do a php-style serialize() on it, base64-encode it and pass it + # on to tapatalk's push API. This is going to get nasty. + + uri = URI.parse( settings.tapatalk_api ) + uri.path = "/au_update_push_setting.php" + + req_data = { + 'url' => settings.discourse_api.to_s, + 'key' => settings.tapatalk_api_key, + 'uid' => user["id"], # FIXME: should we use id instead? + 'data' => Base64::encode64( php_serialize( push_status ) ).tr!("\n", "") + } + + # We have this strange call to a strange path - that seems to fail anyway - + # to do + tapatalk = Net::HTTP.new( "directory.tapatalk.com", 443 ) + tapatalk.use_ssl = true + tapatalk.verify_mode = OpenSSL::SSL::VERIFY_NONE # FIXME + + rsp = tapatalk.start {|remote| + req = Net::HTTP::Post.new( uri.path ) + req.form_data = req_data + remote.request( req ) + } + raise "Unknown response: #{rsp.inspect} #{rsp.body}" unless rsp.is_a?( Net::HTTPOK ) + + rsp_data = JSON.parse( rsp.body, :create_extensions => false ) + + unless rsp_data['result'] + puts "Warning: POSTing to directory failed: #{rsp_data.inspect}. Upstream ignores said failures, so we will too." + end + + respond_xmlrpc( 'result' => true ) + end + end post "/" do