// vim: ts=2 sw=2 expandtab cindent
//
// 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;

Components.utils.import("resource:///modules/FlockWebmailUtils.jsm");

const ACCOUNTUTILS_CID        = Components.ID("{d84bce30-4ce5-11db-b0de-0800200c9a66}");
const ACCOUNTUTILS_CONTRACTID = "@flock.com/account-utils;1";
const PREF_CONFIRM_LOGOUT = "flock.accountsSidebar.confirmLogouLogin";
const ACCOUNT_PROPERTIES = "chrome://flock/locale/services/accountsSidebar.properties";
const PROP_STR_TITLE = "flock.services.accountsSidebar.logoutLogin.title";
const PROP_STR_DESC = "flock.services.accountsSidebar.logoutLogin.description";
const PROP_STR_DONT_SHOW = "flock.services.accountsSidebar.logoutLogin.dontShow";

const FLOCK_LOGIN = "Flock Login";

// ===================================================
// ========== BEGIN flockAccountUtils class ==========
// ===================================================

const ACCOUNTUTILS_INTERFACES = [
  Components.interfaces.nsISupports,
  Components.interfaces.flockIAccountUtils,
  Components.interfaces.flockIWebmailMailtoHandler
];

function flockAccountUtils() {

  this._acctSvc = CC["@flock.com/account-service;1"]
                  .getService(CI.flockIAccountService);
  this._logger = CC['@flock.com/logger;1'].createInstance(CI.flockILogger);
  this._logger.init('flockAccountUtils');
  this._logger.info('Created Account Utility Object');

  // mTempPasswords is an associative array of temporary passwords
  this.mTempPasswords = [];
}


// BEGIN nsISupports interface
flockAccountUtils.prototype.QueryInterface =
function flockAccountUtils_QueryInterface(aIID)
{
  var interfaces = ACCOUNTUTILS_INTERFACES;
  for (var i in interfaces) {
    if (aIID.equals(interfaces[i])) {
      return this;
    }
  }
  throw Components.results.NS_ERROR_NO_INTERFACE;
}
// END nsISupports interface


// BEGIN flockIAccountUtils interface

flockAccountUtils.prototype.clearTempPassword =
function flockAccountUtils_clearTempPassword(aKey)
{
  this._logger.info("{flockIAccountUtils}.clearTempPassword('"+aKey+"')");
  this.mTempPasswords[aKey] = undefined;
}

flockAccountUtils.prototype.ensureNoAuthenticatedAccountsBeforeLogin =
function flockAccountUtils_ensureNoAuthenticatedAccountsBeforeLogin(aAccount)
{
  this._logger.info("{flockIAccountUtils}." +
                    "ensureNoAuthenticatedAccountsBeforeLogin('" +
                    aAccount.urn + "')");
  if (aAccount.isAuthenticated()) {
    // If it's already logged in, we're done
    return false;
  }

  // Is there another account already logged in for this service?
  var service = aAccount.getService();
  var authenticatedAccountUrn = service.getAuthenticatedAccount();
  if (!authenticatedAccountUrn) {
    // No authenticated account, so we're done
    return true;
  }
  var authenticatedAccount = service.getAccount(authenticatedAccountUrn);

  // An authenticated account exists, so it needs to be logged out.
  var doLogout = true;

  // Unless the user has opted out, confirm that they want to log out of the
  // currently authenticated account
  var prefs = CC["@mozilla.org/preferences-service;1"]
              .getService(CI.nsIPrefBranch);
  if (!prefs.getPrefType(PREF_CONFIRM_LOGOUT) ||
      prefs.getBoolPref(PREF_CONFIRM_LOGOUT))
  {
    var sb = CC["@mozilla.org/intl/stringbundle;1"]
             .getService(CI.nsIStringBundleService)
             .createBundle(ACCOUNT_PROPERTIES);
    var title = sb.formatStringFromName(PROP_STR_TITLE, [service.title], 1);
    var newAcctName = aAccount.getParam("name");
    var oldAcctName = authenticatedAccount.getParam("name");
    var desc = sb.formatStringFromName(PROP_STR_DESC,
                                       [newAcctName, oldAcctName],
                                       2);
    var dontShow = sb.GetStringFromName(PROP_STR_DONT_SHOW);
    var checkResult = {value: true};
    // Do they want to log out?
    var win = CC["@mozilla.org/appshell/window-mediator;1"]
              .getService(CI.nsIWindowMediator)
              .getMostRecentWindow("navigator:browser");
    doLogout = CC["@mozilla.org/embedcomp/prompt-service;1"]
               .getService(CI.nsIPromptService)
               .confirmCheck(win.content, title, desc, dontShow, checkResult);
    if (!checkResult.value) {
      // They've opted out from future confirmations
      prefs.setBoolPref(PREF_CONFIRM_LOGOUT, false);
    }
  }

  if (doLogout) {
    authenticatedAccount.logout(null);
  }

  return doLogout;
}

