Merge pull request #512 from galfert/node_binary_fix
[remotestorage.js] / src / discover.js
1 (function(global) {
2
3   // feature detection flags
4   var haveXMLHttpRequest, haveLocalStorage;
5   // used to store settings in localStorage
6   var SETTINGS_KEY = 'remotestorage:discover';
7   // cache loaded from localStorage
8   var cachedInfo = {};
9
10   /**
11    * Class: RemoteStorage.Discover
12    *
13    * This class deals with the webfinger lookup
14    *
15    * Arguments:
16    * userAddress - user@host
17    * callback    - gets called with href of the storage, the type and the authURL
18    * Example:
19    * (start code)
20    *
21    * (end code)
22    **/
23
24   RemoteStorage.Discover = function(userAddress, callback) {
25     if (userAddress in cachedInfo) {
26       var info = cachedInfo[userAddress];
27       callback(info.href, info.type, info.authURL);
28       return;
29     }
30     var hostname = userAddress.split('@')[1];
31     var params = '?resource=' + encodeURIComponent('acct:' + userAddress);
32     var urls = [
33       'https://' + hostname + '/.well-known/webfinger' + params,
34       'https://' + hostname + '/.well-known/host-meta.json' + params,
35       'http://' + hostname + '/.well-known/webfinger' + params,
36       'http://' + hostname + '/.well-known/host-meta.json' + params
37     ];
38
39     function tryOne() {
40       var xhr = new XMLHttpRequest();
41       var url = urls.shift();
42       if (!url) { return callback(); }
43       RemoteStorage.log('try url', url);
44       xhr.open('GET', url, true);
45       xhr.onabort = xhr.onerror = function() {
46         console.error("webfinger error", arguments, '(', url, ')');
47         tryOne();
48       };
49       xhr.onload = function() {
50         if (xhr.status !== 200) { return tryOne(); }
51         var profile;
52
53         try {
54           profile = JSON.parse(xhr.responseText);
55         } catch(e) {
56           RemoteStorage.log("Failed to parse profile ", xhr.responseText, e);
57           tryOne();
58           return;
59         }
60
61         if (!profile.links) {
62           RemoteStorage.log("profile has no links section ", JSON.stringify(profile));
63           tryOne();
64           return;
65         }
66
67         var link;
68         profile.links.forEach(function(l) {
69           if (l.rel === 'remotestorage') {
70             link = l;
71           } else if (l.rel === 'remoteStorage' && !link) {
72             link = l;
73           }
74         });
75         RemoteStorage.log('got profile', profile, 'and link', link);
76         if (link) {
77           var authURL = link.properties['auth-endpoint'] ||
78             link.properties['http://tools.ietf.org/html/rfc6749#section-4.2'];
79           cachedInfo[userAddress] = { href: link.href, type: link.type, authURL: authURL };
80           if (haveLocalStorage) {
81             localStorage[SETTINGS_KEY] = JSON.stringify({ cache: cachedInfo });
82           }
83           callback(link.href, link.type, authURL);
84         } else {
85           tryOne();
86         }
87       };
88       xhr.send();
89     }
90     tryOne();
91   };
92
93   RemoteStorage.Discover._rs_init = function(remoteStorage) {
94     if (haveLocalStorage) {
95       var settings;
96       try { settings = JSON.parse(localStorage[SETTINGS_KEY]); } catch(e) {}
97       if (settings) {
98         cachedInfo = settings.cache;
99       }
100     }
101   };
102
103   RemoteStorage.Discover._rs_supported = function() {
104     haveLocalStorage = !! global.localStorage;
105     haveXMLHttpRequest = !! global.XMLHttpRequest;
106     return haveXMLHttpRequest;
107   };
108
109   RemoteStorage.Discover._rs_cleanup = function() {
110     if (haveLocalStorage) {
111       delete localStorage[SETTINGS_KEY];
112     }
113   };
114
115 })(typeof(window) !== 'undefined' ? window : global);