// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2009
// http://flock.com
//
// This file may be used under the terms of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// END FLOCK GPL

const CC = Components.classes;
const CI = Components.interfaces;
const CR = Components.results;
const CU = Components.utils;

CU.import("resource:///modules/FlockXPCOMUtils.jsm");
CU.import("resource:///modules/FlockPrefsUtils.jsm");

const COOKIE_SCAN_CLASSNAME = "Flock OOBE Cookie Scan";
const COOKIE_SCAN_CLASSID =
  Components.ID("{966db6f8-7850-4751-9f6f-6cdef9c06e0f}");
const COOKIE_SCAN_CONTRACTID = "@flock.com/oobe-cookie-scan;1";

const WD_NAME = "flockoobe";

const PREF_FLOCK_IMPORT_SOURCE = "flock.firstrun.import.source";

function CookieScan() {
  this._logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
  this._logger.init("cookiescan");

  this.acUtils = CC["@flock.com/account-utils;1"]
                .getService(CI.flockIAccountUtils);

  this._wd = this.acUtils.loadWebDetectiveFile(WD_NAME + ".xml", "res/detect");

  this._metrics = CC["@flock.com/metrics-service;1"]
                  .getService(CI.flockIMetricsService);
}

CookieScan.prototype = new FlockXPCOMUtils.genericComponent(
  COOKIE_SCAN_CLASSNAME,
  COOKIE_SCAN_CLASSID,
  COOKIE_SCAN_CONTRACTID,
  CookieScan,
  CI.nsIClassInfo.SINGLETON,
  [CI.flockIOOBECookieScan]
);


let gServiceRankings = {
  "people": [ "facebook", "myspace", "twitter" , "bebo", "digg"],
  "media": [ "youtube", "flickr", "searchvideo", "revver" ],
  "webmail": [ "gmail", "yahoomail", "aolmail" ],
  "blog": [ "blogger", "wordpress", "livejournal", "typepad", "blogsome",
            "xanga" ],
  "upload": [ "onetmedia", "tinypic", "photobucket", "picasa", "flickr" ],
  "onlinefaves": [ "delicious" ]
};

let gCategoryRankings = [
  "people", "media", "webmail", "blog", "upload", "onlinefaves"
];

let gHistoryOnlyServices = [
  "@flock.com/webservice/revver;1",
  "@flock.com/?photo-api-searchvideo;1"
];

let gUseLoginHistory = {
  "blogger": true,
  "wordpress": true,
  "livejournal": true,
  "typepad": true,
  "blogsome": true,
  "xanga": true
};

CookieScan.prototype.getServiceData =
function CS_getServiceData() {
  this._loadRankings();
  this._loadServiceInfo();

  this._serviceData = [];

  this._categories = {};
  for each (let category in gCategoryRankings) {
    this._categories[category] = { serviceList: [], found: {} };
  }

  // Scan Cookie/History data and build service list accordingly
  this._scanCookies();
  this._scanHistory();
  this._fillServiceData();
  this._fillDefaultServiceData();

  return this._serviceData;
}

CookieScan.prototype._fillServiceData =
function CS__fillServiceData() {
  for each (let category in gCategoryRankings) {
    let categoryInfo = this._categories[category];
    let serviceList = categoryInfo.serviceList;
    if (serviceList.length) {
      this._serviceData.push({ category: category, services: serviceList });
      this._reportServicesFound(serviceList);
    }
  }
};

CookieScan.prototype._fillDefaultServiceData =
function CS__fillDefaultServiceData() {
  for each (let category in gCategoryRankings) {
    let categoryInfo = this._categories[category];
    let serviceList = categoryInfo.serviceList;
    for each (let name in gServiceRankings[category]) {
      let info = this._serviceInfo[name];
      if (info) {
        serviceList.push(info.service);
      }
    }
    this._serviceData.push({ category: category, services: serviceList });
  }
};

CookieScan.prototype._fillCategories =
function CS__fillCategories(aHitFunc) {
  for (let [category, categoryState] in Iterator(this._categories)) {
    let categoryList = categoryState.serviceList;
    let categoryFound = categoryState.found;

    let foundList = [];

    for each (let name in gServiceRankings[category]) {
      if (categoryFound[name]) {
        continue;
      }

      let info = this._serviceInfo[name];
      if (!info) {
        continue;
      }

      try {
        if (aHitFunc(info.service)) {
          categoryFound[info.name] = true;
          delete this._serviceInfo[name];
          foundList.push(info);
        }
      } catch (ex) {
        // Something went wrong with the hit function, just ignore
      }
    }

    for each (let info in foundList) {
      categoryList.push(info.service);
    }
  }
};