flockAccountUtils.prototype.ensureOnlyAuthenticatedAccount =
function flockAccountUtils_ensureOnlyAuthenticatedAccount(aAccount)
{
  this._logger.info("{flockIAccountUtils}.ensureOnlyAuthenticatedAccount('"
                    + aAccount.urn + "')");
  var alreadyAuthenticated = false;
  var serviceId = aAccount.getService().contractId;
  var accounts = this._coop.Account.find({serviceId: serviceId});
  for each (var urn in accounts) {
    if (urn == aAccount.urn) {
      alreadyAuthenticated = aAccount.isAuthenticated();
      if (!alreadyAuthenticated) {
        aAccount.setParam("isAuthenticated", true);
        this._acctSvc.notifyObservers("accountIsAuthenticated", aAccount);
      }
    } else {
      // no change is sent to the RDF if the account was not authenticated
      this._coop.Account.get(urn).isAuthenticated = false;
    }
  }
  return alreadyAuthenticated;
}

flockAccountUtils.prototype.getAccountById =
function flockAccountUtils_getAccountById(aService, aAccountId)
{
  this._logger.info("{flockIAccountUtils}.getAccountById('"
                    + aService.urn + "', '" + aAccountId + "')");
  var f = {serviceId: aService.contractId, accountId: aAccountId.toLowerCase()};
  var accountUrns = this._coop.Account.find(f);
  this._logger.info(" - found " + accountUrns.length
                    + " matching account (should be exactly 1)");
  if (accountUrns.length > 0) {
    this._logger.info(" - account urn = " + accountUrns[0]);
    return aService.getAccount(accountUrns[0]);
  }
  return null;
}

flockAccountUtils.prototype.getAccountByLogin =
function flockAccountUtils_getAccountByLogin(aService, aLogin)
{
  this._logger.info(".getAccountByLogin('"
                    + aService.urn + "', '" + aLogin + "')");
  // First see if it's in the temporary passwords
  for (var key in this.mTempPasswords) {
    if (key.length && key.indexOf(aService.urn) != -1) {
      var p = this.mTempPasswords[key];
      if (p && p.username == aLogin) {
        var accountID = key.substring(aService.urn.length + 1);
        if (accountID && accountID.length) {
          this._logger.debug(".getAccountByLogin() - found temporary "
                             + "password with account ID = " + accountID);
          return this.getAccountById(aService, accountID);
        }
      }
    }
  }
  // Next try the login manager
  var loginManager = CC["@mozilla.org/login-manager;1"]
                     .getService(CI.nsILoginManager);
  var logins = loginManager.findLogins({}, null, FLOCK_LOGIN, null);
  for each (var login in logins) {
    if (login.username && login.hostname.indexOf(aService.urn) == 0) {
      var accountID = login.hostname.substring(aService.urn.length + 1);
      this._logger.debug(".getAccountByLogin() - found login manager "
                         + "entry with account ID = " + accountID);
      return this.getAccountById(aService, accountID);
    }
  }

  return null;
}

// Return a reference to a flockIWebService given its short name
flockAccountUtils.prototype.getServiceFromShortname =
function flockAccountUtils_getServiceFromShortname(aShortname) {
  var serviceUrn = this._coop.Service.find({ name: aShortname });
  if (serviceUrn.length) {
    var coopService = this._coop.Service.get(serviceUrn[0], null, true);
    return CC[coopService.get("serviceId")].getService(CI.flockIWebService);
  }
  return null;
};

