Merge lp:~urbanape/bindwood/using-oauth into lp:bindwood
- using-oauth
- Merge into trunk
Proposed by
Zachery Bir
Status: | Merged |
---|---|
Approved by: | Guillermo Gonzalez |
Approved revision: | 21 |
Merged at revision: | not available |
Proposed branch: | lp:~urbanape/bindwood/using-oauth |
Merge into: | lp:bindwood |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~urbanape/bindwood/using-oauth |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guillermo Gonzalez | Approve | ||
Tim Cole (community) | Approve | ||
Review via email: mp+11371@code.launchpad.net |
Commit message
This branch enables OAuth integration for Bindwood.
Description of the change
To post a comment you must log in.
Revision history for this message
Zachery Bir (urbanape) wrote : | # |
Revision history for this message
Tim Cole (tcole) wrote : | # |
Looks okay. Good catch with the semicolons.
review:
Approve
- 21. By Zachery Bir
-
Woops, renamed this variable
Revision history for this message
Guillermo Gonzalez (verterok) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'content/browserOverlay.xul' |
2 | --- content/browserOverlay.xul 2009-08-18 14:17:30 +0000 |
3 | +++ content/browserOverlay.xul 2009-09-02 17:07:11 +0000 |
4 | @@ -19,6 +19,8 @@ |
5 | <overlay id="bindwood" |
6 | xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
7 | |
8 | + <script type="application/x-javascript" src="chrome://bindwood/content/sha1.js" /> |
9 | + <script type="application/x-javascript" src="chrome://bindwood/content/oauth.js" /> |
10 | <script type="application/x-javascript" src="chrome://bindwood/content/couch.js" /> |
11 | <script type="application/x-javascript" src="chrome://bindwood/content/sync.js" /> |
12 | |
13 | |
14 | === modified file 'content/couch.js' |
15 | --- content/couch.js 2009-07-08 01:24:33 +0000 |
16 | +++ content/couch.js 2009-09-08 15:29:55 +0000 |
17 | @@ -1,12 +1,12 @@ |
18 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
19 | -// use this file except in compliance with the License. You may obtain a copy |
20 | -// of the License at |
21 | +// use this file except in compliance with the License. You may obtain a copy of |
22 | +// the License at |
23 | // |
24 | // http://www.apache.org/licenses/LICENSE-2.0 |
25 | // |
26 | // Unless required by applicable law or agreed to in writing, software |
27 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
28 | -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
29 | +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
30 | // License for the specific language governing permissions and limitations under |
31 | // the License. |
32 | |
33 | @@ -22,17 +22,26 @@ |
34 | this.last_req = null; |
35 | |
36 | this.request = function(method, uri, requestOptions) { |
37 | - requestOptions = requestOptions || {} |
38 | - requestOptions.headers = combine(requestOptions.headers, httpHeaders) |
39 | + CouchDB.message.action = uri; |
40 | + Bindwood.writeMessage("Set the action of the CouchDB.message to: " + uri); |
41 | + CouchDB.message.method = method; |
42 | + Bindwood.writeMessage("Set the method of the CouchDB.message to: " + method); |
43 | + OAuth.completeRequest(CouchDB.message, CouchDB.accessor); |
44 | + Bindwood.writeMessage("Signed the message"); |
45 | + var parameters = CouchDB.message.parameters; |
46 | + requestOptions = requestOptions || {}; |
47 | + requestOptions.headers = combine(requestOptions.headers, httpHeaders); |
48 | + requestOptions.headers = combine(requestOptions.headers, {Authorization: OAuth.getAuthorizationHeader('', parameters)}); |
49 | + Bindwood.writeMessage("Request headers: " + JSON.stringify(headers)); |
50 | return CouchDB.request(method, uri, requestOptions); |
51 | - } |
52 | + }; |
53 | |
54 | // Creates the database on the server |
55 | this.createDb = function() { |
56 | this.last_req = this.request("PUT", this.uri); |
57 | CouchDB.maybeThrowError(this.last_req); |
58 | return JSON.parse(this.last_req.responseText); |
59 | - } |
60 | + }; |
61 | |
62 | // Deletes the database on the server |
63 | this.deleteDb = function() { |
64 | @@ -41,7 +50,7 @@ |
65 | return false; |
66 | CouchDB.maybeThrowError(this.last_req); |
67 | return JSON.parse(this.last_req.responseText); |
68 | - } |
69 | + }; |
70 | |
71 | // Save a document to the database |
72 | this.save = function(doc, options) { |
73 | @@ -55,7 +64,7 @@ |
74 | var result = JSON.parse(this.last_req.responseText); |
75 | doc._rev = result.rev; |
76 | return result; |
77 | - } |
78 | + }; |
79 | |
80 | // Open a document from the database |
81 | this.open = function(docId, options) { |
82 | @@ -64,7 +73,7 @@ |
83 | return null; |
84 | CouchDB.maybeThrowError(this.last_req); |
85 | return JSON.parse(this.last_req.responseText); |
86 | - } |
87 | + }; |
88 | |
89 | // Deletes a document from the database |
90 | this.deleteDoc = function(doc) { |
91 | @@ -74,7 +83,7 @@ |
92 | doc._rev = result.rev; //record rev in input document |
93 | doc._deleted = true; |
94 | return result; |
95 | - } |
96 | + }; |
97 | |
98 | // Deletes an attachment from a document |
99 | this.deleteDocAttachment = function(doc, attachment_name) { |
100 | @@ -83,17 +92,17 @@ |
101 | var result = JSON.parse(this.last_req.responseText); |
102 | doc._rev = result.rev; //record rev in input document |
103 | return result; |
104 | - } |
105 | + }; |
106 | |
107 | this.bulkSave = function(docs, options) { |
108 | // first prepoulate the UUIDs for new documents |
109 | - var newCount = 0 |
110 | + var newCount = 0; |
111 | for (var i=0; i<docs.length; i++) { |
112 | if (docs[i]._id == undefined) |
113 | newCount++; |
114 | } |
115 | var newUuids = CouchDB.newUuids(docs.length); |
116 | - var newCount = 0 |
117 | + var newCount = 0; |
118 | for (var i=0; i<docs.length; i++) { |
119 | if (docs[i]._id == undefined) |
120 | docs[i]._id = newUuids.pop(); |
121 | @@ -118,17 +127,17 @@ |
122 | } |
123 | return results; |
124 | } |
125 | - } |
126 | + }; |
127 | |
128 | this.ensureFullCommit = function() { |
129 | this.last_req = this.request("POST", this.uri + "_ensure_full_commit"); |
130 | CouchDB.maybeThrowError(this.last_req); |
131 | return JSON.parse(this.last_req.responseText); |
132 | - } |
133 | + }; |
134 | |
135 | // Applies the map function to the contents of database and returns the results. |
136 | - this.query = function(mapFun, reduceFun, options, keys) { |
137 | - var body = {language: "javascript"}; |
138 | + this.query = function(mapFun, reduceFun, options, keys, language) { |
139 | + var body = {language: language || "javascript"}; |
140 | if(keys) { |
141 | body.keys = keys ; |
142 | } |
143 | @@ -144,13 +153,16 @@ |
144 | body.options = options.options; |
145 | delete options.options; |
146 | } |
147 | - this.last_req = this.request("POST", this.uri + "_temp_view" + encodeOptions(options), { |
148 | + var uri = this.uri + "_temp_view" + encodeOptions(options); |
149 | + Bindwood.writeMessage("Query URI: " + uri); |
150 | + Bindwood.writeMessage("Query body: " + JSON.stringify(body)); |
151 | + this.last_req = this.request("POST", uri , { |
152 | headers: {"Content-Type": "application/json"}, |
153 | body: JSON.stringify(body) |
154 | }); |
155 | CouchDB.maybeThrowError(this.last_req); |
156 | return JSON.parse(this.last_req.responseText); |
157 | - } |
158 | + }; |
159 | |
160 | this.view = function(viewname, options, keys) { |
161 | var viewParts = viewname.split('/'); |
162 | @@ -168,14 +180,27 @@ |
163 | return null; |
164 | CouchDB.maybeThrowError(this.last_req); |
165 | return JSON.parse(this.last_req.responseText); |
166 | - } |
167 | + }; |
168 | |
169 | // gets information about the database |
170 | this.info = function() { |
171 | this.last_req = this.request("GET", this.uri); |
172 | CouchDB.maybeThrowError(this.last_req); |
173 | return JSON.parse(this.last_req.responseText); |
174 | - } |
175 | + }; |
176 | + |
177 | + // gets information about a design doc |
178 | + this.designInfo = function(docid) { |
179 | + this.last_req = this.request("GET", this.uri + docid + "/_info"); |
180 | + CouchDB.maybeThrowError(this.last_req); |
181 | + return JSON.parse(this.last_req.responseText); |
182 | + }; |
183 | + |
184 | + this.viewCleanup = function() { |
185 | + this.last_req = this.request("POST", this.uri + "_view_cleanup"); |
186 | + CouchDB.maybeThrowError(this.last_req); |
187 | + return JSON.parse(this.last_req.responseText); |
188 | + }; |
189 | |
190 | this.allDocs = function(options,keys) { |
191 | if(!keys) { |
192 | @@ -188,7 +213,7 @@ |
193 | } |
194 | CouchDB.maybeThrowError(this.last_req); |
195 | return JSON.parse(this.last_req.responseText); |
196 | - } |
197 | + }; |
198 | |
199 | this.designDocs = function() { |
200 | return this.allDocs({startkey:"_design", endkey:"_design0"}); |
201 | @@ -206,13 +231,13 @@ |
202 | } |
203 | CouchDB.maybeThrowError(req); |
204 | return JSON.parse(req.responseText); |
205 | - } |
206 | + }; |
207 | |
208 | this.compact = function() { |
209 | this.last_req = this.request("POST", this.uri + "_compact"); |
210 | CouchDB.maybeThrowError(this.last_req); |
211 | return JSON.parse(this.last_req.responseText); |
212 | - } |
213 | + }; |
214 | |
215 | this.setDbProperty = function(propId, propValue) { |
216 | this.last_req = this.request("PUT", this.uri + propId,{ |
217 | @@ -220,13 +245,13 @@ |
218 | }); |
219 | CouchDB.maybeThrowError(this.last_req); |
220 | return JSON.parse(this.last_req.responseText); |
221 | - } |
222 | + }; |
223 | |
224 | this.getDbProperty = function(propId) { |
225 | this.last_req = this.request("GET", this.uri + propId); |
226 | CouchDB.maybeThrowError(this.last_req); |
227 | return JSON.parse(this.last_req.responseText); |
228 | - } |
229 | + }; |
230 | |
231 | this.setAdmins = function(adminsArray) { |
232 | this.last_req = this.request("PUT", this.uri + "_admins",{ |
233 | @@ -234,18 +259,18 @@ |
234 | }); |
235 | CouchDB.maybeThrowError(this.last_req); |
236 | return JSON.parse(this.last_req.responseText); |
237 | - } |
238 | + }; |
239 | |
240 | this.getAdmins = function() { |
241 | this.last_req = this.request("GET", this.uri + "_admins"); |
242 | CouchDB.maybeThrowError(this.last_req); |
243 | return JSON.parse(this.last_req.responseText); |
244 | - } |
245 | + }; |
246 | |
247 | // Convert a options object to an url query string. |
248 | // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"' |
249 | function encodeOptions(options) { |
250 | - var buf = [] |
251 | + var buf = []; |
252 | if (typeof(options) == "object" && options !== null) { |
253 | for (var name in options) { |
254 | if (!options.hasOwnProperty(name)) continue; |
255 | @@ -278,22 +303,82 @@ |
256 | return object1; |
257 | } |
258 | |
259 | - |
260 | } |
261 | |
262 | -CouchDB.PORT_NUMBER = 5984; // default |
263 | - |
264 | // this is the XMLHttpRequest object from last request made by the following |
265 | // CouchDB.* functions (except for calls to request itself). |
266 | // Use this from callers to check HTTP status or header values of requests. |
267 | CouchDB.last_req = null; |
268 | |
269 | +CouchDB.login = function(username, password) { |
270 | + CouchDB.last_req = CouchDB.request("POST", "/_session", { |
271 | + headers: {"Content-Type": "application/x-www-form-urlencoded", |
272 | + "X-CouchDB-WWW-Authenticate": "Cookie"}, |
273 | + body: "username=" + encodeURIComponent(username) + "&password=" + encodeURIComponent(password) |
274 | + }); |
275 | + return JSON.parse(CouchDB.last_req.responseText); |
276 | +}; |
277 | + |
278 | +CouchDB.logout = function() { |
279 | + CouchDB.last_req = CouchDB.request("DELETE", "/_session", { |
280 | + headers: {"Content-Type": "application/x-www-form-urlencoded", |
281 | + "X-CouchDB-WWW-Authenticate": "Cookie"} |
282 | + }); |
283 | + return JSON.parse(CouchDB.last_req.responseText); |
284 | +}; |
285 | + |
286 | +CouchDB.createUser = function(username, password, email, roles, basicAuth) { |
287 | + var roles_str = ""; |
288 | + if (roles) { |
289 | + for (var i=0; i< roles.length; i++) { |
290 | + roles_str += "&roles=" + encodeURIComponent(roles[i]); |
291 | + } |
292 | + } |
293 | + var headers = {"Content-Type": "application/x-www-form-urlencoded"}; |
294 | + if (basicAuth) { |
295 | + headers['Authorization'] = basicAuth; |
296 | + } else { |
297 | + headers['X-CouchDB-WWW-Authenticate'] = 'Cookie'; |
298 | + } |
299 | + |
300 | + CouchDB.last_req = CouchDB.request("POST", "/_user/", { |
301 | + headers: headers, |
302 | + body: "username=" + encodeURIComponent(username) + "&password=" + encodeURIComponent(password) |
303 | + + "&email="+ encodeURIComponent(email)+ roles_str |
304 | + |
305 | + }); |
306 | + return JSON.parse(CouchDB.last_req.responseText); |
307 | +}; |
308 | + |
309 | +CouchDB.updateUser = function(username, email, roles, password, old_password) { |
310 | + var roles_str = ""; |
311 | + if (roles) { |
312 | + for (var i=0; i< roles.length; i++) { |
313 | + roles_str += "&roles=" + encodeURIComponent(roles[i]); |
314 | + } |
315 | + } |
316 | + |
317 | + var body = "email="+ encodeURIComponent(email)+ roles_str; |
318 | + |
319 | + if (typeof(password) != "undefined" && password) |
320 | + body += "&password=" + password; |
321 | + |
322 | + if (typeof(old_password) != "undefined" && old_password) |
323 | + body += "&old_password=" + old_password; |
324 | + |
325 | + CouchDB.last_req = CouchDB.request("PUT", "/_user/"+encodeURIComponent(username), { |
326 | + headers: {"Content-Type": "application/x-www-form-urlencoded", |
327 | + "X-CouchDB-WWW-Authenticate": "Cookie"}, |
328 | + body: body |
329 | + }); |
330 | + return JSON.parse(CouchDB.last_req.responseText); |
331 | +}; |
332 | |
333 | CouchDB.allDbs = function() { |
334 | CouchDB.last_req = CouchDB.request("GET", "/_all_dbs"); |
335 | CouchDB.maybeThrowError(CouchDB.last_req); |
336 | return JSON.parse(CouchDB.last_req.responseText); |
337 | -} |
338 | +}; |
339 | |
340 | CouchDB.allDesignDocs = function() { |
341 | var ddocs = {}, dbs = CouchDB.allDbs(); |
342 | @@ -308,7 +393,7 @@ |
343 | CouchDB.last_req = CouchDB.request("GET", "/"); |
344 | CouchDB.maybeThrowError(CouchDB.last_req); |
345 | return JSON.parse(CouchDB.last_req.responseText).version; |
346 | -} |
347 | +}; |
348 | |
349 | CouchDB.replicate = function(source, target, rep_options) { |
350 | rep_options = rep_options || {}; |
351 | @@ -319,29 +404,40 @@ |
352 | }); |
353 | CouchDB.maybeThrowError(CouchDB.last_req); |
354 | return JSON.parse(CouchDB.last_req.responseText); |
355 | -} |
356 | +}; |
357 | |
358 | -CouchDB.request = function(method, uri, options) { |
359 | - options = options || {}; |
360 | - var req = null; |
361 | +CouchDB.newXhr = function() { |
362 | if (typeof(XMLHttpRequest) != "undefined") { |
363 | - req = new XMLHttpRequest(); |
364 | + return new XMLHttpRequest(); |
365 | } else if (typeof(ActiveXObject) != "undefined") { |
366 | - req = new ActiveXObject("Microsoft.XMLHTTP"); |
367 | + return new ActiveXObject("Microsoft.XMLHTTP"); |
368 | } else { |
369 | throw new Error("No XMLHTTPRequest support detected"); |
370 | } |
371 | - req.open(method, "http://localhost:" + CouchDB.PORT_NUMBER + uri, false); |
372 | +}; |
373 | + |
374 | +CouchDB.request = function(method, uri, options) { |
375 | + options = options || {}; |
376 | + var req = CouchDB.newXhr(); |
377 | + var computed_uri = "http://localhost:" + CouchDB.port + uri; |
378 | + Bindwood.writeMessage("Opening a request to: " + computed_uri); |
379 | + req.open(method, computed_uri, false); |
380 | if (options.headers) { |
381 | + Bindwood.writeMessage("Setting optional headers"); |
382 | var headers = options.headers; |
383 | for (var headerName in headers) { |
384 | if (!headers.hasOwnProperty(headerName)) continue; |
385 | req.setRequestHeader(headerName, headers[headerName]); |
386 | } |
387 | } |
388 | - req.send(options.body || ""); |
389 | + Bindwood.writeMessage("Sending the request"); |
390 | + try { |
391 | + req.send(options.body || ""); |
392 | + } catch(e) { |
393 | + Bindwood.writeError("Problem sending request: ", e); |
394 | + } |
395 | return req; |
396 | -} |
397 | +}; |
398 | |
399 | CouchDB.requestStats = function(module, key, test) { |
400 | var query_arg = ""; |
401 | @@ -351,7 +447,7 @@ |
402 | |
403 | var stat = CouchDB.request("GET", "/_stats/" + module + "/" + key + query_arg).responseText; |
404 | return JSON.parse(stat)[module][key]; |
405 | -} |
406 | +}; |
407 | |
408 | CouchDB.uuids_cache = []; |
409 | |
410 | @@ -373,7 +469,7 @@ |
411 | CouchDB.uuids_cache.concat(result.uuids.slice(0, 100)); |
412 | return result.uuids.slice(100); |
413 | } |
414 | -} |
415 | +}; |
416 | |
417 | CouchDB.maybeThrowError = function(req) { |
418 | if (req.status >= 400) { |
419 | @@ -384,7 +480,7 @@ |
420 | } |
421 | throw result; |
422 | } |
423 | -} |
424 | +}; |
425 | |
426 | CouchDB.params = function(options) { |
427 | options = options || {}; |
428 | @@ -394,4 +490,4 @@ |
429 | returnArray.push(key + "=" + value); |
430 | } |
431 | return returnArray.join("&"); |
432 | -} |
433 | +}; |
434 | |
435 | === added file 'content/oauth.js' |
436 | --- content/oauth.js 1970-01-01 00:00:00 +0000 |
437 | +++ content/oauth.js 2009-09-04 14:10:27 +0000 |
438 | @@ -0,0 +1,515 @@ |
439 | +/* |
440 | + * Copyright 2008 Netflix, Inc. |
441 | + * |
442 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
443 | + * you may not use this file except in compliance with the License. |
444 | + * You may obtain a copy of the License at |
445 | + * |
446 | + * http://www.apache.org/licenses/LICENSE-2.0 |
447 | + * |
448 | + * Unless required by applicable law or agreed to in writing, software |
449 | + * distributed under the License is distributed on an "AS IS" BASIS, |
450 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
451 | + * See the License for the specific language governing permissions and |
452 | + * limitations under the License. |
453 | + */ |
454 | + |
455 | +/* Here's some JavaScript software for implementing OAuth. |
456 | + |
457 | + This isn't as useful as you might hope. OAuth is based around |
458 | + allowing tools and websites to talk to each other. However, |
459 | + JavaScript running in web browsers is hampered by security |
460 | + restrictions that prevent code running on one website from |
461 | + accessing data stored or served on another. |
462 | + |
463 | + Before you start hacking, make sure you understand the limitations |
464 | + posed by cross-domain XMLHttpRequest. |
465 | + |
466 | + On the bright side, some platforms use JavaScript as their |
467 | + language, but enable the programmer to access other web sites. |
468 | + Examples include Google Gadgets, and Microsoft Vista Sidebar. |
469 | + For those platforms, this library should come in handy. |
470 | +*/ |
471 | + |
472 | +// The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by |
473 | +// http://pajhome.org.uk/crypt/md5/sha1.js |
474 | + |
475 | +/* An OAuth message is represented as an object like this: |
476 | + {method: "GET", action: "http://server.com/path", parameters: ...} |
477 | + |
478 | + The parameters may be either a map {name: value, name2: value2} |
479 | + or an Array of name-value pairs [[name, value], [name2, value2]]. |
480 | + The latter representation is more powerful: it supports parameters |
481 | + in a specific sequence, or several parameters with the same name; |
482 | + for example [["a", 1], ["b", 2], ["a", 3]]. |
483 | + |
484 | + Parameter names and values are NOT percent-encoded in an object. |
485 | + They must be encoded before transmission and decoded after reception. |
486 | + For example, this message object: |
487 | + {method: "GET", action: "http://server/path", parameters: {p: "x y"}} |
488 | + ... can be transmitted as an HTTP request that begins: |
489 | + GET /path?p=x%20y HTTP/1.0 |
490 | + (This isn't a valid OAuth request, since it lacks a signature etc.) |
491 | + Note that the object "x y" is transmitted as x%20y. To encode |
492 | + parameters, you can call OAuth.addToURL, OAuth.formEncode or |
493 | + OAuth.getAuthorization. |
494 | + |
495 | + This message object model harmonizes with the browser object model for |
496 | + input elements of an form, whose value property isn't percent encoded. |
497 | + The browser encodes each value before transmitting it. For example, |
498 | + see consumer.setInputs in example/consumer.js. |
499 | + */ |
500 | +var OAuth; if (OAuth == null) OAuth = {}; |
501 | + |
502 | +OAuth.setProperties = function setProperties(into, from) { |
503 | + if (into != null && from != null) { |
504 | + for (var key in from) { |
505 | + into[key] = from[key]; |
506 | + } |
507 | + } |
508 | + return into; |
509 | +} |
510 | + |
511 | +OAuth.setProperties(OAuth, // utility functions |
512 | +{ |
513 | + percentEncode: function percentEncode(s) { |
514 | + if (s == null) { |
515 | + return ""; |
516 | + } |
517 | + if (s instanceof Array) { |
518 | + var e = ""; |
519 | + for (var i = 0; i < s.length; ++s) { |
520 | + if (e != "") e += '&'; |
521 | + e += percentEncode(s[i]); |
522 | + } |
523 | + return e; |
524 | + } |
525 | + s = encodeURIComponent(s); |
526 | + // Now replace the values which encodeURIComponent doesn't do |
527 | + // encodeURIComponent ignores: - _ . ! ~ * ' ( ) |
528 | + // OAuth dictates the only ones you can ignore are: - _ . ~ |
529 | + // Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent |
530 | + s = s.replace(/\!/g, "%21"); |
531 | + s = s.replace(/\*/g, "%2A"); |
532 | + s = s.replace(/\'/g, "%27"); |
533 | + s = s.replace(/\(/g, "%28"); |
534 | + s = s.replace(/\)/g, "%29"); |
535 | + return s; |
536 | + } |
537 | +, |
538 | + decodePercent: function decodePercent(s) { |
539 | + if (s != null) { |
540 | + // Handle application/x-www-form-urlencoded, which is defined by |
541 | + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 |
542 | + s = s.replace(/\+/g, " "); |
543 | + } |
544 | + return decodeURIComponent(s); |
545 | + } |
546 | +, |
547 | + /** Convert the given parameters to an Array of name-value pairs. */ |
548 | + getParameterList: function getParameterList(parameters) { |
549 | + if (parameters == null) { |
550 | + return []; |
551 | + } |
552 | + if (typeof parameters != "object") { |
553 | + return decodeForm(parameters + ""); |
554 | + } |
555 | + if (parameters instanceof Array) { |
556 | + return parameters; |
557 | + } |
558 | + var list = []; |
559 | + for (var p in parameters) { |
560 | + list.push([p, parameters[p]]); |
561 | + } |
562 | + return list; |
563 | + } |
564 | +, |
565 | + /** Convert the given parameters to a map from name to value. */ |
566 | + getParameterMap: function getParameterMap(parameters) { |
567 | + if (parameters == null) { |
568 | + return {}; |
569 | + } |
570 | + if (typeof parameters != "object") { |
571 | + return getParameterMap(decodeForm(parameters + "")); |
572 | + } |
573 | + if (parameters instanceof Array) { |
574 | + var map = {}; |
575 | + for (var p = 0; p < parameters.length; ++p) { |
576 | + var key = parameters[p][0]; |
577 | + if (map[key] === undefined) { // first value wins |
578 | + map[key] = parameters[p][1]; |
579 | + } |
580 | + } |
581 | + return map; |
582 | + } |
583 | + return parameters; |
584 | + } |
585 | +, |
586 | + getParameter: function getParameter(parameters, name) { |
587 | + if (parameters instanceof Array) { |
588 | + for (var p = 0; p < parameters.length; ++p) { |
589 | + if (parameters[p][0] == name) { |
590 | + return parameters[p][1]; // first value wins |
591 | + } |
592 | + } |
593 | + } else { |
594 | + return OAuth.getParameterMap(parameters)[name]; |
595 | + } |
596 | + return null; |
597 | + } |
598 | +, |
599 | + formEncode: function formEncode(parameters) { |
600 | + var form = ""; |
601 | + var list = OAuth.getParameterList(parameters); |
602 | + for (var p = 0; p < list.length; ++p) { |
603 | + var value = list[p][1]; |
604 | + if (value == null) value = ""; |
605 | + if (form != "") form += '&'; |
606 | + form += OAuth.percentEncode(list[p][0]) |
607 | + +'='+ OAuth.percentEncode(value); |
608 | + } |
609 | + return form; |
610 | + } |
611 | +, |
612 | + decodeForm: function decodeForm(form) { |
613 | + var list = []; |
614 | + var nvps = form.split('&'); |
615 | + for (var n = 0; n < nvps.length; ++n) { |
616 | + var nvp = nvps[n]; |
617 | + if (nvp == "") { |
618 | + continue; |
619 | + } |
620 | + var equals = nvp.indexOf('='); |
621 | + var name; |
622 | + var value; |
623 | + if (equals < 0) { |
624 | + name = OAuth.decodePercent(nvp); |
625 | + value = null; |
626 | + } else { |
627 | + name = OAuth.decodePercent(nvp.substring(0, equals)); |
628 | + value = OAuth.decodePercent(nvp.substring(equals + 1)); |
629 | + } |
630 | + list.push([name, value]); |
631 | + } |
632 | + return list; |
633 | + } |
634 | +, |
635 | + setParameter: function setParameter(message, name, value) { |
636 | + var parameters = message.parameters; |
637 | + if (parameters instanceof Array) { |
638 | + for (var p = 0; p < parameters.length; ++p) { |
639 | + if (parameters[p][0] == name) { |
640 | + if (value === undefined) { |
641 | + parameters.splice(p, 1); |
642 | + } else { |
643 | + parameters[p][1] = value; |
644 | + value = undefined; |
645 | + } |
646 | + } |
647 | + } |
648 | + if (value !== undefined) { |
649 | + parameters.push([name, value]); |
650 | + } |
651 | + } else { |
652 | + parameters = OAuth.getParameterMap(parameters); |
653 | + parameters[name] = value; |
654 | + message.parameters = parameters; |
655 | + } |
656 | + } |
657 | +, |
658 | + setParameters: function setParameters(message, parameters) { |
659 | + var list = OAuth.getParameterList(parameters); |
660 | + for (var i = 0; i < list.length; ++i) { |
661 | + OAuth.setParameter(message, list[i][0], list[i][1]); |
662 | + } |
663 | + } |
664 | +, |
665 | + /** Fill in parameters to help construct a request message. |
666 | + This function doesn't fill in every parameter. |
667 | + The accessor object should be like: |
668 | + {consumerKey:'foo', consumerSecret:'bar', accessorSecret:'nurn', token:'krelm', tokenSecret:'blah'} |
669 | + The accessorSecret property is optional. |
670 | + */ |
671 | + completeRequest: function completeRequest(message, accessor) { |
672 | + if (message.method == null) { |
673 | + message.method = "GET"; |
674 | + } |
675 | + var map = OAuth.getParameterMap(message.parameters); |
676 | + if (map.oauth_consumer_key == null) { |
677 | + OAuth.setParameter(message, "oauth_consumer_key", accessor.consumerKey || ""); |
678 | + } |
679 | + if (map.oauth_token == null && accessor.token != null) { |
680 | + OAuth.setParameter(message, "oauth_token", accessor.token); |
681 | + } |
682 | + if (map.oauth_version == null) { |
683 | + OAuth.setParameter(message, "oauth_version", "1.0"); |
684 | + } |
685 | + if (map.oauth_timestamp == null) { |
686 | + OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); |
687 | + } |
688 | + if (map.oauth_nonce == null) { |
689 | + OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); |
690 | + } |
691 | + OAuth.SignatureMethod.sign(message, accessor); |
692 | + } |
693 | +, |
694 | + setTimestampAndNonce: function setTimestampAndNonce(message) { |
695 | + OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); |
696 | + OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); |
697 | + } |
698 | +, |
699 | + addToURL: function addToURL(url, parameters) { |
700 | + newURL = url; |
701 | + if (parameters != null) { |
702 | + var toAdd = OAuth.formEncode(parameters); |
703 | + if (toAdd.length > 0) { |
704 | + var q = url.indexOf('?'); |
705 | + if (q < 0) newURL += '?'; |
706 | + else newURL += '&'; |
707 | + newURL += toAdd; |
708 | + } |
709 | + } |
710 | + return newURL; |
711 | + } |
712 | +, |
713 | + /** Construct the value of the Authorization header for an HTTP request. */ |
714 | + getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) { |
715 | + var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"'; |
716 | + var list = OAuth.getParameterList(parameters); |
717 | + for (var p = 0; p < list.length; ++p) { |
718 | + var parameter = list[p]; |
719 | + var name = parameter[0]; |
720 | + if (name.indexOf("oauth_") == 0) { |
721 | + header += ',' + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"'; |
722 | + } |
723 | + } |
724 | + return header; |
725 | + } |
726 | +, |
727 | + timestamp: function timestamp() { |
728 | + var d = new Date(); |
729 | + return Math.floor(d.getTime()/1000); |
730 | + } |
731 | +, |
732 | + nonce: function nonce(length) { |
733 | + var chars = OAuth.nonce.CHARS; |
734 | + var result = ""; |
735 | + for (var i = 0; i < length; ++i) { |
736 | + var rnum = Math.floor(Math.random() * chars.length); |
737 | + result += chars.substring(rnum, rnum+1); |
738 | + } |
739 | + return result; |
740 | + } |
741 | +}); |
742 | + |
743 | +OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; |
744 | + |
745 | +/** Define a constructor function, |
746 | + without causing trouble to anyone who was using it as a namespace. |
747 | + That is, if parent[name] already existed and had properties, |
748 | + copy those properties into the new constructor. |
749 | + */ |
750 | +OAuth.declareClass = function declareClass(parent, name, newConstructor) { |
751 | + var previous = parent[name]; |
752 | + parent[name] = newConstructor; |
753 | + if (newConstructor != null && previous != null) { |
754 | + for (var key in previous) { |
755 | + if (key != "prototype") { |
756 | + newConstructor[key] = previous[key]; |
757 | + } |
758 | + } |
759 | + } |
760 | + return newConstructor; |
761 | +} |
762 | + |
763 | +/** An abstract algorithm for signing messages. */ |
764 | +OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod(){}); |
765 | + |
766 | +OAuth.setProperties(OAuth.SignatureMethod.prototype, // instance members |
767 | +{ |
768 | + /** Add a signature to the message. */ |
769 | + sign: function sign(message) { |
770 | + var baseString = OAuth.SignatureMethod.getBaseString(message); |
771 | + var signature = this.getSignature(baseString); |
772 | + OAuth.setParameter(message, "oauth_signature", signature); |
773 | + return signature; // just in case someone's interested |
774 | + } |
775 | +, |
776 | + /** Set the key string for signing. */ |
777 | + initialize: function initialize(name, accessor) { |
778 | + var consumerSecret; |
779 | + if (accessor.accessorSecret != null |
780 | + && name.length > 9 |
781 | + && name.substring(name.length-9) == "-Accessor") |
782 | + { |
783 | + consumerSecret = accessor.accessorSecret; |
784 | + } else { |
785 | + consumerSecret = accessor.consumerSecret; |
786 | + } |
787 | + this.key = OAuth.percentEncode(consumerSecret) |
788 | + +"&"+ OAuth.percentEncode(accessor.tokenSecret); |
789 | + } |
790 | +}); |
791 | + |
792 | +/* SignatureMethod expects an accessor object to be like this: |
793 | + {tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."} |
794 | + The accessorSecret property is optional. |
795 | + */ |
796 | +// Class members: |
797 | +OAuth.setProperties(OAuth.SignatureMethod, // class members |
798 | +{ |
799 | + sign: function sign(message, accessor) { |
800 | + var name = OAuth.getParameterMap(message.parameters).oauth_signature_method; |
801 | + if (name == null || name == "") { |
802 | + name = "HMAC-SHA1"; |
803 | + OAuth.setParameter(message, "oauth_signature_method", name); |
804 | + } |
805 | + OAuth.SignatureMethod.newMethod(name, accessor).sign(message); |
806 | + } |
807 | +, |
808 | + /** Instantiate a SignatureMethod for the given method name. */ |
809 | + newMethod: function newMethod(name, accessor) { |
810 | + var impl = OAuth.SignatureMethod.REGISTERED[name]; |
811 | + if (impl != null) { |
812 | + var method = new impl(); |
813 | + method.initialize(name, accessor); |
814 | + return method; |
815 | + } |
816 | + var err = new Error("signature_method_rejected"); |
817 | + var acceptable = ""; |
818 | + for (var r in OAuth.SignatureMethod.REGISTERED) { |
819 | + if (acceptable != "") acceptable += '&'; |
820 | + acceptable += OAuth.percentEncode(r); |
821 | + } |
822 | + err.oauth_acceptable_signature_methods = acceptable; |
823 | + throw err; |
824 | + } |
825 | +, |
826 | + /** A map from signature method name to constructor. */ |
827 | + REGISTERED : {} |
828 | +, |
829 | + /** Subsequently, the given constructor will be used for the named methods. |
830 | + The constructor will be called with no parameters. |
831 | + The resulting object should usually implement getSignature(baseString). |
832 | + You can easily define such a constructor by calling makeSubclass, below. |
833 | + */ |
834 | + registerMethodClass: function registerMethodClass(names, classConstructor) { |
835 | + for (var n = 0; n < names.length; ++n) { |
836 | + OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor; |
837 | + } |
838 | + } |
839 | +, |
840 | + /** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */ |
841 | + makeSubclass: function makeSubclass(getSignatureFunction) { |
842 | + var superClass = OAuth.SignatureMethod; |
843 | + var subClass = function() { |
844 | + superClass.call(this); |
845 | + }; |
846 | + subClass.prototype = new superClass(); |
847 | + // Delete instance variables from prototype: |
848 | + // delete subclass.prototype... There aren't any. |
849 | + subClass.prototype.getSignature = getSignatureFunction; |
850 | + subClass.prototype.constructor = subClass; |
851 | + return subClass; |
852 | + } |
853 | +, |
854 | + getBaseString: function getBaseString(message) { |
855 | + var URL = message.action; |
856 | + var q = URL.indexOf('?'); |
857 | + var parameters; |
858 | + if (q < 0) { |
859 | + parameters = message.parameters; |
860 | + } else { |
861 | + // Combine the URL query string with the other parameters: |
862 | + parameters = OAuth.decodeForm(URL.substring(q + 1)); |
863 | + var toAdd = OAuth.getParameterList(message.parameters); |
864 | + for (var a = 0; a < toAdd.length; ++a) { |
865 | + parameters.push(toAdd[a]); |
866 | + } |
867 | + } |
868 | + return OAuth.percentEncode(message.method.toUpperCase()) |
869 | + +'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL)) |
870 | + +'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters)); |
871 | + } |
872 | +, |
873 | + normalizeUrl: function normalizeUrl(url) { |
874 | + var uri = OAuth.SignatureMethod.parseUri(url); |
875 | + var scheme = uri.protocol.toLowerCase(); |
876 | + var authority = uri.authority.toLowerCase(); |
877 | + var dropPort = (scheme == "http" && uri.port == 80) |
878 | + || (scheme == "https" && uri.port == 443); |
879 | + if (dropPort) { |
880 | + // find the last : in the authority |
881 | + var index = authority.lastIndexOf(":"); |
882 | + if (index >= 0) { |
883 | + authority = authority.substring(0, index); |
884 | + } |
885 | + } |
886 | + var path = uri.path; |
887 | + /* This actually produces different signatures from the python lib |
888 | + if (!path) { |
889 | + path = "/"; // conforms to RFC 2616 section 3.2.2 |
890 | + } |
891 | + */ |
892 | + // we know that there is no query and no fragment here. |
893 | + return scheme + "://" + authority + path; |
894 | + } |
895 | +, |
896 | + parseUri: function parseUri (str) { |
897 | + /* This function was adapted from parseUri 1.2.1 |
898 | + http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js |
899 | + */ |
900 | + var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], |
901 | + parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }}; |
902 | + var m = o.parser.strict.exec(str); |
903 | + var uri = {}; |
904 | + var i = 14; |
905 | + while (i--) uri[o.key[i]] = m[i] || ""; |
906 | + return uri; |
907 | + } |
908 | +, |
909 | + normalizeParameters: function normalizeParameters(parameters) { |
910 | + if (parameters == null) { |
911 | + return ""; |
912 | + } |
913 | + var list = OAuth.getParameterList(parameters); |
914 | + var sortable = []; |
915 | + for (var p = 0; p < list.length; ++p) { |
916 | + var nvp = list[p]; |
917 | + if (nvp[0] == "oauth_signature" || nvp[0] == "realm") { |
918 | + continue; |
919 | + } else { |
920 | + sortable.push([ OAuth.percentEncode(nvp[0]) |
921 | + + " " // because it comes before any character that can appear in a percentEncoded string. |
922 | + + OAuth.percentEncode(nvp[1]) |
923 | + , nvp]); |
924 | + } |
925 | + } |
926 | + sortable.sort(function(a,b) { |
927 | + if (a[0] < b[0]) return -1; |
928 | + if (a[0] > b[0]) return 1; |
929 | + return 0; |
930 | + }); |
931 | + var sorted = []; |
932 | + for (var s = 0; s < sortable.length; ++s) { |
933 | + sorted.push(sortable[s][1]); |
934 | + } |
935 | + return OAuth.formEncode(sorted); |
936 | + } |
937 | +}); |
938 | + |
939 | +OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"], |
940 | + OAuth.SignatureMethod.makeSubclass( |
941 | + function getSignature(baseString) { |
942 | + return this.key; |
943 | + } |
944 | + )); |
945 | + |
946 | +OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"], |
947 | + OAuth.SignatureMethod.makeSubclass( |
948 | + function getSignature(baseString) { |
949 | + b64pad = '='; |
950 | + var signature = b64_hmac_sha1(this.key, baseString); |
951 | + return signature; |
952 | + } |
953 | + )); |
954 | |
955 | === added file 'content/sha1.js' |
956 | --- content/sha1.js 1970-01-01 00:00:00 +0000 |
957 | +++ content/sha1.js 2009-09-02 17:07:11 +0000 |
958 | @@ -0,0 +1,202 @@ |
959 | +/* |
960 | + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined |
961 | + * in FIPS PUB 180-1 |
962 | + * Version 2.1a Copyright Paul Johnston 2000 - 2002. |
963 | + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet |
964 | + * Distributed under the BSD License |
965 | + * See http://pajhome.org.uk/crypt/md5 for details. |
966 | + */ |
967 | + |
968 | +/* |
969 | + * Configurable variables. You may need to tweak these to be compatible with |
970 | + * the server-side, but the defaults work in most cases. |
971 | + */ |
972 | +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ |
973 | +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ |
974 | +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ |
975 | + |
976 | +/* |
977 | + * These are the functions you'll usually want to call |
978 | + * They take string arguments and return either hex or base-64 encoded strings |
979 | + */ |
980 | +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} |
981 | +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} |
982 | +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} |
983 | +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} |
984 | +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} |
985 | +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} |
986 | + |
987 | +/* |
988 | + * Perform a simple self-test to see if the VM is working |
989 | + */ |
990 | +function sha1_vm_test() |
991 | +{ |
992 | + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; |
993 | +} |
994 | + |
995 | +/* |
996 | + * Calculate the SHA-1 of an array of big-endian words, and a bit length |
997 | + */ |
998 | +function core_sha1(x, len) |
999 | +{ |
1000 | + /* append padding */ |
1001 | + x[len >> 5] |= 0x80 << (24 - len % 32); |
1002 | + x[((len + 64 >> 9) << 4) + 15] = len; |
1003 | + |
1004 | + var w = Array(80); |
1005 | + var a = 1732584193; |
1006 | + var b = -271733879; |
1007 | + var c = -1732584194; |
1008 | + var d = 271733878; |
1009 | + var e = -1009589776; |
1010 | + |
1011 | + for(var i = 0; i < x.length; i += 16) |
1012 | + { |
1013 | + var olda = a; |
1014 | + var oldb = b; |
1015 | + var oldc = c; |
1016 | + var oldd = d; |
1017 | + var olde = e; |
1018 | + |
1019 | + for(var j = 0; j < 80; j++) |
1020 | + { |
1021 | + if(j < 16) w[j] = x[i + j]; |
1022 | + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); |
1023 | + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), |
1024 | + safe_add(safe_add(e, w[j]), sha1_kt(j))); |
1025 | + e = d; |
1026 | + d = c; |
1027 | + c = rol(b, 30); |
1028 | + b = a; |
1029 | + a = t; |
1030 | + } |
1031 | + |
1032 | + a = safe_add(a, olda); |
1033 | + b = safe_add(b, oldb); |
1034 | + c = safe_add(c, oldc); |
1035 | + d = safe_add(d, oldd); |
1036 | + e = safe_add(e, olde); |
1037 | + } |
1038 | + return Array(a, b, c, d, e); |
1039 | + |
1040 | +} |
1041 | + |
1042 | +/* |
1043 | + * Perform the appropriate triplet combination function for the current |
1044 | + * iteration |
1045 | + */ |
1046 | +function sha1_ft(t, b, c, d) |
1047 | +{ |
1048 | + if(t < 20) return (b & c) | ((~b) & d); |
1049 | + if(t < 40) return b ^ c ^ d; |
1050 | + if(t < 60) return (b & c) | (b & d) | (c & d); |
1051 | + return b ^ c ^ d; |
1052 | +} |
1053 | + |
1054 | +/* |
1055 | + * Determine the appropriate additive constant for the current iteration |
1056 | + */ |
1057 | +function sha1_kt(t) |
1058 | +{ |
1059 | + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : |
1060 | + (t < 60) ? -1894007588 : -899497514; |
1061 | +} |
1062 | + |
1063 | +/* |
1064 | + * Calculate the HMAC-SHA1 of a key and some data |
1065 | + */ |
1066 | +function core_hmac_sha1(key, data) |
1067 | +{ |
1068 | + var bkey = str2binb(key); |
1069 | + if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); |
1070 | + |
1071 | + var ipad = Array(16), opad = Array(16); |
1072 | + for(var i = 0; i < 16; i++) |
1073 | + { |
1074 | + ipad[i] = bkey[i] ^ 0x36363636; |
1075 | + opad[i] = bkey[i] ^ 0x5C5C5C5C; |
1076 | + } |
1077 | + |
1078 | + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); |
1079 | + return core_sha1(opad.concat(hash), 512 + 160); |
1080 | +} |
1081 | + |
1082 | +/* |
1083 | + * Add integers, wrapping at 2^32. This uses 16-bit operations internally |
1084 | + * to work around bugs in some JS interpreters. |
1085 | + */ |
1086 | +function safe_add(x, y) |
1087 | +{ |
1088 | + var lsw = (x & 0xFFFF) + (y & 0xFFFF); |
1089 | + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); |
1090 | + return (msw << 16) | (lsw & 0xFFFF); |
1091 | +} |
1092 | + |
1093 | +/* |
1094 | + * Bitwise rotate a 32-bit number to the left. |
1095 | + */ |
1096 | +function rol(num, cnt) |
1097 | +{ |
1098 | + return (num << cnt) | (num >>> (32 - cnt)); |
1099 | +} |
1100 | + |
1101 | +/* |
1102 | + * Convert an 8-bit or 16-bit string to an array of big-endian words |
1103 | + * In 8-bit function, characters >255 have their hi-byte silently ignored. |
1104 | + */ |
1105 | +function str2binb(str) |
1106 | +{ |
1107 | + var bin = Array(); |
1108 | + var mask = (1 << chrsz) - 1; |
1109 | + for(var i = 0; i < str.length * chrsz; i += chrsz) |
1110 | + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); |
1111 | + return bin; |
1112 | +} |
1113 | + |
1114 | +/* |
1115 | + * Convert an array of big-endian words to a string |
1116 | + */ |
1117 | +function binb2str(bin) |
1118 | +{ |
1119 | + var str = ""; |
1120 | + var mask = (1 << chrsz) - 1; |
1121 | + for(var i = 0; i < bin.length * 32; i += chrsz) |
1122 | + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); |
1123 | + return str; |
1124 | +} |
1125 | + |
1126 | +/* |
1127 | + * Convert an array of big-endian words to a hex string. |
1128 | + */ |
1129 | +function binb2hex(binarray) |
1130 | +{ |
1131 | + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; |
1132 | + var str = ""; |
1133 | + for(var i = 0; i < binarray.length * 4; i++) |
1134 | + { |
1135 | + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + |
1136 | + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); |
1137 | + } |
1138 | + return str; |
1139 | +} |
1140 | + |
1141 | +/* |
1142 | + * Convert an array of big-endian words to a base-64 string |
1143 | + */ |
1144 | +function binb2b64(binarray) |
1145 | +{ |
1146 | + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
1147 | + var str = ""; |
1148 | + for(var i = 0; i < binarray.length * 4; i += 3) |
1149 | + { |
1150 | + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) |
1151 | + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |
1152 | + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); |
1153 | + for(var j = 0; j < 4; j++) |
1154 | + { |
1155 | + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; |
1156 | + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); |
1157 | + } |
1158 | + } |
1159 | + return str; |
1160 | +} |
1161 | |
1162 | === modified file 'content/sync.js' |
1163 | --- content/sync.js 2009-08-27 18:45:48 +0000 |
1164 | +++ content/sync.js 2009-09-08 15:29:55 +0000 |
1165 | @@ -112,11 +112,11 @@ |
1166 | .getService(Components.interfaces.nsIWindowMediator) |
1167 | .getEnumerator("").getNext() == window) { |
1168 | Bindwood.writeMessage("First window opened. Getting a Couch Port"); |
1169 | - Bindwood.getCouchPortNumber(Bindwood.startProcess); |
1170 | + Bindwood.getCouchEnvironment(Bindwood.startProcess); |
1171 | } |
1172 | }, |
1173 | |
1174 | - getCouchPortNumber: function(continueFunction) { |
1175 | + getCouchEnvironment: function(continueFunction) { |
1176 | // find the desktop Couch port number by making a D-Bus call |
1177 | // we call D-Bus by shelling out to a bash script which calls |
1178 | // it for us, and writes the port number into a temp file |
1179 | @@ -139,12 +139,12 @@ |
1180 | var MY_ID = "bindwood@ubuntu.com"; |
1181 | var em = Components.classes["@mozilla.org/extensions/manager;1"]. |
1182 | getService(Components.interfaces.nsIExtensionManager); |
1183 | - var dbus_script = em.getInstallLocation(MY_ID).getItemFile(MY_ID, "dbus.sh"); |
1184 | - Bindwood.writeMessage("Found path to dbus_script: " + dbus_script.path); |
1185 | + var couchdb_env_script = em.getInstallLocation(MY_ID).getItemFile(MY_ID, "couchdb_env.sh"); |
1186 | + Bindwood.writeMessage("Found path to dbus_script: " + couchdb_env_script.path); |
1187 | // create an nsILocalFile for the executable |
1188 | var nsifile = Components.classes["@mozilla.org/file/local;1"] |
1189 | .createInstance(Components.interfaces.nsILocalFile); |
1190 | - nsifile.initWithPath(dbus_script.path); |
1191 | + nsifile.initWithPath(couchdb_env_script.path); |
1192 | |
1193 | // create an nsIProcess2 to execute this bash script |
1194 | var process = Components.classes["@mozilla.org/process/util;1"] |
1195 | @@ -153,14 +153,16 @@ |
1196 | |
1197 | // Run the process, passing the tmpfile path |
1198 | var args = [tmpfile.path]; |
1199 | - Bindwood.writeMessage("Running dbus script"); |
1200 | + Bindwood.writeMessage("Running couchdb env script"); |
1201 | process.runAsync(args, args.length, { |
1202 | observe: function(process, finishState, unused_data) { |
1203 | - var port = 5984; |
1204 | + // If the script exists cleanly, we should have a file |
1205 | + // containing the port couch is running on as well as |
1206 | + // the various OAuth tokens necessary to talk to it. |
1207 | if (finishState == "process-finished") { |
1208 | - // read temp file to find port number |
1209 | + // read temp file to find couch environment |
1210 | // https://developer.mozilla.org/en/Code_snippets/File_I%2f%2fO#Reading_from_a_file |
1211 | - var data = ""; |
1212 | + var environment; |
1213 | var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. |
1214 | createInstance(Components.interfaces.nsIFileInputStream); |
1215 | var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. |
1216 | @@ -169,28 +171,56 @@ |
1217 | cstream.init(fstream, "UTF-8", 0, 0); |
1218 | let (str = {}) { |
1219 | cstream.readString(-1, str); // read the whole file and put it in str.value |
1220 | - data = str.value; |
1221 | + environment = str.value; |
1222 | }; |
1223 | cstream.close(); // this closes fstream |
1224 | - data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
1225 | - if (/^[0-9]+$/.test(data)) { |
1226 | - port = data; |
1227 | - } else { |
1228 | - Bindwood.writeMessage("D-Bus port data is not a number (" + data + ")"); |
1229 | - } |
1230 | + environment = environment.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
1231 | } else { |
1232 | // fall back to system CouchDB |
1233 | Bindwood.writeMessage("D-Bus port find failed"); |
1234 | } |
1235 | tmpfile.remove(false); |
1236 | - continueFunction(port); |
1237 | - } |
1238 | + |
1239 | + if (environment == 'ENOCOUCH') { |
1240 | + // No Couch environment found. Just spit out a |
1241 | + // message and return, stopping Bindwood from |
1242 | + // doing anything further. |
1243 | + Bindwood.writeError("No suitable Couch environment found. Not proceeding.", e); |
1244 | + } else { |
1245 | + // If we don't have a Couch environment, don't bother |
1246 | + // trying to fall back on the system CouchDB. |
1247 | + continueFunction(environment); |
1248 | + } |
1249 | + } |
1250 | }); |
1251 | }, |
1252 | |
1253 | - startProcess: function(couchPortNumber) { |
1254 | - Bindwood.writeMessage("Starting process with Couch on port " + couchPortNumber); |
1255 | - CouchDB.PORT_NUMBER = couchPortNumber; |
1256 | + startProcess: function(couchEnvironment) { |
1257 | + Bindwood.writeMessage("Starting process with Couch environment: " + couchEnvironment); |
1258 | + |
1259 | + var env_array = couchEnvironment.split(':'); |
1260 | + var port = env_array[0]; |
1261 | + var consumer_key = env_array[1]; |
1262 | + var consumer_secret = env_array[2]; |
1263 | + var token = env_array[3]; |
1264 | + var token_secret = env_array[4]; |
1265 | + |
1266 | + CouchDB.port = port; |
1267 | + CouchDB.accessor = { |
1268 | + consumerSecret: consumer_secret, |
1269 | + tokenSecret: token_secret |
1270 | + }; |
1271 | + CouchDB.message = { |
1272 | + parameters: { |
1273 | + oauth_callback: "None", |
1274 | + oauth_consumer_key: consumer_key, |
1275 | + oauth_signature_method: "HMAC-SHA1", |
1276 | + oauth_token: token, |
1277 | + oauth_verifier: "None", |
1278 | + oauth_version: "1.0" |
1279 | + } |
1280 | + }; |
1281 | + |
1282 | try { |
1283 | Bindwood.writeMessage("Pushing bookmarks"); |
1284 | Bindwood.pushBookmarks(); |
1285 | @@ -260,10 +290,19 @@ |
1286 | } |
1287 | } |
1288 | |
1289 | - Bindwood.pushBookmarksFromList(Bindwood.bookmarksService.toolbarFolder, |
1290 | - "toolbarFolder", couch); |
1291 | - Bindwood.pushBookmarksFromList(Bindwood.bookmarksService.bookmarksMenuFolder, |
1292 | - "bookmarksMenuFolder", couch); |
1293 | + try { |
1294 | + Bindwood.pushBookmarksFromList(Bindwood.bookmarksService.toolbarFolder, |
1295 | + "toolbarFolder", couch); |
1296 | + } catch(e) { |
1297 | + Bindwood.writeError("Error pushing toolbarFolder bookmarks: ", e); |
1298 | + } |
1299 | + |
1300 | + try { |
1301 | + Bindwood.pushBookmarksFromList(Bindwood.bookmarksService.bookmarksMenuFolder, |
1302 | + "bookmarksMenuFolder", couch); |
1303 | + } catch(e) { |
1304 | + Bindwood.writeError("Error pushing bookmarksMenuFolder bookmarks: ", e); |
1305 | + } |
1306 | }, |
1307 | |
1308 | getBookmarksFromList: function(bookmarksList) { |
1309 | @@ -320,15 +359,19 @@ |
1310 | |
1311 | Bindwood.uuidItemIdMap[uuid] = itemId; |
1312 | |
1313 | - var results = db.query(function(doc) { |
1314 | - if (doc.application_annotations && |
1315 | - doc.application_annotations.Firefox && |
1316 | - doc.application_annotations.Firefox.uuid) { |
1317 | - emit(doc.application_annotations.Firefox.uuid, doc); |
1318 | - } |
1319 | - }, null, { |
1320 | - startkey: uuid, endkey: uuid |
1321 | - }); |
1322 | + try { |
1323 | + var results = db.query(function(doc) { |
1324 | + if (doc.application_annotations && |
1325 | + doc.application_annotations.Firefox && |
1326 | + doc.application_annotations.Firefox.uuid) { |
1327 | + emit(doc.application_annotations.Firefox.uuid, doc); |
1328 | + } |
1329 | + }, null, { |
1330 | + startkey: uuid, endkey: uuid |
1331 | + }); |
1332 | + } catch(e) { |
1333 | + Bindwood.writeError("Error querying couch: ", e); |
1334 | + } |
1335 | |
1336 | if (results.rows.length === 0) { |
1337 | // this bookmark is not in CouchDB, so write it |
1338 | @@ -440,7 +483,7 @@ |
1339 | Bindwood.writeMessage("The URI from Couch (" + bm.uri + ") is different from local (" + metadata.spec + ")"); |
1340 | try { |
1341 | var new_uri = Bindwood.ioService.newURI(bm.uri, null, null); |
1342 | - Bindowod.writeMessage("Creating a new URI for our local bookmark"); |
1343 | + Bindwood.writeMessage("Creating a new URI for our local bookmark"); |
1344 | Bindwood.bookmarksService.changeBookmarkURI(itemId, new_uri); |
1345 | } catch(e) { |
1346 | Bindwood.writeError("Problem creating a new URI for bookmark: ", e); |
1347 | @@ -482,7 +525,7 @@ |
1348 | |
1349 | try { |
1350 | var new_uri = Bindwood.ioService.newURI(bm.uri, null, null); |
1351 | - Bindowod.writeMessage("Creating a new URI for our local bookmark"); |
1352 | + Bindwood.writeMessage("Creating a new URI for our local bookmark"); |
1353 | Bindwood.bookmarksService.changeBookmarkURI(itemId, new_uri); |
1354 | } catch(e) { |
1355 | Bindwood.writeError("Problem creating a new URI for bookmark: ", e); |
1356 | |
1357 | === renamed file 'dbus.sh' => 'couchdb_env.sh' |
1358 | --- dbus.sh 2009-08-18 14:17:30 +0000 |
1359 | +++ couchdb_env.sh 2009-09-01 19:40:34 +0000 |
1360 | @@ -13,12 +13,16 @@ |
1361 | # You should have received a copy of the GNU General Public License along |
1362 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
1363 | OUT=$1 |
1364 | -PORT=$(dbus-send --session --dest=org.desktopcouch.CouchDB --print-reply --type=method_call / org.desktopcouch.CouchDB.getPort 2>/dev/null | grep int32 | awk '{print $2}' ) |
1365 | + |
1366 | +PORT=$(dbus-send --session --dest=org.desktopcouch.CouchDB --print-reply --type=method_call / org.desktopcouch.CouchDB.getPort 2>/dev/null | grep int32 | awk '{print $2}') |
1367 | + |
1368 | +TOKENS=$(python -c "import gnomekeyring; print gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET, {'desktopcouch': 'oauth'})[0].secret" 2>/dev/null) |
1369 | + |
1370 | if [ -z "$PORT" ]; then |
1371 | - # D-Bus call failed for some reason, so use default port |
1372 | - echo 5984 > $OUT |
1373 | + # D-Bus call failed for some reason, so just punt |
1374 | + echo ENOCOUCH > $OUT |
1375 | else |
1376 | - echo $PORT > $OUT |
1377 | + echo $PORT:$TOKENS > $OUT |
1378 | fi |
1379 | |
1380 |
This branch enables OAuth integration with our Desktop CouchDB.