-
Notifications
You must be signed in to change notification settings - Fork 5
/
facebook.js
285 lines (253 loc) · 9.94 KB
/
facebook.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/*
most of code is from (https://github.com/javierfigueroa/turedsocial).
modified to remove app_secret from html and javascript
MIT license.
*/
/**
* Split a string by string
* @param delimiter string The boundary string.
* @param string string The input string.
* @param limit int[optional] If limit is set and positive, the returned array will contain
* a maximum of limit elements with the last
* element containing the rest of string.
*
* If the limit parameter is negative, all components
* except the last -limit are returned.
*
* If the limit parameter is zero, then this is treated as 1.
*
* @returns array If delimiter is an empty string (""),
* explode will return false.
* If delimiter contains a value that is not
* contained in string and a negative
* limit is used, then an empty array will be
* returned. For any other limit, an array containing
* string will be returned.
*/
function explode(delimiter, string, limit) {
var emptyArray = { 0: '' };
// third argument is not required
if ( arguments.length < 2 ||
typeof arguments[0] == 'undefined' || typeof arguments[1] == 'undefined' ) {
return null;
}
if ( delimiter === '' || delimiter === false ||
delimiter === null ) {
return false;
}
if ( typeof delimiter == 'function' || typeof delimiter == 'object' ||
typeof string == 'function' || typeof string == 'object' ) {
return emptyArray;
}
if ( delimiter === true ) {
delimiter = '1';
}
if (!limit) {
return string.toString().split(delimiter.toString());
} else {
// support for limit argument
var splitted = string.toString().split(delimiter.toString());
var partA = splitted.splice(0, limit - 1);
var partB = splitted.join(delimiter.toString());
partA.push(partB);
return partA;
}
};
/**
* Handler for X-FACEBOOK-PLATFORM SASL authentication.
*
* @param (XMLElement) elem - The challenge stanza.
*
* @returns false to remove the handler.
*/
Strophe.Connection.prototype._sasl_challenge1_fb = function (elem)
{
var challenge = Base64.decode(Strophe.getText(elem));
var nonce = "";
var method = "";
var version = "";
// remove unneeded handlers
this.deleteHandler(this._sasl_failure_handler);
var challenges = explode("&", challenge);
for(i=0; i<challenges.length; i++)
{
map = explode("=", challenges[i]);
switch (map[0])
{
case "nonce":
nonce = map[1];
break;
case "method":
method = map[1];
break;
case "version":
version = map[1];
break;
}
}
var responseText = "";
responseText += 'api_key=' + this.apiKey;
responseText += '&call_id=' + (Math.floor(new Date().getTime()/1000));
responseText += '&method=' + method;
responseText += '&nonce=' + nonce;
responseText += '&access_token=' + this.accessToken;
responseText += '&v=' + '1.0';
this._sasl_challenge_handler = this._addSysHandler(
this._sasl_digest_challenge2_cb.bind(this), null,
"challenge", null, null);
this._sasl_success_handler = this._addSysHandler(
this._sasl_success_cb.bind(this), null,
"success", null, null);
this._sasl_failure_handler = this._addSysHandler(
this._sasl_failure_cb.bind(this), null,
"failure", null, null);
this.send($build('response', {
xmlns: Strophe.NS.SASL
}).t(Base64.encode(responseText)).tree());
return false;
};
/**
* Handler for initial connection request with Facebokk.
*
* This handler is used to process the initial connection request
* response from the BOSH server. It is used to set up authentication
* handlers and start the authentication process.
*
* SASL authentication will be attempted if available, otherwise
* the code will fall back to legacy authentication.
*
* @param (Strophe.Request) req - The current request.
*/
Strophe.Connection.prototype._connect_fb = function (req) {
Strophe.info("_connect_fb was called");
this.connected = true;
var bodyWrap = req.getResponse();
if (!bodyWrap) { return; }
this.xmlInput(bodyWrap);
this.rawInput(Strophe.serialize(bodyWrap));
var typ = bodyWrap.getAttribute("type");
var cond, conflict;
if (typ !== null && typ == "terminate") {
// an error occurred
cond = bodyWrap.getAttribute("condition");
conflict = bodyWrap.getElementsByTagName("conflict");
if (cond !== null) {
if (cond == "remote-stream-error" && conflict.length > 0) {
cond = "conflict";
}
this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
} else {
this._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
}
return;
}
// check to make sure we don't overwrite these if _connect_fb is
// called multiple times in the case of missing stream:features
if (!this.sid) {
this.sid = bodyWrap.getAttribute("sid");
}
if (!this.stream_id) {
this.stream_id = bodyWrap.getAttribute("authid");
}
var wind = bodyWrap.getAttribute('requests');
if (wind) { this.window = wind; }
var hold = bodyWrap.getAttribute('hold');
if (hold) { this.hold = hold; }
var wait = bodyWrap.getAttribute('wait');
if (wait) { this.wait = wait; }
var mechanisms = bodyWrap.getElementsByTagName("mechanism");
var i, mech, auth_str, hashed_auth_str, xfacebook;
if (mechanisms.length == 0) {
// we didn't get stream:features yet, so we need wait for it
// by sending a blank poll request
var body = this._buildBody();
this._requests.push(
new Strophe.Request(body.tree(),
this._onRequestStateChange.bind(
this, this._connect_fb.bind(this)),
body.tree().getAttribute("rid")));
this._throttledRequestHandler();
return;
} else {
for (i = 0; i < mechanisms.length; i++) {
mech = Strophe.getText(mechanisms[i]);
if (mech == 'X-FACEBOOK-PLATFORM') {
xfacebook = true;
break;
}
}
}
if (!xfacebook) {
return;
}
this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
this._sasl_challenge_handler = this._addSysHandler(
this._sasl_challenge1_fb.bind(this), null,
"challenge", null, null);
this._sasl_failure_handler = this._addSysHandler(
this._sasl_challenge1_fb.bind(this), null,
"failure", null, null);
this.send($build("auth", {
xmlns: Strophe.NS.SASL,
mechanism: "X-FACEBOOK-PLATFORM"
}).tree());
};
/**
* Starts the connection process with facebok XMPP Chat Server.
*
* As the connection process proceeds, the user supplied callback will
* be triggered multiple times with status updates. The callback
* should take two arguments - the status code and the error condition.
*
* The status code will be one of the values in the Strophe.Status
* constants. The error condition will be one of the conditions
* defined in RFC 3920 or the condition 'strophe-parsererror'.
*
* Please see XEP 124 for a more detailed explanation of the optional
* parameters below.
*
* @param (String) jid - The user's JID. It must be [email protected],
* where facebook id is the number id of the facebook profile
* @param (Function) callback The connect callback function.
* @param (Integer) wait - The optional HTTPBIND wait value. This is the
* time the server will wait before returning an empty result for
* a request. The default setting of 60 seconds is recommended.
* Other settings will require tweaks to the Strophe.TIMEOUT value.
* @param (Integer) hold - The optional HTTPBIND hold value. This is the
* number of connections the server will hold at one time. This
* should almost always be set to 1 (the default).
* @param apiKey The API key of our Facebook Application
* @param sessionKey The actual session key for the user who we are attempting to log in
*/
Strophe.Connection.prototype.facebookConnect = function (jid, callback, wait, hold, apiKey, accessToken){
this.jid = jid;
this.connect_callback = callback;
this.disconnecting = false;
this.connected = false;
this.authenticated = false;
this.errors = 0;
this.apiKey = apiKey;
this.accessToken = accessToken;
this.wait = wait || this.wait;
this.hold = hold || this.hold;
// parse jid for domain and resource
this.domain = Strophe.getDomainFromJid(this.jid);
// build the body tag
var body = this._buildBody().attrs({
to: this.domain,
"xml:lang": "en",
wait: this.wait,
hold: this.hold,
content: "text/xml; charset=utf-8",
ver: "1.6",
"xmpp:version": "1.0",
"xmlns:xmpp": Strophe.NS.BOSH
});
this._changeConnectStatus(Strophe.Status.CONNECTING, null);
this._requests.push(
new Strophe.Request(body.tree(),
this._onRequestStateChange.bind(
this, this._connect_fb.bind(this)),
body.tree().getAttribute("rid")));
this._throttledRequestHandler();
};