flockAccountUtils.prototype.getWebServicesByInterface =
function flockAccountUtils_getWebServicesByInterface(aWebServiceInterface)
{
  var servicesEnum = {
    _arr: [],
    QueryInterface: function se_QueryInterface(aIid) {
      if (aIid.equals(Components.interfaces.nsISupports) ||
          aIid.equals(Components.interfaces.nsISimpleEnumerator))
      {
        return this;
      }
      throw Components.results.NS_ERROR_NO_INTERFACE;
    },
    hasMoreElements: function se_hasMoreElements() {
      return (this._arr.length > 0);
    },
    getNext: function se_getNext() {
      return this._arr.pop();
    }
  };

  var catMgr = Components.classes["@mozilla.org/categorymanager;1"]
                         .getService(Components.interfaces.nsICategoryManager);
  var catsEnum = catMgr.enumerateCategory("flockWebService");
  while (catsEnum.hasMoreElements()) {
    var entry = catsEnum.getNext()
                        .QueryInterface(Components.interfaces.nsISupportsCString);
    var contractId = catMgr.getCategoryEntry("flockWebService", entry.data);
    var svc = Components.classes[contractId]
                        .getService(Components.interfaces.flockIWebService);
    if (svc instanceof Components.interfaces[aWebServiceInterface]) {
      servicesEnum._arr.push(svc);
    }
  }

  return servicesEnum;
}

flockAccountUtils.prototype.getAccountsByInterface =
function flockAccountUtils_getAccountsByInterface(aServiceInterface)
{
  var accountsEnum = {
    arr : [],
    QueryInterface : function(iid) {
      if (!iid.equals(Components.interfaces.nsISupports) &&
          !iid.equals(Components.interfaces.nsISimpleEnumerator))
      {
        throw Components.results.NS_ERROR_NO_INTERFACE;
      }
      return this;
    },
    hasMoreElements : function() {
      return (this.arr.length > 0);
    },
    getNext : function() {
      return this.arr.pop();
    }
  };

  var c_accounts = this._coop.accounts_root.children.enumerate();
  while (c_accounts.hasMoreElements()) {
    var c_account = c_accounts.getNext();
    if (!c_account) continue; // getNext() can return NULL when hasMoreElements() is TRUE.
    if (Components.classes[c_account.serviceId]) {
      var svc = Components.classes[c_account.serviceId]
                          .getService(Components.interfaces.flockIWebService);
      if (svc instanceof Components.interfaces[aServiceInterface]) {
        accountsEnum.arr.push(svc.getAccount(c_account.id()));
      }
    } else {
      this._logger.debug( "No service found for account '"+c_account.name +
                          "' with serviceId: "+c_account.serviceId );
    }
  }

  return accountsEnum;
}

flockAccountUtils.prototype.doAccountsExist =
function flockAccountUtils_doAccountsExist()
{
  var accounts = this._coop.accounts_root.children.enumerate();
  return accounts.hasMoreElements();
}

flockAccountUtils.prototype.getElementByAttribute =
function flockAccountUtils_getElementByAttribute(aAncestorElement, aAttributeName, aAttributeValue)
{
  //this._logger.info("{flockIAccountUtils}.getElementByAttribute("+aAncestorElement+", '"+aAttributeName+"', '"+aAttributeValue+"')");
  for (var i = 0; i < aAncestorElement.childNodes.length; i++) {
    var childNode = aAncestorElement.childNodes.item(i);
    try {
      var childElem = childNode.QueryInterface(Components.interfaces.nsIDOMHTMLElement);
      if (childElem.getAttribute(aAttributeName) == aAttributeValue) {
        return childElem;
      }
      var recursiveResult = this.getElementByAttribute(childElem, aAttributeName, aAttributeValue);
      if (recursiveResult) {
        return recursiveResult;
      }
    } catch (ex) {
      // This just means that childNode is not an Element
    }
  }
  return null;
}

