rfacebook 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +12 -3
- data/lib/facebook_desktop_session.rb +77 -116
- data/lib/facebook_session.rb +282 -334
- data/lib/facebook_web_session.rb +28 -103
- data/lib/facepricot.rb +20 -22
- data/lib/rfacebook.rb +2 -0
- data/{lib/rfacebook_on_rails/view_extensions.rb → test/facebook_desktop_session_test.rb} +23 -39
- data/test/facebook_session_test_methods.rb +106 -0
- data/test/facebook_web_session_test.rb +48 -0
- data/test/test_helper.rb +216 -0
- metadata +55 -60
- data/lib/facebook_rails_controller_extensions.rb +0 -10
- data/lib/rfacebook_on_rails/controller_extensions.rb +0 -543
- data/lib/rfacebook_on_rails/model_extensions.rb +0 -203
- data/lib/rfacebook_on_rails/plugin/Rakefile.rb +0 -1
- data/lib/rfacebook_on_rails/plugin/init.rb +0 -139
- data/lib/rfacebook_on_rails/plugin/install.rb +0 -1
- data/lib/rfacebook_on_rails/plugin/rake.rb +0 -141
- data/lib/rfacebook_on_rails/plugin/uninstall.rb +0 -1
- data/lib/rfacebook_on_rails/session_extensions.rb +0 -202
- data/lib/rfacebook_on_rails/status_manager.rb +0 -309
- data/lib/rfacebook_on_rails/templates/debug_panel.rhtml +0 -220
- data/lib/rfacebook_on_rails/templates/exception_backtrace.rhtml +0 -97
data/README
CHANGED
@@ -36,6 +36,15 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
36
36
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
37
37
|
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
============================== RELEASE NOTES ==============================
|
40
|
+
|
41
|
+
0.9.8:
|
42
|
+
- split up the Gem (core library) and plugin (Rails extensions) - you will now have to update your Rails plugin separately from the Gem
|
43
|
+
- better documentation in the core library
|
44
|
+
- deprecated all the confusing is_valid/is_ready/is_activated/is_expired methods in favor of simply 'ready?' and 'expired?'
|
45
|
+
- removed the plugin code from the gem...the Rails plugin is now much better-separated
|
46
|
+
- moved unit tests that applied to the core into the Gem itself (rather than the plugin)
|
47
|
+
- cleaned out some unnecessary code in controller_extensions
|
48
|
+
- refactored signature checking so that it happens in the plugin rather than in the core lib
|
49
|
+
- added ssh_port to tunnel configuration (if for some reason you dont use 22)
|
50
|
+
- added 'log_out_of_facebook' method to force a user to log out (clears the fbsession data)
|
@@ -31,133 +31,94 @@ require "facebook_session"
|
|
31
31
|
|
32
32
|
module RFacebook
|
33
33
|
|
34
|
-
class FacebookDesktopSession < FacebookSession
|
34
|
+
class FacebookDesktopSession < FacebookSession
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
# :section: Properties
|
39
|
-
################################################################################################
|
40
|
-
|
41
|
-
attr_reader :session_secret # you should need this for infinite desktop sessions
|
42
|
-
|
43
|
-
################################################################################################
|
44
|
-
################################################################################################
|
45
|
-
# :section: Initialization
|
46
|
-
################################################################################################
|
47
|
-
|
48
|
-
# Function: initialize
|
49
|
-
# Constructs a FacebookDesktopSession. Also calls the API to grab an auth_token.
|
50
|
-
#
|
51
|
-
# Parameters:
|
52
|
-
# api_key - your API key
|
53
|
-
# api_secret - your API secret
|
54
|
-
# options.suppress_errors - boolean, set to true if you don't want errors to be thrown (defaults to false)
|
55
|
-
def initialize(api_key, api_secret, suppress_errors = false)
|
56
|
-
super(api_key, api_secret, suppress_errors)
|
57
|
-
@desktop_auth_token = get_auth_token
|
58
|
-
end
|
59
|
-
|
60
|
-
################################################################################################
|
61
|
-
################################################################################################
|
62
|
-
# :section: URL Accessors
|
63
|
-
################################################################################################
|
64
|
-
|
65
|
-
# Function: get_login_url
|
66
|
-
# Gets the authentication URL
|
67
|
-
#
|
68
|
-
# Parameters:
|
69
|
-
# options.next - the page to redirect to after login
|
70
|
-
# options.popup - boolean, whether or not to use the popup style (defaults to true)
|
71
|
-
# options.skipcookie - boolean, whether to force new Facebook login (defaults to false)
|
72
|
-
# options.hidecheckbox - boolean, whether to show the "infinite session" option checkbox (defaults to false)
|
73
|
-
def get_login_url(options={})
|
74
|
-
# options
|
75
|
-
path_next = options[:next] ||= nil
|
76
|
-
popup = (options[:popup] == nil) ? true : false
|
77
|
-
skipcookie = (options[:skipcookie] == nil) ? false : true
|
78
|
-
hidecheckbox = (options[:hidecheckbox] == nil) ? false : true
|
36
|
+
# you should need this for infinite desktop sessions
|
37
|
+
attr_reader :session_secret
|
79
38
|
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
39
|
+
# Constructs a FacebookDesktopSession, calling the API to grab an auth_token.
|
40
|
+
#
|
41
|
+
# api_key:: your API key
|
42
|
+
# api_secret:: your API secret
|
43
|
+
# quiet:: boolean, set to true if you don't want errors to be thrown (defaults to false)
|
44
|
+
def initialize(api_key, api_secret, quiet = false)
|
45
|
+
super(api_key, api_secret, quiet)
|
46
|
+
result = remote_call("auth.createToken", {})
|
47
|
+
@desktop_auth_token = result.at("auth_createToken_response")
|
48
|
+
@desktop_auth_token = @desktop_auth_token.nil? ? nil : @desktop_auth_token.inner_html.to_s
|
49
|
+
end
|
85
50
|
|
86
|
-
#
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
51
|
+
# Gets the authentication URL
|
52
|
+
#
|
53
|
+
# options.next:: the page to redirect to after login
|
54
|
+
# options.popup:: boolean, whether or not to use the popup style (defaults to true)
|
55
|
+
# options.skipcookie:: boolean, whether to force new Facebook login (defaults to false)
|
56
|
+
# options.hidecheckbox:: boolean, whether to show the "infinite session" option checkbox (defaults to false)
|
57
|
+
def get_login_url(options={})
|
58
|
+
# options
|
59
|
+
path_next = options[:next] ||= nil
|
60
|
+
popup = (options[:popup] == nil) ? true : false
|
61
|
+
skipcookie = (options[:skipcookie] == nil) ? false : true
|
62
|
+
hidecheckbox = (options[:hidecheckbox] == nil) ? false : true
|
63
|
+
|
64
|
+
# get some extra portions of the URL
|
65
|
+
optionalNext = (path_next == nil) ? "" : "&next=#{CGI.escape(path_next.to_s)}"
|
66
|
+
optionalPopup = (popup == true) ? "&popup=true" : ""
|
67
|
+
optionalSkipCookie = (skipcookie == true) ? "&skipcookie=true" : ""
|
68
|
+
optionalHideCheckbox = (hidecheckbox == true) ? "&hide_checkbox=true" : ""
|
69
|
+
|
70
|
+
# build and return URL
|
71
|
+
return "http://#{WWW_HOST}#{WWW_PATH_LOGIN}?v=1.0&api_key=#{@api_key}&auth_token=#{@desktop_auth_token}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
|
103
72
|
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Function: activate_with_previous_session
|
107
|
-
# Sets the session key and secret directly (for example, if you have an infinite session key)
|
108
|
-
#
|
109
|
-
# Parameters:
|
110
|
-
# key - the session key to use
|
111
|
-
# secret - the session secret to use
|
112
|
-
def activate_with_previous_session(key, secret)
|
113
|
-
# set the session key and secret
|
114
|
-
@session_key = key
|
115
|
-
@session_secret = secret
|
116
73
|
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
result = call_method("auth.createToken", {})
|
128
|
-
result = result.at("auth_createToken_response").inner_html.to_s ||= result.at("auth_createToken_response").inner_html.to_s
|
129
|
-
return result
|
130
|
-
end
|
131
|
-
|
74
|
+
# Activates the session and makes it ready for usage. Call this method only after
|
75
|
+
# the user has logged in via the login URL.
|
76
|
+
def activate
|
77
|
+
result = remote_call("auth.getSession", {:auth_token => @desktop_auth_token}, true)
|
78
|
+
if result != nil
|
79
|
+
@session_user_id = result.at("uid").inner_html
|
80
|
+
@session_key = result.at("session_key").inner_html
|
81
|
+
@session_secret = result.at("secret").inner_html
|
82
|
+
end
|
83
|
+
end
|
132
84
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
85
|
+
# Activate using the session key and secret directly (for example, if you have an infinite session)
|
86
|
+
#
|
87
|
+
# key:: the session key to use
|
88
|
+
# secret:: the session secret to use
|
89
|
+
def activate_with_previous_session(key, secret)
|
90
|
+
# set the session key and secret
|
91
|
+
@session_key = key
|
92
|
+
@session_secret = secret
|
137
93
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
end
|
94
|
+
# determine the current user's id
|
95
|
+
result = remote_call("users.getLoggedInUser")
|
96
|
+
@session_user_id = result.at("users_getLoggedInUser_response").inner_html
|
97
|
+
end
|
143
98
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
# for any calls other than the call to createToken and getSession
|
148
|
-
def get_secret(params) # :nodoc:
|
149
|
-
|
150
|
-
if ( params[:method] != "facebook.auth.getSession" and params[:method] != "facebook.auth.createToken")
|
151
|
-
return @session_secret
|
152
|
-
else
|
153
|
-
return @api_secret
|
99
|
+
# returns true if this session is completely ready to be used and make API calls
|
100
|
+
def ready?
|
101
|
+
return (@session_key != nil and @session_secret != nil and !expired?)
|
154
102
|
end
|
103
|
+
|
104
|
+
# Used for signing a set of parameters in the way that Facebook
|
105
|
+
# specifies: <http://developers.facebook.com/documentation.php?v=1.0&doc=auth>
|
106
|
+
#
|
107
|
+
# params:: a Hash containing the parameters to sign
|
108
|
+
def signature(params)
|
109
|
+
# choose the proper secret
|
110
|
+
signatureSecret = nil
|
111
|
+
unless (params[:method] == "facebook.auth.getSession" or params[:method] == "facebook.auth.createToken")
|
112
|
+
signatureSecret = @session_secret
|
113
|
+
else
|
114
|
+
signatureSecret = @api_secret
|
115
|
+
end
|
155
116
|
|
156
|
-
|
117
|
+
# sign the parameters with that secret
|
118
|
+
return signature_helper(params, signatureSecret)
|
119
|
+
end
|
157
120
|
|
158
|
-
end
|
159
|
-
|
160
|
-
|
121
|
+
end
|
161
122
|
|
162
123
|
end
|
163
124
|
|
data/lib/facebook_session.rb
CHANGED
@@ -27,10 +27,6 @@
|
|
27
27
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
28
|
#
|
29
29
|
|
30
|
-
#
|
31
|
-
# Some code was inspired by techniques used in Alpha Chen's old client.
|
32
|
-
#
|
33
|
-
|
34
30
|
require "digest/md5"
|
35
31
|
require "net/https"
|
36
32
|
require "cgi"
|
@@ -38,389 +34,341 @@ require "facepricot"
|
|
38
34
|
|
39
35
|
module RFacebook
|
40
36
|
|
41
|
-
|
42
|
-
|
37
|
+
# TODO: better handling of session expiration
|
38
|
+
# TODO: support Bebo's API
|
43
39
|
|
44
|
-
|
45
|
-
WWW_PATH_LOGIN = "/login.php"
|
46
|
-
WWW_PATH_ADD = "/add.php"
|
47
|
-
WWW_PATH_INSTALL = "/install.php"
|
40
|
+
API_VERSION = "1.0"
|
48
41
|
|
49
|
-
|
42
|
+
API_HOST = "api.facebook.com"
|
43
|
+
API_PATH_REST = "/restserver.php"
|
44
|
+
|
45
|
+
WWW_HOST = "www.facebook.com"
|
46
|
+
WWW_PATH_LOGIN = "/login.php"
|
47
|
+
WWW_PATH_ADD = "/add.php"
|
48
|
+
WWW_PATH_INSTALL = "/install.php"
|
50
49
|
|
51
|
-
################################################################################################
|
52
|
-
################################################################################################
|
53
|
-
# :section: Errors
|
54
|
-
################################################################################################
|
55
|
-
|
56
|
-
class RemoteStandardError < StandardError; end
|
57
|
-
class ExpiredSessionStandardError < StandardError; end
|
58
|
-
class NotActivatedStandardError < StandardError; end
|
59
|
-
|
60
|
-
################################################################################################
|
61
|
-
################################################################################################
|
62
|
-
# :section: Properties
|
63
|
-
################################################################################################
|
64
50
|
|
65
|
-
|
66
|
-
# The user id of the user associated with this sesssion.
|
67
|
-
attr_reader :session_user_id
|
51
|
+
class FacebookSession
|
68
52
|
|
69
|
-
|
70
|
-
|
71
|
-
|
53
|
+
################################################################################################
|
54
|
+
################################################################################################
|
55
|
+
# :section: Error classes
|
56
|
+
################################################################################################
|
72
57
|
|
73
|
-
|
74
|
-
|
75
|
-
|
58
|
+
# TODO: better exception classes in v1.0?
|
59
|
+
class RemoteStandardError < StandardError
|
60
|
+
attr_reader :code
|
61
|
+
def initialize(message, code)
|
62
|
+
@code = code
|
63
|
+
end
|
64
|
+
end
|
65
|
+
class ExpiredSessionStandardError < RemoteStandardError; end
|
66
|
+
class NotActivatedStandardError < StandardError; end
|
67
|
+
|
68
|
+
################################################################################################
|
69
|
+
################################################################################################
|
70
|
+
# :section: Properties
|
71
|
+
################################################################################################
|
76
72
|
|
77
|
-
|
78
|
-
|
79
|
-
attr_reader :last_error_message
|
73
|
+
# The user id of the user associated with this sesssion.
|
74
|
+
attr_reader :session_user_id
|
80
75
|
|
81
|
-
|
82
|
-
|
83
|
-
attr_reader :last_error_code
|
76
|
+
# The key for this session. You will need to save this for infinite sessions.
|
77
|
+
attr_reader :session_key
|
84
78
|
|
85
|
-
|
86
|
-
|
87
|
-
# You can set suppress_errors=true to stop these exceptions
|
88
|
-
# from being thrown.
|
89
|
-
attr_accessor :suppress_errors
|
79
|
+
# The expiration time of this session, as given from Facebook API login.
|
80
|
+
attr_reader :session_expires
|
90
81
|
|
91
|
-
|
92
|
-
|
93
|
-
attr_accessor :logger
|
82
|
+
# Can be set to any valid logger (for example, RAIL_DEFAULT_LOGGER)
|
83
|
+
attr_accessor :logger
|
94
84
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
85
|
+
################################################################################################
|
86
|
+
################################################################################################
|
87
|
+
# :section: Public Interface
|
88
|
+
################################################################################################
|
89
|
+
|
90
|
+
# Constructs a FacebookSession.
|
91
|
+
#
|
92
|
+
# api_key:: your API key
|
93
|
+
# api_secret:: your API secret
|
94
|
+
# quiet:: boolean, set to true if you don't want exceptions to be thrown (defaults to false)
|
95
|
+
def initialize(api_key, api_secret, quiet = false)
|
96
|
+
# required parameters
|
97
|
+
@api_key = api_key
|
98
|
+
@api_secret = api_secret
|
99
|
+
|
100
|
+
# optional parameters
|
101
|
+
@quiet = quiet
|
102
|
+
|
103
|
+
# initialize internal state
|
104
|
+
@last_error_message = nil # DEPRECATED
|
105
|
+
@last_error_code = nil # DEPRECATED
|
106
|
+
@expired = false
|
107
|
+
end
|
101
108
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
return (@session_expired == true)
|
107
|
-
end
|
109
|
+
# Template method. Returns true when the session is definitely prepared to make API calls.
|
110
|
+
def ready?
|
111
|
+
raise NotImplementedError
|
112
|
+
end
|
108
113
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
114
|
+
# Returns true if the session is expired (will often mean that the session is not ready as well)
|
115
|
+
def expired?
|
116
|
+
return @expired
|
117
|
+
end
|
114
118
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
119
|
+
# Returns true if exceptions are being suppressed in favor of log messages
|
120
|
+
def quiet?
|
121
|
+
return @quiet
|
122
|
+
end
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
124
|
+
# Sets whether or not we suppress exceptions from being thrown
|
125
|
+
def quiet=(val)
|
126
|
+
@quiet = val
|
127
|
+
end
|
128
|
+
|
129
|
+
# Template method. Used for signing a set of parameters in the way that Facebook
|
130
|
+
# specifies: <http://developers.facebook.com/documentation.php?v=1.0&doc=auth>
|
131
|
+
#
|
132
|
+
# params:: a Hash containing the parameters to sign
|
133
|
+
def signature(params)
|
134
|
+
raise NotImplementedError
|
135
|
+
end
|
136
|
+
|
137
|
+
################################################################################################
|
138
|
+
################################################################################################
|
139
|
+
# :section: Utility methods
|
140
|
+
################################################################################################
|
141
|
+
private
|
125
142
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# optional parameters
|
141
|
-
@suppress_errors = suppress_errors
|
142
|
-
|
143
|
-
# initialize internal state
|
144
|
-
@last_error_message = nil
|
145
|
-
@last_error_code = nil
|
146
|
-
@session_expired = false
|
143
|
+
# This allows *any* Facebook method to be called, using the Ruby
|
144
|
+
# mechanism for responding to unimplemented methods. Basically,
|
145
|
+
# this converts a call to "auth_getSession" to "auth.getSession"
|
146
|
+
# and does the proper API call using the parameter hash given.
|
147
|
+
#
|
148
|
+
# This allows you to call an API method such as facebook.users.getInfo
|
149
|
+
# by calling "fbsession.users_getInfo"
|
150
|
+
def method_missing(methodSymbol, *params)
|
151
|
+
# get the remote method name
|
152
|
+
remoteMethod = methodSymbol.to_s.gsub("_", ".")
|
153
|
+
if methodSymbol.to_s.match(/cached_(.*)/)
|
154
|
+
log_debug "** RFACEBOOK(GEM) - DEPRECATION NOTICE - cached methods are deprecated, making a raw call without caching."
|
155
|
+
tokens.shift
|
156
|
+
end
|
147
157
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
################################################################################################
|
154
|
-
################################################################################################
|
155
|
-
# :section: API Calls
|
156
|
-
################################################################################################
|
157
|
-
protected
|
158
|
+
# there can only be one parameter, a Hash, for remote methods
|
159
|
+
unless (params.size == 1 and params.first.is_a?(Hash))
|
160
|
+
log_debug "** RFACEBOOK(GEM) - when you call a remote Facebook method"
|
161
|
+
end
|
158
162
|
|
159
|
-
|
160
|
-
|
161
|
-
# mechanism for responding to unimplemented methods. Basically,
|
162
|
-
# this converts a call to "auth_getSession" to "auth.getSession"
|
163
|
-
# and does the proper API call using the parameter hash given.
|
164
|
-
#
|
165
|
-
# This allows you to call an API method such as facebook.users.getInfo
|
166
|
-
# by calling "fbsession.users_getInfo"
|
167
|
-
#
|
168
|
-
def method_missing(methodSymbol, *params)
|
169
|
-
tokens = methodSymbol.to_s.split("_")
|
170
|
-
if tokens[0] == "cached"
|
171
|
-
tokens.shift
|
172
|
-
return cached_call_method(tokens.join("."), params.first)
|
173
|
-
else
|
174
|
-
return call_method(tokens.join("."), params.first)
|
163
|
+
# make the remote method call
|
164
|
+
return remote_call(remoteMethod, params.first)
|
175
165
|
end
|
176
|
-
end
|
177
|
-
|
178
166
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
# use_ssl - set to true if the call will be made over SSL
|
186
|
-
def call_method(method, params={}, use_ssl=false) # :nodoc:
|
167
|
+
# Sets everything up to make a POST request to Facebook's API servers.
|
168
|
+
#
|
169
|
+
# method:: i.e. "users.getInfo"
|
170
|
+
# params:: hash of key,value pairs for the parameters to this method
|
171
|
+
# useSSL:: set to true if the call will be made over SSL
|
172
|
+
def remote_call(method, params={}, useSSL=false) # :nodoc:
|
187
173
|
|
188
|
-
|
189
|
-
|
190
|
-
# ensure that this object has been activated somehow
|
191
|
-
if (!method.include?("auth") and !is_activated?)
|
192
|
-
raise NotActivatedStandardError, "You must activate the session before using it."
|
193
|
-
end
|
194
|
-
|
195
|
-
# set up params hash
|
196
|
-
if (!params)
|
197
|
-
params = {}
|
198
|
-
end
|
199
|
-
params = params.dup
|
174
|
+
log_debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#remote_call - #{method}(#{params.inspect}) - making remote call"
|
200
175
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
176
|
+
# set up the parameters
|
177
|
+
params = (params || {}).dup
|
178
|
+
params[:method] = "facebook.#{method}"
|
179
|
+
params[:api_key] = @api_key
|
180
|
+
params[:v] = API_VERSION
|
181
|
+
# params[:format] ||= @response_format # TODO: consider JSON capability
|
205
182
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
183
|
+
# non-auth methods get special consideration
|
184
|
+
unless(method == "auth.getSession" or method == "auth.createToken")
|
185
|
+
# session must be activated for non-auth methods
|
186
|
+
raise NotActivatedStandardError, "You must activate the session before using it." unless ready?
|
187
|
+
|
188
|
+
# secret and call ID must be set for non-auth methods
|
189
|
+
params[:session_key] = session_key
|
190
|
+
params[:call_id] = Time.now.to_f.to_s
|
191
|
+
end
|
210
192
|
|
211
|
-
|
212
|
-
|
193
|
+
# in the parameters, all arrays must be converted to comma-separated lists
|
194
|
+
params.each{|k,v| params[k] = v.join(",") if v.is_a?(Array)}
|
213
195
|
|
214
|
-
|
215
|
-
|
196
|
+
# sign the parameter list by adding a proper sig
|
197
|
+
params[:sig] = signature(params)
|
216
198
|
|
217
|
-
|
218
|
-
|
219
|
-
|
199
|
+
# make the remote call and contain the results in a Facepricot XML object
|
200
|
+
xml = post_request(params, useSSL)
|
201
|
+
return handle_xml_response(xml)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Wraps an XML response in a Facepricot XML document, and checks for
|
205
|
+
# an error response (raising or logging errors as needed)
|
206
|
+
#
|
207
|
+
# NOTE: Facepricot chaining may be deprecated in the 1.0 release
|
208
|
+
def handle_xml_response(rawXML)
|
209
|
+
facepricotXML = Facepricot.new(rawXML)
|
220
210
|
|
221
|
-
|
222
|
-
|
211
|
+
# error checking
|
212
|
+
if facepricotXML.at("error_response")
|
223
213
|
|
224
|
-
|
214
|
+
# get the error code
|
215
|
+
errorCode = facepricotXML.at("error_code").inner_html.to_i
|
216
|
+
errorMessage = facepricotXML.at("error_msg").inner_html
|
217
|
+
log_debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#remote_call - remote call failed (#{errorCode}: #{errorMessage})"
|
225
218
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
@last_error_code = code
|
230
|
-
|
231
|
-
# check to see if this error was an expired session error
|
232
|
-
if code == 102
|
233
|
-
@session_expired = true
|
234
|
-
raise ExpiredSessionStandardError, @last_error_message unless @suppress_errors == true
|
235
|
-
end
|
219
|
+
# TODO: remove these 2 lines
|
220
|
+
@last_error_message = "ERROR #{errorCode}: #{errorMessage}" # DEPRECATED
|
221
|
+
@last_error_code = errorCode # DEPRECATED
|
236
222
|
|
237
|
-
|
238
|
-
|
223
|
+
# check to see if this error was an expired session error
|
224
|
+
case errorCode
|
225
|
+
|
226
|
+
# the remote method did not exist, convert that to a standard Ruby no-method error
|
227
|
+
when 3
|
228
|
+
raise NoMethodError, errorMessage unless quiet? == true
|
229
|
+
|
230
|
+
# the parameters were wrong, or not enough parameters...convert that to a standard Ruby argument error
|
231
|
+
when 100,606
|
232
|
+
raise ArgumentError, errorMessage unless quiet? == true
|
233
|
+
|
234
|
+
# when the session expires, we need to record that internally
|
235
|
+
when 102
|
236
|
+
@expired = true
|
237
|
+
raise ExpiredSessionStandardError.new(errorMessage, errorCode) unless quiet? == true
|
239
238
|
|
240
|
-
|
241
|
-
|
239
|
+
# otherwise, just raise a regular remote error with the error code
|
240
|
+
else
|
241
|
+
raise RemoteStandardError.new(errorMessage, errorCode) unless quiet? == true
|
242
|
+
end
|
242
243
|
|
243
|
-
|
244
|
-
|
244
|
+
# since the quiet flag may have been activated, we may not have thrown
|
245
|
+
# an actual exception, so we still need to return nil here
|
246
|
+
return nil
|
247
|
+
end
|
245
248
|
|
246
|
-
|
247
|
-
|
249
|
+
# everything was just fine, return the Facepricot XML response
|
250
|
+
return facepricotXML
|
251
|
+
end
|
248
252
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
port = (use_ssl == true) ? 443 : 80
|
260
|
-
http_server = Net::HTTP.new(API_SERVER_BASE_URL, port)
|
261
|
-
http_server.use_ssl = use_ssl
|
253
|
+
# Posts a request to the remote Facebook API servers, and returns the
|
254
|
+
# raw text body of the result
|
255
|
+
#
|
256
|
+
# params:: a Hash of the post parameters to send to the REST API
|
257
|
+
# useSSL:: defaults to false, set to true if you want to use SSL for the POST
|
258
|
+
def post_request(params, useSSL=false)
|
259
|
+
# get a server handle
|
260
|
+
port = (useSSL == true) ? 443 : 80
|
261
|
+
http_server = Net::HTTP.new(API_HOST, port)
|
262
|
+
http_server.use_ssl = useSSL
|
262
263
|
|
263
|
-
|
264
|
-
|
265
|
-
|
264
|
+
# build a request
|
265
|
+
http_request = Net::HTTP::Post.new(API_PATH_REST)
|
266
|
+
http_request.form_data = params
|
266
267
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
# Function: cached_call_method
|
272
|
-
# Does the same thing as call_method, except that the response is cached for
|
273
|
-
# the lifetime of the FacebookSession.
|
274
|
-
#
|
275
|
-
# Parameters:
|
276
|
-
# method - i.e. "users.getInfo"
|
277
|
-
# params - hash of key,value pairs for the parameters to this method
|
278
|
-
# use_ssl - set to true if the call will be made over SSL
|
279
|
-
def cached_call_method(method,params={},use_ssl=false) # :nodoc:
|
280
|
-
key = cache_key_for(method,params)
|
281
|
-
log_debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#cached_call_method - #{method}(#{params.inspect}) - attempting to hit cache"
|
282
|
-
return @callcache[key] ||= call_method(method,params,use_ssl)
|
283
|
-
end
|
268
|
+
# get the response XML
|
269
|
+
return http_server.start{|http| http.request(http_request)}.body
|
270
|
+
end
|
284
271
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
if v.is_a?(Array)
|
296
|
-
v = v.join(",")
|
297
|
-
end
|
298
|
-
pairs << "#{k}=#{v}"
|
272
|
+
# Generates a proper Facebook signature.
|
273
|
+
#
|
274
|
+
# params:: a Hash containing the parameters to sign
|
275
|
+
# secret:: the secret to use to sign the parameters
|
276
|
+
def signature_helper(params, secret) # :nodoc:
|
277
|
+
args = []
|
278
|
+
params.each{|k,v| args << "#{k}=#{v}"}
|
279
|
+
sortedArray = args.sort
|
280
|
+
requestStr = sortedArray.join("")
|
281
|
+
return Digest::MD5.hexdigest("#{requestStr}#{secret}")
|
299
282
|
end
|
300
|
-
return "#{method}(#{pairs.sort.join("...")})".to_sym
|
301
|
-
end
|
302
283
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
# Function: get_secret
|
309
|
-
# Returns the proper secret to sign a set of parameters with.
|
310
|
-
# A WebSession will always use the api_secret, but a DesktopSession
|
311
|
-
# sometimes needs to use a session_secret.
|
312
|
-
#
|
313
|
-
# THIS IS AN ABSTRACT METHOD
|
314
|
-
#
|
315
|
-
# Parameters:
|
316
|
-
# params - a Hash containing the parameters to sign
|
317
|
-
#
|
318
|
-
def get_secret(params) # :nodoc:
|
319
|
-
raise StandardError
|
320
|
-
end
|
321
|
-
|
322
|
-
# Function: param_signature
|
323
|
-
# Generates a param_signature for a call to the API, per the spec on Facebook
|
324
|
-
# see: <http://developers.facebook.com/documentation.php?v=1.0&doc=auth>
|
325
|
-
#
|
326
|
-
# Parameters:
|
327
|
-
# params - a Hash containing the parameters to sign
|
328
|
-
#
|
329
|
-
def param_signature(params) # :nodoc:
|
330
|
-
return generate_signature(params, get_secret(params));
|
331
|
-
end
|
284
|
+
# log a debug message
|
285
|
+
def log_debug(message) # :nodoc:
|
286
|
+
@logger.debug(message) if @logger
|
287
|
+
end
|
332
288
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
#
|
337
|
-
# Parameters:
|
338
|
-
# hash - a Hash containing the parameters to sign
|
339
|
-
# secret - the API or session secret to use to sign the parameters
|
340
|
-
#
|
341
|
-
def generate_signature(hash, secret) # :nodoc:
|
342
|
-
args = []
|
343
|
-
hash.each do |k,v|
|
344
|
-
args << "#{k}=#{v}"
|
289
|
+
# log an informational message
|
290
|
+
def log_info(message) # :nodoc:
|
291
|
+
@logger.info(message) if @logger
|
345
292
|
end
|
346
|
-
sortedArray = args.sort
|
347
|
-
requestStr = sortedArray.join("")
|
348
|
-
return Digest::MD5.hexdigest("#{requestStr}#{secret}")
|
349
|
-
end
|
350
293
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
def _dump(depth)
|
358
|
-
instanceVarHash = {}
|
359
|
-
self.instance_variables.each { |k| instanceVarHash[k] = self.instance_variable_get(k) }
|
360
|
-
# the logger must be removed before serializing
|
361
|
-
return Marshal.dump(instanceVarHash.delete_if{|k,v| k == "@logger"})
|
362
|
-
end
|
294
|
+
################################################################################################
|
295
|
+
################################################################################################
|
296
|
+
# :section: Serialization
|
297
|
+
################################################################################################
|
298
|
+
public
|
363
299
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
300
|
+
# dump to a serialized string, removing the logger object (which cannot be serialized)
|
301
|
+
def _dump(depth) # :nodoc:
|
302
|
+
instanceVarHash = {}
|
303
|
+
self.instance_variables.each { |k| instanceVarHash[k] = self.instance_variable_get(k) }
|
304
|
+
return Marshal.dump(instanceVarHash.delete_if{|k,v| k == "@logger"})
|
369
305
|
end
|
370
|
-
return instance
|
371
|
-
end
|
372
306
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
307
|
+
# load from a serialized string
|
308
|
+
def self._load(dumpedStr) # :nodoc:
|
309
|
+
instance = self.new(nil,nil)
|
310
|
+
dumped = Marshal.load(dumpedStr)
|
311
|
+
dumped.each do |k,v|
|
312
|
+
instance.instance_variable_set(k,v)
|
313
|
+
end
|
314
|
+
return instance
|
315
|
+
end
|
378
316
|
|
379
|
-
def log_debug(message) # :nodoc:
|
380
|
-
@logger.debug(message) if @logger
|
381
|
-
end
|
382
317
|
|
383
|
-
|
384
|
-
|
385
|
-
|
318
|
+
################################################################################################
|
319
|
+
################################################################################################
|
320
|
+
# :section: Deprecated Methods
|
321
|
+
################################################################################################
|
322
|
+
public
|
323
|
+
|
324
|
+
# DEPRECATED
|
325
|
+
def is_expired? # :nodoc:
|
326
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: is_expired? is deprecated, use expired? instead"
|
327
|
+
return expired?
|
328
|
+
end
|
329
|
+
|
330
|
+
# DEPRECATED
|
331
|
+
def is_activated? # :nodoc:
|
332
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: is_activated? is deprecated, use ready? instead"
|
333
|
+
return ready?
|
334
|
+
end
|
386
335
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
336
|
+
# DEPRECATED
|
337
|
+
def is_valid? # :nodoc:
|
338
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: is_valid? is deprecated, use ready? instead"
|
339
|
+
return ready?
|
340
|
+
end
|
392
341
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
342
|
+
# DEPRECATED
|
343
|
+
def is_ready? # :nodoc:
|
344
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: is_valid? is deprecated, use ready? instead"
|
345
|
+
return ready?
|
346
|
+
end
|
398
347
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
348
|
+
# DEPRECATED
|
349
|
+
def last_error_message # :nodoc:
|
350
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: last_error_message is deprecated"
|
351
|
+
return @last_error_message
|
352
|
+
end
|
404
353
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
354
|
+
# DEPRECATED
|
355
|
+
def last_error_code # :nodoc:
|
356
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: last_error_code is deprecated"
|
357
|
+
return @last_error_code
|
358
|
+
end
|
410
359
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
360
|
+
# DEPRECATED
|
361
|
+
def suppress_errors # :nodoc:
|
362
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: suppress_errors is deprecated, use quiet? instead"
|
363
|
+
return quiet?
|
364
|
+
end
|
416
365
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
366
|
+
# DEPRECATED
|
367
|
+
def suppress_errors=(val) # :nodoc:
|
368
|
+
RAILS_DEFAULT_LOGGER.info "** RFACEBOOK(GEM) DEPRECATION WARNING: suppress_errors= is deprecated, use quiet= instead"
|
369
|
+
@quiet=val
|
370
|
+
end
|
423
371
|
|
424
|
-
end
|
372
|
+
end
|
425
373
|
|
426
374
|
end
|