home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Multimedia / Songbird / Songbird_2.0.0-2311_windows-i686-msvc8.exe / components / sbLastFmWebServices.js < prev    next >
Text File  |  2012-06-08  |  8KB  |  260 lines

  1. /*
  2. //
  3. // BEGIN SONGBIRD GPL
  4. //
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright(c) 2005-2008 POTI, Inc.
  8. // http://songbirdnest.com
  9. //
  10. // This file may be licensed under the terms of of the
  11. // GNU General Public License Version 2 (the "GPL").
  12. //
  13. // Software distributed under the License is distributed
  14. // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
  15. // express or implied. See the GPL for the specific language
  16. // governing rights and limitations.
  17. //
  18. // You should have received a copy of the GPL along with this
  19. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  20. // or write to the Free Software Foundation, Inc.,
  21. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. //
  23. // END SONGBIRD GPL
  24. //
  25. */
  26.  
  27. // these constants make everything better
  28. const Cc = Components.classes;
  29. const CC = Components.Constructor;
  30. const Ci = Components.interfaces;
  31. const Cr = Components.results;
  32. const Cu = Components.utils;
  33.  
  34. // Last.fm API key, secret and URL
  35. const API_KEY = '4d5bce1e977549f10623b51dd0e10c5a';
  36. const API_SECRET = '3ebb03d4561260686b98388037931f11'; // obviously not secret
  37. const API_URL = 'http://ws.audioscrobbler.com/2.0/';
  38.  
  39. // Import some helper scripts
  40. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  41. Cu.import("resource://app/jsmodules/sbProperties.jsm");
  42.  
  43. // XPCOM Constants
  44. const CONTRACTID = "@songbirdnest.com/Songbird/webservices/last-fm;1";
  45. const CLASSNAME = "Songbird Last.FM WebService Interface";
  46. const CID = Components.ID("{6582d596-95dd-4449-be8b-7793a15bdfa2}");
  47. const IID = Ci.sbILastFmWebServices;
  48.  
  49. // helper for enumerating enumerators.
  50. function enumerate(enumerator, func) {
  51.   while(enumerator.hasMoreElements()) {
  52.     try {
  53.       func(enumerator.getNext());
  54.     } catch(e) {
  55.       Cu.reportError(e);
  56.     }
  57.   }
  58. }
  59.  
  60. // calculate a hex md5 digest thing
  61. function md5(str) {
  62.   var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
  63.       .createInstance(Ci.nsIScriptableUnicodeConverter);
  64.  
  65.   converter.charset = "UTF-8";
  66.   // result is an out parameter,
  67.   // result.value will contain the array length
  68.   var result = {};
  69.   // data is an array of bytes
  70.   var data = converter.convertToByteArray(str, result);
  71.   var ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
  72.   ch.init(ch.MD5);
  73.   ch.update(data, data.length);
  74.   var hash = ch.finish(false);
  75.  
  76.   // return the two-digit hexadecimal code for a byte
  77.   function toHexString(charCode) {
  78.     return ("0" + charCode.toString(16)).slice(-2);
  79.   }
  80.  
  81.   // convert the binary hash data to a hex string.
  82.   var s = [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
  83.   return s;
  84. }
  85.  
  86. // urlencode an object's keys & values
  87. function urlencode(o) {
  88.   s = '';
  89.   for (var k in o) {
  90.     var v = o[k];
  91.     if (s.length) { s += '&'; }
  92.     s += encodeURIComponent(k) + '=' + encodeURIComponent(v);
  93.   }
  94.   return s;
  95. }
  96.  
  97. // make an HTTP POST request
  98. function POST(url, params, onload, onerror) {
  99.   var xhr = null;
  100.   try {
  101.     // create the XMLHttpRequest object
  102.     xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
  103.     // don't tie it to a XUL window
  104.     xhr.mozBackgroundRequest = true;
  105.     // open the connection to the url
  106.     xhr.open('POST', url, true);
  107.     // set up event handlers
  108.     xhr.onload = function(event) { onload(xhr); }
  109.     xhr.onerror = function(event) { onerror(xhr); }
  110.     // we're always sending url encoded parameters
  111.     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  112.     // send url encoded parameters
  113.     xhr.send(urlencode(params));
  114.     // pass the XHR back to the caller
  115.   } catch(e) {
  116.     Cu.reportError(e);
  117.     onerror(xhr);
  118.   }
  119.   return xhr;
  120. }
  121.  
  122. /**
  123.  * sbILastFmWebServices
  124.  */
  125.  
  126. function sbLastFmWebServices() {
  127.   // our interface is really lightweight - make the service available as a JS
  128.   // object so we can avoid the IDL / XPConnect complexity.
  129.   this.wrappedJSObject = this;
  130.  
  131.   // report errors using the console service
  132.   this._console = Cc["@mozilla.org/consoleservice;1"]
  133.                     .getService(Ci.nsIConsoleService);
  134.  
  135. }
  136. sbLastFmWebServices.prototype = {
  137.   constructor: sbLastFmWebServices,       // Constructor to this object
  138.   classDescription:  CLASSNAME,
  139.   classID:           CID,
  140.   contractID:        CONTRACTID,
  141.   _xpcom_categories:
  142.   [{
  143.     category: "app-startup",
  144.     entry: "webservices-lastfm",
  145.     value: "service," + CONTRACTID
  146.   }],
  147.  
  148.   // Error reporting
  149.   log: function sbLastFmWebServices_log(message) {
  150.     this._console.logStringMessage('[last-fm] '+message);
  151.   },
  152.  
  153.   /**
  154.    * See sbILastFmWebServices.idl
  155.    */
  156.   apiCall: function sbLastFmWebServices_apiCall(aMethod,
  157.                                                 aArguments,
  158.                                                 aCallback) {
  159.     // make a new Last.fm Web Service API call
  160.     // see: http://www.last.fm/api/rest
  161.     // note: this isn't really REST.
  162.   
  163.     function callback(success, response) {
  164.       if (typeof(aCallback) == 'function') {
  165.         aCallback(success, response);
  166.       } else {
  167.         aCallback.responseReceived(success, response);
  168.       }
  169.     }
  170.   
  171.     // create an object to hold the HTTP params
  172.     var post_params = new Object();
  173.   
  174.     // load the params from the nsIPropertyBag
  175.     if (aArguments instanceof Ci.nsIPropertyBag) {
  176.       enumerate(aArguments.enumerator, function(item) {
  177.         item.QueryInterface(Ci.nsIProperty);
  178.         post_params[item.name] = item.value;
  179.       })
  180.     } else {
  181.       // or from the object
  182.       for (var k in aArguments) { post_params[k] = aArguments[k]; }
  183.     }
  184.   
  185.     // set the method and API key
  186.     post_params.method = aMethod;
  187.     post_params.api_key = API_KEY;
  188.   
  189.     // calculate the signature...
  190.     // put the key/value pairs in an array
  191.     var sorted_params = new Array();
  192.     for (var k in post_params) {
  193.       sorted_params.push(k+post_params[k])
  194.     }
  195.     // sort them
  196.     sorted_params.sort();
  197.     // join them into a string
  198.     sorted_params = sorted_params.join('');
  199.     // hash them with the "secret" to get the API signature
  200.     post_params.api_sig = md5(sorted_params+API_SECRET);
  201.     
  202.     // post the request
  203.     var self = this;
  204.     POST(API_URL, post_params, function (xhr) {
  205.       if (!xhr.responseXML) {
  206.         // we expect all API responses to have XML
  207.         self.log('Last.fm WebServices Error: No valid XML response.');
  208.         callback(false, null);
  209.         return;
  210.       }
  211.       
  212.       // Check for an error status
  213.       var nsResolver = xhr.responseXML.createNSResolver(
  214.                                 xhr.responseXML.ownerDocument == null ?
  215.                                 xhr.responseXML.documentElement :
  216.                                 xhr.responseXML.ownerDocument.documentElement);
  217.       var result = xhr.responseXML.evaluate("/lfm/@status",
  218.                                             xhr.responseXML,
  219.                                             nsResolver,
  220.                                             2,  //XPathResult.STRING_TYPE,
  221.                                             null);
  222.       if (result.stringValue && result.stringValue == 'failed') {
  223.         result = xhr.responseXML.evaluate("/lfm/error",
  224.                                           xhr.responseXML,
  225.                                           nsResolver,
  226.                                           2,  //XPathResult.STRING_TYPE,
  227.                                           null);
  228.         var error = "Unknown Error";
  229.         if (result.stringValue) {
  230.           error = result.stringValue;
  231.         }
  232.         self.log('Last.fm WebServices Error: ' + error);
  233.         callback(false, xhr.responseXML);
  234.         return;
  235.       }
  236.  
  237.       // all should be good!
  238.       callback(true, xhr.responseXML);
  239.     }, function (xhr) {
  240.       self.log('Last.fm WebServices Error: Bad response from server.');
  241.       callback(false, null);
  242.     });
  243.   },
  244.  
  245.   /**
  246.    * See nsISupports.idl
  247.    */
  248.   QueryInterface: XPCOMUtils.generateQI([IID])
  249. }
  250.  
  251. /**
  252.  * ----------------------------------------------------------------------------
  253.  * Registration for XPCOM
  254.  * ----------------------------------------------------------------------------
  255.  */
  256.  
  257. function NSGetModule(comMgr, fileSpec) {
  258.   return XPCOMUtils.generateModule([sbLastFmWebServices]);
  259. } // NSGetModule
  260.