flockAccountUtils.prototype.createAccount =
function flockAccountUtils_createAccount(aService,
                                         aAccountId,
                                         aUsername,
                                         aUrl,
                                         aIsTransient)
{
  aService.QueryInterface(Components.interfaces.nsIClassInfo);

  // Add the account
  var accountURN = "urn:flock:"
                   + aService.shortName
                   + ":account:"
                   + aAccountId;
  var coopAccount = this._coop.get(accountURN);
  if (!coopAccount) {
    coopAccount = new this._coop.Account(accountURN, {
      name: aUsername,
      screenName: aUsername,
      serviceId: aService.contractId,
      accountId: aAccountId.toLowerCase(),
      favicon: aService.icon,
      nextRefresh: new Date(0),
      isTransient: aIsTransient,
      URL: aUrl ? aUrl : aService.url
    });
    coopAccount.isPollable = aService instanceof CI.flockIPollingService;
    this._coop.accounts_root.children.addOnce(coopAccount);
  } else {
    this._cleanupMigratedAccountParams(coopAccount);
  }
  if (aService instanceof CI.flockISocialWebService) {
    // Create the friends list
    var friendsListUrn = accountURN + ":friends";
    if (!this._coop.get(friendsListUrn)) {
      var friendsList = new this._coop.FriendsList(
        friendsListUrn,
        {
          account: coopAccount
        });
      coopAccount.friendsList = friendsList;
    }
    // Create the friends Activity Log (MyWorld)
    coopAccount.friendActivityLog = new this._coop.FriendActivityLog();
  }
  return accountURN;
}

flockAccountUtils.prototype._cleanupMigratedAccountParams =
function flockAccountUtils__cleanupMigratedAccountParams(aCoopAccount)
{
  aCoopAccount.set("fbPokes", null, true);
  aCoopAccount.set("fbFriendRequests", null, true);
  aCoopAccount.set("fbGroupInvites", null, true);
  aCoopAccount.set("fbEventInvites", null, true);
  aCoopAccount.set("flickr_comments", null, true);
  aCoopAccount.set("flickr_nsid", null, true);
  aCoopAccount.set("flickr_id", null, true);
  aCoopAccount.set("flickr_username", null, true);
  aCoopAccount.set("flickr_fullname", null, true);
  aCoopAccount.set("flickr_comments_timespan", null, true);
}