CookieScan.prototype._loadRankings =
function CS__loadRankings() {
  let nsJSON = CC["@mozilla.org/dom/json;1"].createInstance(CI.nsIJSON);

  let ranking;

  ranking = this._wd.getLocalizedString(WD_NAME, "serviceRankings", null);
  let serviceRankings = nsJSON.decode(ranking);
  if (serviceRankings) {
    gServiceRankings = serviceRankings;
  }

  ranking = this._wd.getLocalizedString(WD_NAME, "categoryRankings", null);
  let categoryRankings = nsJSON.decode(ranking);
  if (categoryRankings) {
    gCategoryRankings = categoryRankings;
  }
  var ignore = this._wd.getLocalizedString(WD_NAME, "ignoreCookies", null);
  gIgnoreCookies = nsJSON.decode(ignore);
};

CookieScan.prototype._loadServiceInfo =
function CS__loadServiceInfo() {
  let serviceInfo = {};

  function addService(aCID) {
    try {
      let service = CC[aCID].getService(CI.flockIWebService);
      let name = service.shortName;
      serviceInfo[name] = { name: name, service: service };
    } catch (ex) {
      // bad service implementation, ignore
    }
  }

  let catMgr = CC["@mozilla.org/categorymanager;1"]
               .getService(CI.nsICategoryManager);
  let svcEnum = catMgr.enumerateCategory("wsm-startup");

  while (svcEnum.hasMoreElements()) {
    let entry = svcEnum.getNext().QueryInterface(CI.nsISupportsCString).data;
    let contractID = catMgr.getCategoryEntry("wsm-startup", entry);
    addService(contractID);
  }

  for each (let contractID in gHistoryOnlyServices) {
    addService(contractID);
  }

  this._serviceInfo = serviceInfo;
};

CookieScan.prototype._scanCookies =
function CS__scanCookies() {
  var inst = this;
  function cookieHit(aService) {
    var ignoreCookies = gIgnoreCookies[aService.shortName];
    if (ignoreCookies == "all") {
      return false;
    }
    // we only use the host and path parts of the session cookies
    // to detect any cookie crumb from a previous login to a service.
    var cookies = inst._wd.getSessionCookies(aService.shortName);
    var ckUrl = "";
    while (cookies && cookies.hasMoreElements()) {
      var cookie = cookies.getNext().QueryInterface(CI.nsICookie);
      var url = cookie.host + cookie.path;
      if (cookie.host != ".google.com") {
        var lengthDiff = ckUrl.length - url.length;
        // no need to re-get the cookie if it's a subdomain of the domain
        if (lengthDiff < 0 || ckUrl.indexOf(url, lengthDiff) == -1) {
          ckUrl = url;
          var s = inst.acUtils.getCookie("https://" + ckUrl, null);
          if (ignoreCookies && s) {
            if (ignoreCookies.indexOf(cookie.name) != -1) {
              continue;
            }
            for each (var c in ignoreCookies.split(",")) {
              var pos = s.indexOf(c + "=");
              if (pos != -1) {
                var posNext = s.indexOf("; ", pos);
                s = s.substring(0, pos ? pos - 1 : 0) + 
                    (posNext > 0 ? s.substring(posNext + 2) : "");
              }
            }
          }
          if (s) {
            return true;
          }
        }
      }
    }
    return false;
  }

  this._fillCategories(cookieHit);
};

CookieScan.prototype._scanHistory =
function CS__scanHistory() {
  let ios = CC["@mozilla.org/network/io-service;1"]
            .getService(CI.nsIIOService);
  let navHistory = CC["@mozilla.org/browser/nav-history-service;1"]
                   .getService(CI.nsINavHistoryService);

  let query = navHistory.getNewQuery();
  let options = navHistory.getNewQueryOptions();

  query.domainIsHost = false;

  function historyHit(aService) {
    let useLoginURL = gUseLoginHistory[aService.shortName] ? true : false;

    if (useLoginURL) {
      aService.QueryInterface(CI.flockILoginWebService);
      let uri = ios.newURI(aService.loginURL, null, null);

      query.domain = null;
      query.uri = uri;
    } else {
      let uri = ios.newURI(aService.url, null, null);

      query.domain = uri.host.replace(/^www\./, "");
      query.uri = null;
    }

    let queryResults = navHistory.executeQuery(query, options);
    let resultsRoot = queryResults.root;
    resultsRoot.containerOpen = true;

    return resultsRoot.hasChildren;
  }

  this._fillCategories(historyHit);
};

CookieScan.prototype._reportServicesFound =
function CS__reportServicesFound(aServiceList) {
  for each (let serviceInfo in aServiceList) {
    this._metrics.report("GettingStarted-OOBE2-ServiceFound",
                         serviceInfo.name);
  }
};

var gComponentsArray = [CookieScan];

var NSGetModule = FlockXPCOMUtils.generateNSGetModule(COOKIE_SCAN_CLASSNAME,
                                                      gComponentsArray);
