home *** CD-ROM | disk | FTP | other *** search
- /**
- //
- // BEGIN SONGBIRD GPL
- //
- // This file is part of the Songbird web player.
- //
- // Copyright(c) 2005-2008 POTI, Inc.
- // http://songbirdnest.com
- //
- // This file may be licensed under the terms of of the
- // GNU General Public License Version 2 (the "GPL").
- //
- // Software distributed under the License is distributed
- // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
- // express or implied. See the GPL for the specific language
- // governing rights and limitations.
- //
- // You should have received a copy of the GPL along with this
- // program. If not, go to http://www.gnu.org/licenses/gpl.html
- // or write to the Free Software Foundation, Inc.,
- // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- //
- // END SONGBIRD GPL
- //
- */
- Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
- const SONGBIRD_METRICS_CONTRACTID = "@songbirdnest.com/Songbird/Metrics;1";
- const SONGBIRD_METRICS_CLASSNAME = "Songbird Metrics Service Interface";
- const SONGBIRD_METRICS_CID = Components.ID("{1066527d-b135-4e0c-9ea4-f6109ae97d02}");
- const SONGBIRD_METRICS_IID = Components.interfaces.sbIMetrics;
-
- const SONGBIRD_POSTMETRICS_PREFKEY = "songbird.url.metrics";
-
- const SONGBIRD_UPLOAD_METRICS_EVERY_NDAYS = 1; // every day
-
- function Metrics() {
- this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefBranch);
- }
-
- Metrics.prototype = {
- classDescription: SONGBIRD_METRICS_CLASSNAME,
- classID: SONGBIRD_METRICS_CID,
- contractID: SONGBIRD_METRICS_CONTRACTID,
- QueryInterface: XPCOMUtils.generateQI([
- SONGBIRD_METRICS_IID,
- Components.interfaces.nsIWebProgressListener,
- Components.interfaces.nsISupportsWeakReference
- ]),
-
- _postreq: null,
- _dbquery: null,
-
- LOG: function(str) {
- var consoleService = Components.classes['@mozilla.org/consoleservice;1']
- .getService(Components.interfaces.nsIConsoleService);
- consoleService.logStringMessage(str);
- },
-
-
- /**
- * Check to see if metrics should be submitted.
- */
- checkUploadMetrics: function()
- {
- if (!this._isEnabled()) return;
-
- var timeUp = this._isWaitPeriodUp();
-
- if (timeUp)
- {
- this.uploadMetrics();
- }
- },
-
- /**
- * Bundle all metrics info and send it to the server.
- *
- * TODO: Rethink version and OS strings
- */
- uploadMetrics: function()
- {
- dump("*** UPLOADING METRICS ***");
- var user_install_uuid = this._getPlayerUUID();
-
- var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULRuntime);
- var user_os = xulRuntime.OS;
-
-
- var metrics = this._getTable();
-
- var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
- // appInfo.name + " " + appInfo.version + " - " + appInfo.appBuildID;
-
- var abi = "Unknown";
- // Not all builds have a known ABI
- try {
- abi = appInfo.XPCOMABI;
-
- // TODO: Throwing an exception every time is bad.. should probably detect os x
-
- // Mac universal build should report a different ABI than either macppc
- // or mactel.
- var macutils = Components.classes["@mozilla.org/xpcom/mac-utils;1"]
- .getService(Components.interfaces.nsIMacUtils);
- if (macutils.isUniversalBinary) abi = "Universal-gcc3";
- }
- catch (e) {}
-
- var platform = appInfo.OS + "_" + abi;
-
- var tzo = (new Date()).getTimezoneOffset();
- var neg = (tzo < 0);
- tzo = Math.abs(tzo);
- var tzh = Math.floor(tzo / 60);
- var tzm = tzo - (tzh*60);
- // note: timezone is -XX:XX if the offset is positive, and +XX:00 if the offset is negative !
- // this is because the offset has the reverse sign compared to the timezone, since
- // the offset is what you should add to localtime to get UTC, so if you add -XX:XX, you're
- // subtracting XX:XX, because the timezone is UTC+XX:XX
- var tz = (neg ? "+" : "-") + this.formatDigits(tzh,2) + ":" + this.formatDigits(tzm,2);
-
- // build xml
-
- var xml = "";
- xml += '<metrics schema_version="2.0" guid="' + user_install_uuid
- + '" version="' + appInfo.version
- + '" build="' + appInfo.appBuildID
- + '" product="' + appInfo.name
- + '" platform="' + platform
- + '" os="' + user_os
- + '" timezone="' + tz
- + '">\n';
-
- for (var i = 0; i < metrics.length; i++)
- {
- var key = metrics[i][0];
- var val = metrics[i][1];
- if ( val > 0 )
- {
- var dot = key.indexOf(".");
- if (dot >= 0) {
- var timestamp = key.substr(0, dot);
- var cleanKey = key.substr(dot + 1);
- var date = new Date();
- date.setTime(timestamp);
-
- var hourstart = date.getFullYear() + "-" +
- this.formatDigits(date.getMonth()+1,2) + "-" +
- this.formatDigits(date.getDate(),2) + " " +
- this.formatDigits(date.getHours(),2) + ":" +
- this.formatDigits(date.getMinutes(),2) + ":" +
- this.formatDigits(date.getSeconds(),2);
- xml += '\t<item hour_start="' + hourstart + '" key="' + encodeURIComponent(cleanKey) + '" value="' + val + '"/>\n';
- }
- }
- }
- xml += '</metrics>';
-
- /*
- // Happy little self-contained test display
- var gPrompt = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
- .getService(Components.interfaces.nsIPromptService);
- gPrompt.alert( null, "METRICS XML", xml );
- */
-
- // upload xml
-
- var domparser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
- .getService(Components.interfaces.nsIDOMParser);
- var document = domparser.parseFromString(xml, "text/xml");
-
- var onpostload = {
- _that: null,
- handleEvent: function( event ) { this._that.onPostLoad(); }
- };
- onpostload._that = this;
-
- var onposterror = {
- _that: null,
- handleEvent: function( event ) { this._that.onPostError(); }
- };
- onposterror._that = this;
-
- var postURL = this.prefs.getCharPref(SONGBIRD_POSTMETRICS_PREFKEY);
-
- this._postreq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest);
- this._postreq.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest).addEventListener("load", onpostload, false);
- this._postreq.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest).addEventListener("error", onposterror, false);
- this._postreq.open('POST', postURL, true);
- this._postreq.send(document);
- },
-
- formatDigits: function(str, n) {
- str = str+'';
- while (str.length < n) str = "0" + str;
- return str;
- },
-
- onPostLoad: function() {
- this.LOG("POST metrics done: " + this._postreq.status + " - " + this._postreq.responseText);
-
- // POST successful, reset all metrics to 0
- if (this._postreq.status == 200 && this._postreq.responseText == "OK")
- {
- this._emptyTable();
- var pref = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefBranch);
- var timenow = new Date();
- var now = timenow.getTime();
-
- pref.setCharPref("app.metrics.last_upload", now);
- pref.setCharPref("app.metrics.last_version", this._getCurrentVersion());
- pref.setIntPref("app.metrics.last_update_count", this._getUpdateCount());
-
- this.LOG("metrics reset");
- }
- else
- {
- this.LOG("POST metrics failed: " + this._postreq.responseText);
- }
- },
-
- onPostError: function() {
- this.LOG("POST metrics error");
- },
-
-
-
- /**
- * Return true unless metrics have been
- * explicitly disabled.
- */
- _isEnabled: function() {
-
- // Make sure we are allowed to send metrics
- var enabled = 0;
- try {
- enabled = parseInt(this.prefs.getCharPref("app.metrics.enabled"));
- }
- catch (e) { }
- //if (!enabled) dump("*** METRICS ARE DISABLED ***\n");
-
- return enabled;
- },
-
-
- /**
- * Return true if SONGBIRD_UPLOAD_METRICS_EVERY_NDAYS days have passed
- * since last submission
- */
- _isWaitPeriodUp: function() {
-
- var timenow = new Date();
- var now = timenow.getTime();
- var last = 0;
- try
- {
- last = parseInt(this.prefs.getCharPref("app.metrics.last_upload"));
- }
- catch (e)
- {
- // first start, pretend we just uploaded so we'll trigger the next upload in n days
- this.prefs.setCharPref("app.metrics.last_upload", now);
- last = now;
- }
-
- var diff = now - last;
-
- return (diff > (1000 /*one second*/ * 60 /*one minute*/ * 60 /*one hour*/ * 24 /*one day*/ * SONGBIRD_UPLOAD_METRICS_EVERY_NDAYS))
- },
-
-
- /**
- * Has the version changed since last metrics submission
- */
- _hasVersionChanged: function() {
-
- var upgraded = false;
-
- var currentVersion = this._getCurrentVersion();
- var lastVersion = null;
-
- try
- {
- lastVersion = this.prefs.getCharPref("app.metrics.last_version");
- }
- catch (e) { }
-
- if (currentVersion != lastVersion)
- {
- upgraded = true;
- }
-
- return upgraded;
- },
-
- /**
- * TODO: REPLACE WITH SOMETHING OFFICIAL
- */
- _getCurrentVersion: function() {
-
- var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
- return appInfo.name + " " + appInfo.version + " - " + appInfo.appBuildID;
- },
-
-
- /**
- * Find out how many updates have been applied through the update manager
- */
- _getUpdateCount: function() {
- var updateManager = Components.classes["@mozilla.org/updates/update-manager;1"].getService(Components.interfaces.nsIUpdateManager);
- return updateManager.updateCount;
- },
-
-
- /**
- * TODO: REPLACE WITH SOMETHING OFFICIAL
- */
- _getPlayerUUID: function() {
-
- var uuid = "";
-
- try
- {
- uuid = this.prefs.getCharPref("app.player_uuid");
- }
- catch (e)
- {
- uuid = "";
- }
-
- if (uuid == "")
- {
- var aUUIDGenerator = Components.classes["@mozilla.org/uuid-generator;1"].createInstance(Components.interfaces.nsIUUIDGenerator);
- uuid = aUUIDGenerator.generateUUID();
- this.prefs.setCharPref("app.player_uuid", uuid);
- }
-
- return uuid;
- },
-
- metricsInc: function( aCategory, aUniqueID, aExtraString ) {
- this.metricsAdd( aCategory, aUniqueID, aExtraString, 1 );
- },
-
- metricsAdd: function( aCategory, aUniqueID, aExtraString, aIntValue ) {
- // timestamps are recorded as UTC !
- var d = new Date();
- var timestamp = (Math.floor(d.getTime() / 3600000) * 3600000) + (d.getTimezoneOffset() * 60000);
-
- // Cook up the key string
- var key = timestamp + "." + aCategory + "." + aUniqueID;
- if (aExtraString != null && aExtraString != "") key = key + "." + aExtraString;
-
- try {
- // Don't record things if we're disabled.
- if (!this._isEnabled()) return;
-
- // Make sure it's an actual int.
- intvalue = parseInt(aIntValue);
-
- // Then add our value to the old value and write it back
- var cur = this._getValue( key );
- var newval = cur + intvalue;
- this._setValue( key, newval );
- }
- catch(e) {
- this.LOG("error: metricsAdd( " +
- aCategory +
- ", " +
- aUniqueID +
- ", " +
- aExtraString +
- ", " +
- aIntValue +
- " ) == '" +
- key +
- "'\n\n" +
- e);
- }
- },
-
- _initDB: function() {
- if (!this._dbquery) {
- this._dbquery = Components.classes["@songbirdnest.com/Songbird/DatabaseQuery;1"].
- createInstance(Components.interfaces.sbIDatabaseQuery);
- this._dbquery.setAsyncQuery(false);
- this._dbquery.setDatabaseGUID("metrics");
- this._dbquery.resetQuery();
- this._dbquery.addQuery("CREATE TABLE IF NOT EXISTS metrics (keyname TEXT UNIQUE NOT NULL, keyvalue BIGINT DEFAULT 0)");
- this._dbquery.execute();
- }
- },
-
- _getValue: function(key) {
- var retval = 0;
-
- this._initDB();
- this._dbquery.resetQuery();
-
- this._dbquery.addQuery("SELECT * FROM metrics WHERE keyname = \"" + key + "\"");
- this._dbquery.execute();
-
- var dbresult = this._dbquery.getResultObject();
- if (dbresult.getRowCount() > 0) {
- retval = parseInt(dbresult.getRowCell(0, 1));
- }
-
- return retval;
- },
-
- _setValue: function(key, n) {
- this._initDB();
- this._dbquery.resetQuery();
- this._dbquery.addQuery("INSERT OR REPLACE INTO metrics VALUES (\"" + key + "\", " + n + ")");
- this._dbquery.execute();
- },
-
- _getTable: function() {
- var table = new Array();
- this._initDB();
- this._dbquery.resetQuery();
- this._dbquery.addQuery("SELECT * FROM metrics");
- this._dbquery.execute();
-
- var dbresult = this._dbquery.getResultObject();
- var count = dbresult.getRowCount();
-
- for (var i=0;i<count;i++) {
- var key = dbresult.getRowCell(i, 0);
- var val = parseInt(dbresult.getRowCell(i, 1));
- table.push([key, val]);
- }
-
- return table;
- },
-
- _emptyTable: function() {
- this._initDB();
- this._dbquery.resetQuery();
- this._dbquery.addQuery("DELETE FROM metrics");
- this._dbquery.execute();
- },
- } // Metrics.prototype
-
- function NSGetModule(compMgr, fileSpec) {
- return XPCOMUtils.generateModule([Metrics]);
- }
-
-