flockAccountUtils.prototype.removeAccount =
function flockAccountUtils_removeAccount(aAccountUrn)
{
  this._logger.info("{flockIAccountUtils}.removeAccount('"+aAccountUrn+"')");
  var c_acct = this._coop.get(aAccountUrn);

  // Remove associated passwords
  if (c_acct.accountId && c_acct.serviceId && Components.classes[c_acct.serviceId]) {
    var svc = Components.classes[c_acct.serviceId]
                        .getService(Components.interfaces.flockIWebService);
    if (svc) {
      // Clear the internal password entry that some services use
      var internalPWhost = svc.urn+":"+c_acct.accountId;
      this.clearTempPassword(internalPWhost);
      this.removeAllPasswordsForHost(internalPWhost);

      // Clear the user-created password entry for this account, if we can
      // positively identify one
      if (svc instanceof CI.flockILoginWebService) {
        var lm = CC["@mozilla.org/login-manager;1"]
                 .getService(CI.nsILoginManager);
        var logins = lm.getAllLogins({});
        var domains = this._coop.get(svc.urn).domains.split(",");
        for each (var login in logins) {
          for each (var domain in domains) {
            var index = login.hostname.indexOf("." + domain);
            if ((login.hostname == "http://" + domain) ||
                (login.hostname == "https://" + domain) ||
                ((index != -1) &&
                 (index + domain.length + 1 == login.hostname.length)))
            {
              // The host for this password matches the service domain
              try {
                if (login.username == c_acct.name ||
                    login.username == c_acct.screenName ||
                    login.username == c_acct.accountId)
                {
                  // And the username matches the account, so remove this entry
                  lm.removeLogin(login);
                }
              } catch (ex) {
                this._logger.error("ERROR removing password:"
                                   + " hostname: " + login.hostname
                                   + " username: " + c_acct.username
                                   + " exception: " + ex);
              }
            }
          }
        }
      }
      // Fire the accountRemoved notification
      this._acctSvc.notifyObservers("accountRemoved",
                                     svc.getAccount(aAccountUrn));
    }
  }

  // Remove all of this Account's children which are Streams or Blogs
  var acctChildren = c_acct.children.enumerate();
  while (acctChildren.hasMoreElements()) {
    var acctChild = acctChildren.getNext();
    if (acctChild.isInstanceOf(this._coop.Stream)) {
      this._logger.info("    removing account Stream");
      var stream = acctChild;
      // Remove all Stream items
      var streamChildren = stream.children.enumerate();
      while (streamChildren.hasMoreElements()) {
        var streamChild = streamChildren.getNext();
        streamChild.destroy();
      }
      stream.destroy();
    }
    else if (acctChild.isInstanceOf(this._coop.Blog)) {
      this._logger.info("    removing account Blog");
      acctChild.destroy();
    }
  }
  
  var friendsList = c_acct.friendsList;
  if (friendsList) {
    if (friendsList.children) {
      var friendsEnum = friendsList.children.enumerate();
      // Remove friends
      while (friendsEnum.hasMoreElements()) {
        var identity = friendsEnum.getNext();
        this._logger.debug("Friend " + identity.accountId
                                     + " has been deleted from RDF");
        identity.destroy();
      }
    }
    // Remove friends list
    friendsList.destroy();
  }

  var accountParamList = c_acct.accountParamList;
  if (accountParamList) {
    var paramEnum = accountParamList.children.enumerate();
    while (paramEnum.hasMoreElements()) {
      var param = paramEnum.getNext();
      this._logger.info("removeAccount coop param: " + param.key);
      param.destroy();
    }
    // Remove list
    accountParamList.destroy();
  }

  var friendActivityLog = c_acct.friendActivityLog;
  if (friendActivityLog) {
    var activityEnum = friendActivityLog.children.enumerate();
    while (activityEnum.hasMoreElements()) {
      var activity = activityEnum.getNext();
      this._logger.info("removeAccount coop activity: " + activity.updateURL);
      activity.destroy();
    }
    // Remove list
    friendActivityLog.destroy();
  }

  var otherActivityLog = c_acct.otherActivityLog;
  if (otherActivityLog) {
    var otherEnum = otherActivityLog.children.enumerate();
    while (otherEnum.hasMoreElements()) {
      otherEnum.getNext().destroy();
    }
    otherActivityLog.destroy();
  }

  this._logger.info("  destroying account " + aAccountUrn);
  c_acct.destroy();
}

flockAccountUtils.prototype.removeAllAccounts =
function flockAccountUtils_removeAllAccounts()
{
  var c_accounts = this._coop.Account.all();
  while (c_accounts.hasMoreElements()) {
    var c_acct = c_accounts.getNext();
    var svc = Components.classes[c_acct.serviceId]
                        .getService(Components.interfaces.flockIWebService);
    var acct = svc.getAccount(c_acct.id());
    acct.logout(null);
    svc.removeAccount(c_acct.id());
  }
}

flockAccountUtils.prototype.getCookie =
function flockAccountUtils_getCookie(aHost, aCookieName)
{
  var ios = CC["@mozilla.org/network/io-service;1"]
            .getService(CI.nsIIOService);
  var cookieSvc = CC["@mozilla.org/cookieService;1"]
                  .getService(CI.flockICookieService);
  var value = cookieSvc.getCookieStringAlways(ios.newURI(aHost, null, null),
                                              null);
  if (aCookieName && value) {
    var index = value.indexOf(aCookieName + "=");
    if (index == -1) {
      return null;
    }
    index += aCookieName.length + 1;
    var end = value.indexOf(";", index);  
    value = unescape(value.substring(index, end == -1 ? value.length : end));
    this._logger.info("{flockIAccountUtils}.getCookie('" + aHost
                      + "', '" + aCookieName+"'): value = " + value);
  }
  return value;
}

flockAccountUtils.prototype.getPassword =
function flockAccountUtils_getPassword(aKey)
{
  this._logger.info("{flockIAccountUtils}.getPassword('"+aKey+"')");
  var pw = this.getTempPassword(aKey);
  if (!pw) {
    pw = this.getFirstPasswordForHost(aKey);
  }
  return pw;
}

flockAccountUtils.prototype.getFirstPasswordForHost =
function flockAccountUtils_getFirstPasswordForHost(aHost)
{
  this._logger.info("{flockIAccountUtils}.getFirstPasswordForHost('"+aHost+"')");
  var loginManager = CC["@mozilla.org/login-manager;1"]
                     .getService(CI.nsILoginManager);
  var logins = loginManager.findLogins({}, aHost, FLOCK_LOGIN, null);

  if (logins.length > 0) {
    return logins[0];
  }
  return null;
}

flockAccountUtils.prototype.getTempPassword =
function flockAccountUtils_getTempPassword(aKey)
{
  this._logger.info("{flockIAccountUtils}.getTempPassword('"+aKey+"')");
  return this.mTempPasswords[aKey];
}

flockAccountUtils.prototype.makeTempPasswordPermanent =
function flockAccountUtile_makeTempPasswordPermanent(aKey)
{
  this._logger.info("{flockIAccountUtils}.makeTempPasswordPermanent('"+aKey+"')");
  var temp = this.getTempPassword(aKey);
  if (temp) {
    this.removeAllPasswordsForHost(aKey);
    this.setPassword(aKey, temp.username, temp.password);
    this.clearTempPassword(aKey);
    return this.getPassword(aKey);
  } else {
    this._logger.info(" - ERROR: temporary password does not exist!  Can't make it permanent...");
    // TODO: Maybe throw an exception here?
  }
  return null;
}

flockAccountUtils.prototype.markAllAccountsAsLoggedOut =
function flockAccountUtils_markAllAccountsAsLoggedOut(aService)
{
  this._logger.info("markAllAccountsAsLoggedOut(" + aService.contractId + ")");
  var accountUrns = this._coop.Account.find({serviceId: aService.contractId,
                                             isAuthenticated: true});
  for each(var urn in accountUrns) {
    var acct = aService.getAccount(urn);
    acct.setParam("isAuthenticated", false);
    this._acctSvc.notifyObservers("accountNotAuthenticated", acct);
  }
}

flockAccountUtils.prototype.setPassword =
function flockAccountUtils_setPassword(aHost, aUser, aPassword)
{
  this._logger.info("{flockIAccountUtils}.setPassword('"+aHost+"', '"+aUser+"', 'XXXXXX')");
  var loginManager = CC["@mozilla.org/login-manager;1"]
                     .getService(CI.nsILoginManager);
  var LoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
                                             CI.nsILoginInfo,
                                             "init");
  var login_info = new LoginInfo(aHost, FLOCK_LOGIN, null,
                                 aUser, aPassword, "", "");
  // addLogin() will throw an exception if the login already exists
  try {
    loginManager.addLogin(login_info);
  } catch (ex) {
    this._logger.error("ERROR setting password:"
                       + " hostname: " + aHost
                       + " username: " + aUser
                       + " exception: " + ex);
  }
}

flockAccountUtils.prototype.setTempPassword =
function flockAccountUtils_setTempPassword(aKey, aUser, aPassword, aFormType)
{
  this._logger.info("{flockIAccountUtils}.setTempPassword('"+aKey+"', '"+aUser+"', 'XXXXXXXX', '"+aFormType+"')");
  var pw = {
    QueryInterface: function(aIID) {
      if (!aIID.equals(Components.interfaces.nsISupports) &&
          !aIID.equals(Components.interfaces.nsILoginInfo) &&
          !aIID.equals(Components.interfaces.flockILoginInfo)) {
        throw Components.interfaces.NS_ERROR_NO_INTERFACE;
      }
      return this;
    },
    hostname: aKey,
    username: aUser,
    password: aPassword,
    formType: aFormType,
    usernameField: "",
    passwordField: ""
  };
  this.mTempPasswords[aKey] = pw;
}

flockAccountUtils.prototype.removeAllPasswordsForHost =
function flockAccountUtils_removeAllPasswordsForHost(aHost)
{
  this._logger.info("{flockIAccountUtils}.removeAllPasswordsForHost('"+aHost+"')");
  var loginManager = CC["@mozilla.org/login-manager;1"]
                     .getService(CI.nsILoginManager);

  var logins = loginManager.findLogins({}, aHost, FLOCK_LOGIN, null);
  for each (var login in logins) {
    loginManager.removeLogin(login);
  }
}

flockAccountUtils.prototype.removePassword =
function flockAccountUtils_removePassword(aKey) {
  this._logger.info("{flockIAccountUtils}.removePassword('" + aKey + "')");
  var pw = this.getFirstPasswordForHost(aKey);
  if (!pw) {
    return null;
  }

  CC["@mozilla.org/login-manager;1"].getService(CI.nsILoginManager)
                                    .removeLogin(pw);
  return pw;
};

flockAccountUtils.prototype.extractPasswordFromHTMLForm =
function flockAccountUtils_extractPasswordFromHTMLForm(aForm)
{
  this._logger.info("{flockIAccountUtils}.extractPasswordFromHTMLForm(aForm)");
  aForm.QueryInterface(Components.interfaces.nsIDOMHTMLFormElement);
  var formElements = aForm.elements;
  var passElements = [];
  var username = "";
  var firstPasswordIndex = formElements.length;
  for (var i = 0; i < formElements.length; i++) {
    var formElement = formElements.item(i);
    if (formElement.type == "password") {
      passElements.push(formElement);
      if (firstPasswordIndex == formElements.length) {
        firstPasswordIndex = i;
      }
    }
  }
  if (firstPasswordIndex == formElements.length) {
    // We didn't find a password
    return null;
  }
  // Backwards to get closest text field
  for (var i = firstPasswordIndex - 1; i >= 0; i--) {
    var formElement = formElements.item(i);
    if (formElement.type == "text") {
      username = formElement.value;
      break;
    }
  }

  var LoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
                                             CI.nsILoginInfo,
                                             "init");

  var login_info = new LoginInfo("chrome://flock",
                                 "",
                                 "",
                                 username,
                                 passElements[0].value,
                                 null,
                                 null);
  return login_info;
}

flockAccountUtils.prototype.loadWebDetectiveFile =
function flockAccountUtils_loadWebDetectiveFile(aDetectionFileName, aSubDir)
{
  this._logger.info("loadWebDetectiveFile('" + aDetectionFileName + "')");
  var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
                         .getService(Components.interfaces.nsIProperties);
  var profDir = dirSvc.get("ProfD", Components.interfaces.nsIFile);
  var file = Components.classes["@mozilla.org/file/local;1"]
                       .createInstance(Components.interfaces.nsILocalFile);
  file.initWithPath(profDir.path);
  file.append("detect");
  var profDetectDir = file.clone();
  if (!profDetectDir.exists()) {
    profDetectDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
  }
  file.append(aDetectionFileName);
  if (!file.exists()) {
    // The file doesn't exist in the profile, so check if there's one in the
    // install folder that we can copy over
    var installDir = dirSvc.get("XPIClnupD", Components.interfaces.nsIFile);
    var file2 = Components.classes["@mozilla.org/file/local;1"]
                          .createInstance(Components.interfaces.nsILocalFile);
    file2.initWithPath(installDir.path);
    for each (var dir in aSubDir.split("/")) {
      file2.append(dir);
    }
    file2.append(aDetectionFileName);
    if (!file2.exists()) {
      this._logger.info("File does not exist: "+file2.path);
      return null;
    }
    file2.copyTo(profDetectDir, aDetectionFileName);
  }
  var webDetective = Components.classes["@flock.com/web-detective;1"]
                               .getService(Components.interfaces.flockIWebDetective);
  webDetective.loadDetectFile(file);
  return webDetective;
}

flockAccountUtils.prototype.removeCookies =
function flockAccountUtils_removeCookies(aCookies)
{
  aCookies.QueryInterface(Components.interfaces.nsISimpleEnumerator);
  var cookieManager = Components.classes["@mozilla.org/cookiemanager;1"]
                                .getService(Components.interfaces.nsICookieManager);
  var deletedCount = 0;
  while (aCookies.hasMoreElements()) {
    var c = aCookies.getNext()
      .QueryInterface(Components.interfaces.nsICookie);
    cookieManager.remove(c.host, c.name, c.path, false);
    deletedCount++;
  }
  this._logger.info(".removeCookies() - Deleted "+deletedCount+" cookies");
}

flockAccountUtils.prototype.peopleSidebarOpen =
function flockAccountUtils_peopleSidebarOpen()
{
  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                     .getService(Components.interfaces.nsIWindowMediator);
  var win = wm.getMostRecentWindow("navigator:browser");
  if (win) {
    var psb = win.document.getElementById("flockPeopleSidebarBroadcaster");
    if (psb.hasAttribute("checked")) {
      // The sidebar is open
      var sidedoc = win.document.getElementById("sidebar").contentDocument;
      var psTabs = sidedoc.getElementById("peopleServiceTabs");
      if (psTabs) {
        return psTabs.selectedServiceId ? psTabs.selectedServiceId : "all";
      }
    }
  }
  return null;
}

flockAccountUtils.prototype.primaryAccountIsHot =
function flockAccountUtils_primaryAccountIsHot() {
  var authAccountUrns = this._coop.Account.find({ isAuthenticated: true });
  for each (var urn in authAccountUrns) {
    var authAccount = this._coop.Account.get(urn);
    var paramList = authAccount.accountParamList;
    if (paramList && paramList.getCustomParam("isPrimary")) {
      return paramList.getCustomParam("hasNewMessages");
    }
  }
  return false;
};

// END flockIAccountUtils interface

// BEGIN flockIWebmailMailtoHandler interface
flockAccountUtils.prototype.webmailMailtoURIHandler =
function flockAccountUtils_webmailMailtoURIHandler(aMailtoURI)
{
  // Check for webmail circumvention. When this is present in the mailto:
  // string we always want to use the default mailer rather than webmail.
  if (aMailtoURI.indexOf("&bypasswebmail=true") != -1) {
    return false;
  }

  // Is there a primary webmail account?
  var primaryAcct = FlockWebmailUtils.getPrimaryAccount();
  if (primaryAcct &&
      FlockWebmailUtils.composeMessage(primaryAcct, aMailtoURI))
  {
    CC["@flock.com/metrics-service;1"]
      .getService(CI.flockIMetricsService)
      .report("WebMail-MailTo", primaryAcct.getService().shortName);
    return true;
  }

  // Unsuccessful
  return false;
}


// END flockIWebmailMailtoHandler interface


// BEGIN helper functions
flockAccountUtils.prototype.__defineGetter__("_coop",
function getter__coop() {
  if (!this.mCoop) {
    this.mCoop = Components.classes["@flock.com/singleton;1"]
                           .getService(Components.interfaces.flockISingleton)
                           .getSingleton("chrome://flock/content/common/load-faves-coop.js")
                           .wrappedJSObject;
  }
  return this.mCoop;
});
// END helper functions

// ========== END flockAccountUtils class ==========



// ================================================
// ========== BEGIN XPCOM Module support ==========
// ================================================

// BEGIN flockAccountUtilsModule object
var flockAccountUtilsModule = {};

flockAccountUtilsModule.registerSelf =
function flockAccountUtilsModule_registerSelf(compMgr, fileSpec, location, type)
{
  compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  compMgr.registerFactoryLocation( ACCOUNTUTILS_CID, 
                                   "Flock Account Utils JS Component",
                                   ACCOUNTUTILS_CONTRACTID, 
                                   fileSpec, 
                                   location,
                                   type );
}

flockAccountUtilsModule.getClassObject =
function flockAccountUtilsModule_getClassObject(compMgr, cid, iid)
{
  if (!cid.equals(ACCOUNTUTILS_CID)) {
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
  if (!iid.equals(Components.interfaces.nsIFactory)) {
    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  }
  return flockAccountUtilsFactory;
}

flockAccountUtilsModule.canUnload =
function flockAccountUtilsModule_canUnload(compMgr)
{
  return true;
}
// END flockAccountUtilsModule object


// BEGIN flockAccountUtilsFactory object
var flockAccountUtilsFactory = {};

flockAccountUtilsFactory.createInstance =
function flockAccountUtilsFactory_createInstance(outer, iid)
{
  if (outer != null) {
    throw Components.results.NS_ERROR_NO_AGGREGATION;
  }
  return (new flockAccountUtils()).QueryInterface(iid);
}
// END flockAccountUtilsFactory object


// NS module entry point
function NSGetModule(compMgr, fileSpec) {
  return flockAccountUtilsModule;
}

// ========== END XPCOM module support ==========
