// 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");
FlockXPCOMUtils.debug = false;

CU.import("resource:///modules/FlockSvcUtils.jsm");
CU.import("resource:///modules/FlockStringBundleHelpers.jsm");
CU.import("resource:///modules/FlockXMLUtils.jsm");
CU.import("resource:///modules/FlockWebmailUtils.jsm");

const MODULE_NAME = "YahooMail";
const CLASS_NAME = "Flock Yahoo! Mail Service";
const CLASS_SHORT_NAME = "yahoomail";
const CLASS_TITLE = "Yahoo! Mail";
const CLASS_ID = Components.ID("{0f1532fe-aabc-4a78-8193-ce366010b697}");
const CONTRACT_ID = "@flock.com/yahoomail-service;1";

const SERVICE_ENABLED_PREF = "flock.service.yahoomail.enabled";
const REFRESH_INTERVAL_PREF = "flock.webmail.refreshInterval";

const YAHOOMAIL_URL = "http://mail.yahoo.com";
const YAHOOMAIL_MESSAGE_URL = "http://mrd.mail.yahoo.com";
const YAHOOMAIL_FAVICON = "chrome://flock/content/services/yahoomail/favicon.png";
const YAHOOMAIL_STRING_BUNDLE = "chrome://flock/locale/services/yahoomail.properties";

const FLOCK_ERROR_CONTRACTID = "@flock.com/error;1";
const FLOCK_RDNDS_CONTRACTID = "@flock.com/rich-dnd-service;1";

const STR_COMPOSEURL = "composeURL";
const STR_FOLDERURL = "folderURL";
const STR_ISCLASSICVIEW = "isClassicView";
const STR_CLASSICFLAVOR = "classicFlavor";
const STR_ISUPGRADEABLE = "isAccountUpgradeable";
const STR_INTL = "intl";
const STR_UNREAD = "unreadMessages";
const STR_ISUTF16 = "isUTF16Encoded";
const STR_WAITING_TO_SEND = "waitingToSend";

const DOMAIN = "yahoo.com";
const MAX_UNREAD_COUNT = 50;  // arbitrary max unread messages to retrieve


/**************************************************************************
 * Component: Flock YahooMailService
 **************************************************************************/
var PREFS = null;

// Constructor
function YahooMailService() {
  PREFS = CC["@mozilla.org/preferences-service;1"]
                .getService(CI.nsIPrefBranch);
  this._init();

  var ws = FlockSvcUtils.flockIWebService;
  ws.addDefaultMethod(this, "isHidden");
  ws.addDefaultMethod(this, "getStringBundle");
  ws.addDefaultMethod(this, "url");

  var lws = FlockSvcUtils.flockILoginWebService;
  lws.addDefaultMethod(this, "loginURL");
  lws.addDefaultMethod(this, "tooltip");
  lws.addDefaultMethod(this, "getAccount");
  lws.addDefaultMethod(this, "getAuthenticatedAccount");
  lws.addDefaultMethod(this, "getAccountUrns");
  lws.addDefaultMethod(this, "logout");
  lws.addDefaultMethod(this, "docRepresentsSuccessfulLogin");
  lws.addDefaultMethod(this, "getCredentialsFromForm");
  lws.addDefaultMethod(this, "ownsDocument");
  lws.addDefaultMethod(this, "ownsLoginForm");
  lws.addDefaultMethod(this, "getSessionValue");

  FlockSvcUtils.flockIRichContentDropHandler
               .addDefaultMethod(this, "_handleTextareaDrop");
}


/**************************************************************************
 * Flock YahooMailService: XPCOM Component Creation
 **************************************************************************/

YahooMailService.prototype = new FlockXPCOMUtils.genericComponent(
  CLASS_NAME,
  CLASS_ID,
  CONTRACT_ID,
  YahooMailService,
  CI.nsIClassInfo.SINGLETON,
  [
    CI.nsIObserver,
    CI.nsITimerCallback,
    CI.flockIPollingService,
    CI.flockIWebService,
    CI.flockILoginWebService,
    CI.flockIMailWebService,
    CI.flockIRichContentDropHandler,
    CI.flockIMigratable
  ]
);

// FlockXPCOMUtils.genericModule() categories
YahooMailService.prototype._xpcom_categories = [
  { category: "wsm-startup" },
  { category: "flockMigratable" },
  { category: "flockWebService", entry: CLASS_SHORT_NAME },
  { category: "flockRichContentHandler", entry: CLASS_SHORT_NAME }
];


/**************************************************************************
 * Flock YahooMailService: Private Data and Methods
 **************************************************************************/

// Service initialization
YahooMailService.prototype._init =
function ymSvc__init() {
  FlockSvcUtils.getLogger(this).info(".init()");

  // Determine whether this service has been disabled, and unregister if so
  if (PREFS.getPrefType(SERVICE_ENABLED_PREF) &&
      !PREFS.getBoolPref(SERVICE_ENABLED_PREF))
  {
    this._logger.info("Pref " + SERVICE_ENABLED_PREF
                              + " set to FALSE... not initializing.");
    this.deleteCategories();
    return;
  }

  this.api = new YahooMailAPI();
  profiler = CC["@flock.com/profiler;1"].getService(CI.flockIProfiler);
  var evtID = profiler.profileEventStart("yahoomail-init");

  var prefs = CC["@mozilla.org/preferences-service;1"]
              .getService(CI.nsIPrefBranch2);
  prefs.addObserver(REFRESH_INTERVAL_PREF, this, false);

  this._accountClassCtor = YahooMailAccount;
  // getWD() also adds the "_acUtils" property.
  FlockSvcUtils.getWD(this);
  FlockSvcUtils.initService(this, true);

  // Load auth manager
  this._authMgr = CC["@flock.com/authmgr;1"]
                  .getService(CI.flockIAuthenticationManager);
  this._authMgr.joinNetwork(CONTRACT_ID, "yahoo");

  // Initialize member that specifies path for localized string bundle
  this._stringBundlePath = YAHOOMAIL_STRING_BUNDLE;

  profiler.profileEventEnd(evtID, "");
}

// Migrate coop objects
YahooMailService.prototype._migrateFromCoop =
function ymSvc_migrateFromCoop(aContext) {
  var accountUrns = this.getAccountUrns();
  while (accountUrns.hasMore()) {
    var account = this.getAccount(accountUrns.getNext());
    if (aContext.majorVersion < 2) {
      // 2.0 changes
      account.setCustomParam("isSessionExpired", false);
    }
  }
  // Makes this a generator-iterator
  yield true;
}


/**************************************************************************
 * Flock YahooMailService: flockIWebService Implementation
 **************************************************************************/

// readonly attribute AString urn;
YahooMailService.prototype.urn = "urn:" + CLASS_SHORT_NAME + ":service";

// readonly attribute AString title;
YahooMailService.prototype.title = CLASS_TITLE;

// readonly attribute AString icon;
YahooMailService.prototype.icon = YAHOOMAIL_FAVICON;

// readonly attribute AString contractId;
// ALMOST duplicated by nsIClassInfo::contractID
// with different case: contractId != contractID
// FIXME: File a bug for this as IDL is case-insensitive.
YahooMailService.prototype.contractId = CONTRACT_ID;

// readonly attribute AString shortName;
YahooMailService.prototype.shortName = CLASS_SHORT_NAME;

// readonly attribute boolean needPassword;
YahooMailService.prototype.needPassword = true;

// flockIWebServiceAccount addAccount(in AString aAccountId,
//                                    in boolean aIsTransient,
//                                    in flockIListener aFlockListener);
YahooMailService.prototype.addAccount =
function ymSvc_addAccount(aAccountId, aIsTransient, aFlockListener) {
  this._logger.debug("addAccount('" + aAccountId + "', "
                                    + aIsTransient + ", aFlockListener)");

  if (!aAccountId) {
    if (aFlockListener) {
      var error = CC[FLOCK_ERROR_CONTRACTID].createInstance(CI.flockIError);
      // XXX See bug 10749 - flockIError cleanup
      error.errorCode = 9999;
      aFlockListener.onError(error, "addAccount");
    }
    return null;
  }

  var pw = this._acUtils.getPassword(this.urn + ":" + aAccountId);
  var user = pw ? pw.username : aAccountId;

  // Create a new account and add it to coop if it doesn't already exist
  var accountUrn =
    this._acUtils.createAccount(this, aAccountId, user, null, aIsTransient);

  // Instantiate account component
  var account = this.getAccount(accountUrn);
  account.setParam("service", this._coopObj);

  // Get refresh interval from prefs and ensure it's within bounds
  FlockWebmailUtils.setRefreshInterval(account);

// Add custom parameters to be used
  account.setCustomParam("isPrimary", false);
  account.setCustomParam("isSessionExpired", false);
  account.setCustomParam("hasNewMessages", false);
  account.setCustomParam("lastPrimaryDate", 0);
  account.setCustomParam(STR_COMPOSEURL, "");
  account.setCustomParam(STR_FOLDERURL, "");
  account.setCustomParam(STR_ISCLASSICVIEW, false);
  account.setCustomParam(STR_CLASSICFLAVOR, "");
  account.setCustomParam(STR_ISUTF16, false);
  account.setCustomParam(STR_ISUPGRADEABLE, true);
  account.setCustomParam(STR_INTL, "");
  account.setCustomParam(STR_UNREAD,
                         FlockWebmailUtils.createEmptyMessageAsJSON());
  account.setCustomParam(STR_WAITING_TO_SEND, "");

  if (aFlockListener) {
    aFlockListener.onSuccess(account, "addAccount");
  }
  return account;
}

// void removeAccount(in AString aUrn);
YahooMailService.prototype.removeAccount =
function ymSvc_removeAccount(aUrn) {
  this._logger.debug("removeAccount('" + aUrn + "')");
  this._acUtils.removeAccount(aUrn);
  FlockWebmailUtils.ensurePrimaryAccountExists();
}

// DEFAULT: flockIWebServiceAccount getAccount(in AString aUrn);
// DEFAULT: nsIStringEnumerator getAccountUrns();
// DEFAULT: void logout();


/**************************************************************************
 * Flock YahooMailService: flockILoginWebService Implementation
 **************************************************************************/

// readonly attribute AString domains;
YahooMailService.prototype.__defineGetter__("domains",
function ymSvc_getDomains() {
  this._logger.debug("Getting property: domains");
  return this._WebDetective.getString(CLASS_SHORT_NAME, "domains", DOMAIN);
});

// DEFAULT: boolean docRepresentsSuccessfulLogin(in nsIDOMHTMLDocument aDocument);

// AString getAccountIDFromDocument(in nsIDOMHTMLDocument aDocument);
YahooMailService.prototype.getAccountIDFromDocument =
function ymSvc_getAccountIDFromDocument(aDocument) {
  this._logger.debug("getAccountIDFromDocument()");
  var results = FlockSvcUtils.newResults();
  if (this._WebDetective
          .detect(this.shortName, "accountinfo", aDocument, results))
  {
    // Bug 11658: Use the alternate account id if we can find it.  This should
    // deal with DSL affiliates Yahoo has partnered with, such as AT&T/SBC.
    var altAccountId = null;
    try {
      altAccountId = results.getPropertyAsAString("altaccountid");
    } catch (ex) {
      // normally we would log an error here but in this case, it's ok if we
      // throw an exception because it means the altaccountid doesn't exist
    }
    if (altAccountId) {
      return altAccountId;
    }
    return results.getPropertyAsAString("accountid");
  }
  return null;
}

// DEFAULT: flockILoginInfo getCredentialsFromForm(in nsIDOMHTMLFormElement aForm);
// DEFAULT: boolean ownsDocument(in nsIDOMHTMLDocument aDocument);
// DEFAULT: boolean ownsLoginForm(in nsIDOMHTMLFormElement aForm);

/**
 * @see flockILoginWebService#updateAccountStatusFromDocument
 */
YahooMailService.prototype.updateAccountStatusFromDocument =
function ymSvc_updateAccountStatusFromDocument(aDocument,
                                               aAccount,
                                               aFlockListener)
{
  if (aAccount) {
    // We are currently logged in to aAccount
    if (!this._acUtils.ensureOnlyAuthenticatedAccount(aAccount)) {
      // Just logged in
      FlockWebmailUtils.onLogin(aAccount);
      this._authMgr.reportAccountAuthState(aAccount, true);
      if (aFlockListener) {
        aFlockListener.onSuccess(aAccount, null);
      }
    }

    // Retrieve the language
    var results = FlockSvcUtils.newResults();
    if (this._WebDetective
            .detect(this.shortName, "language", aDocument, results))
    {
      var intl = results.getPropertyAsAString(STR_INTL);
      this._logger.debug("language = " + intl);
      aAccount.setCustomParam(STR_INTL, intl);
    }

    // Store whether we are in Classic Yahoo! Mail view or modern view
    var isClassicView = !this._WebDetective.detect(this.shortName,
                                                   "modernView",
                                                   aDocument,
                                                   null);
    this._logger.debug(isClassicView ? "using classic view"
                                     : "using modern view");
    aAccount.setCustomParam(STR_ISCLASSICVIEW, isClassicView);
    aAccount.setCustomParam(STR_ISUTF16, isClassicView);

    // Store the current session's URL and path as it could potentially be
    // variable due to one of the following:
    // 1) Classic Yahoo! Mail view or modern view
    // 2) Different server farms
    // 3) Different country 
    var ios = CC["@mozilla.org/network/io-service;1"]
              .getService(CI.nsIIOService); 
    var uri = ios.newURI(aDocument.URL, null, null);
    var folderURL;
    var composeURL;

    if (isClassicView) {
      // A further check to see which flavor of Classic we are using. A "new"
      // Classic was released on April 15, 2008 and it has two distinct flavors
      // (as of April 26, 2008).
      var flavor_re = this._WebDetective
                           .getString(this.shortName, "classicFlavor_re", "");
      // Get default version
      var classicFlavor = this._WebDetective.getString(this.shortName,
                                                       "defaultClassicFlavor",
                                                       "mc");
      // Get the flavor from the URL
      var flavor = uri.path.match(flavor_re);
      if (flavor) {
        classicFlavor = flavor[1];
      }
      aAccount.setCustomParam(STR_CLASSICFLAVOR, classicFlavor);
      this._logger.debug("classic flavor = " + classicFlavor);
      // Get the folder path, based on flavor
      var stringName = "classicFolderPath_" + classicFlavor;
      folderURL = uri.prePath + this._WebDetective
                                    .getString(this.shortName, stringName, "");
      // Get the compose path, based on flavor
      stringName = "classicComposePath_" + classicFlavor;
      composeURL = uri.prePath + this._WebDetective
                                     .getString(this.shortName, stringName, "");
    } else {
      // In the New view, the URLs for accessing folders and opening the
      // composer page have historically had two formats:
      //   1. A universal URL which works for all international versions and
      //      server farms.  Eg. http://compose.mail.yahoo.com
      //   2. An action URL which is dependent on the country and server farm.
      //      Eg. http://us.mg2.mail.yahoo.com/dc/launch/action=compose
      // As of June 12, 2008, both the folder and compose URLs use the action
      // format. For each URL (folder and compose) we'll look in the WD file for
      // both formats and use whichever one we find. This facilitates a WD-only
      // update if a URL changes between the two formats.

      // Get the action path
      var idx = uri.path.indexOf("?");
      var actionPath = uri.prePath
                     + ((idx > -1) ? uri.path.substr(0, idx) : uri.path);

      // Get the folder URL. First we look for the folder "Path" rule. If we
      // find it then we know the URL has an action path format. Otherwise,
      // we'll look for a "URL" rule, which means the folder URL has a universal
      // URL format.
      var folderPath = this._WebDetective
                           .getString(this.shortName, "newFolderPath", "");
      if (folderPath) {
        // Build the action URL
        folderURL = actionPath + folderPath;
      } else {
        // Get the universal URL
        folderURL = this._WebDetective
                        .getString(this.shortName, "newFolderURL", "");
      }

      // Get the compose URL, following the same logic we used when getting the
      // folder URL.
      var composePath = this._WebDetective
                            .getString(this.shortName, "newComposePath", "");
      if (composePath) {
        // Build the action URL
        composeURL = actionPath + composePath;
      } else {
        // Get the universal URL
        composeURL = this._WebDetective
                         .getString(this.shortName, "newComposeURL", "");
      }
    }

    // Save folder and compose URLs
    this._logger.debug("Folder URL = " + folderURL);
    aAccount.setCustomParam(STR_FOLDERURL, folderURL);
    this._logger.debug("Compose URL = " + composeURL);
    aAccount.setCustomParam(STR_COMPOSEURL, composeURL);

    // Check if this account is upgradeable to pro status or not
    var upgradeable = true;
    if (isClassicView) {
      upgradeable = !this._WebDetective.detect(this.shortName,
                                               "upgradeAccountClassic",
                                               aDocument,
                                               null);
    } else if (this._WebDetective.detect(this.shortName,
                                         "upgradeAccount",
                                         aDocument,
                                         results))
    {
      upgradeable = (results.getPropertyAsAString("hasMailPlus") == "false");
    }
    this._logger.debug("upgradeable = " + upgradeable);
    aAccount.setCustomParam(STR_ISUPGRADEABLE, upgradeable);

    // Do we have a pending compose message?
    FlockWebmailUtils.checkForPendingComposeMessage(aAccount);
  } else {
    // No login was detected.  So let's see if we can detect a logged out
    // condition.
    var mailLogout =
      this._WebDetective.detect(this.shortName, "loggedout", aDocument, null);
    var yahooNetworkLogout =
      this._WebDetective
          .detect(this.shortName, "completeLogout", aDocument, null);
    if (mailLogout || yahooNetworkLogout) {
      this._logger.debug("We're logged out!");

      // Yes, we're logged out.  We do slightly different things depending on
      // whether we've just logged out of Mail, or the whole Yahoo network.
      var loggedoutAccount = this.getAccount(this.getAuthenticatedAccount());
      this._acUtils.markAllAccountsAsLoggedOut(this);
      if (mailLogout && loggedoutAccount) {
        this._authMgr.reportAccountAuthState(loggedoutAccount, false);
      }
      if (yahooNetworkLogout) {
        this._authMgr.reportCompleteLogoutFromNetwork(CONTRACT_ID);
      }

      // Ensure the webmail icon does not indicate new unread messages while
      // logged out of the primary account
      if (!this._acUtils.primaryAccountIsHot()) {
        FlockWebmailUtils.lightIcon(false);
      }
    }
  }
}


/**************************************************************************
 * Flock YahooMailService: flockIPollingService Implementation
 **************************************************************************/

// void refresh(in AString aUrn, in flockIPollingListener aPollingListener);
YahooMailService.prototype.refresh =
function ymSvc_refresh(aURN, aPollingListener) {
  this._logger.debug("refresh(" + aURN + ")");

  var account = this.getAccount(aURN);
  if (!account.isAuthenticated()) {
    account.setParam("isPollable", false);
    aPollingListener.onResult();
    return;
  }
  var svc = this;
  var refreshListener = {
    onSuccess: function ymSvc_refresh_onSuccess(aSubject, aTopic) {
      svc._logger.debug("refresh_onSuccess: " + aURN + ": " + aSubject);
      FlockWebmailUtils.handleRefreshedMessages(svc, aURN, aSubject);
      aPollingListener.onResult();
    },
    
    onError: function ymSvc_refresh_onError(aFlockError, aTopic) {
      svc._logger.error("unable to refresh unread messages for: " + aURN);
      var expiredLogin = svc._WebDetective.getString(CLASS_SHORT_NAME,
                                                     "expiredCredentials",
                                                     "");
      if (aFlockError.serviceErrorString == expiredLogin) {
        // Light the webmail icon
        FlockWebmailUtils.handleAuthError(account);
      }
      aPollingListener.onError(aFlockError);
    }
  };
  this.api.refreshUnreadMessages(refreshListener,
                                 account.getCustomParam(STR_INTL),
                                 account.getCustomParam(STR_ISCLASSICVIEW));
}


/**************************************************************************
 * Flock YahooMailService: nsIObserver Implementation
 **************************************************************************/

YahooMailService.prototype.observe =
function ymSvc_observe(aSubject, aTopic, aState) {
  switch (aTopic) {
    case "nsPref:changed":
      if (aState == REFRESH_INTERVAL_PREF) {
        this._logger.debug("Observer called: refresh interval changed\n");
        FlockWebmailUtils.setRefreshIntervalForAllAccounts(this);
      }
      break;
  }
}


/**************************************************************************
 * Flock YahooMailService: flockIRichContentDropHandler Implementation
 **************************************************************************/

// boolean handleDrop(in nsITransferable aFlavours,
//                    in nsIDOMHTMLElement aTargetElement);
YahooMailService.prototype.handleDrop =
function ymSvc_handleDrop(aFlavours, aTargetElement) {
  // Handle textarea drops
  if (aTargetElement instanceof CI.nsIDOMHTMLTextAreaElement) {
    return FlockWebmailUtils.handleTextareaDrop(this,
                                                aFlavours,
                                                aTargetElement);
  }

  // Handle drops onto a rich text editor
  var richDnDSvc = CC[FLOCK_RDNDS_CONTRACTID]
                   .getService(CI.flockIRichDNDService);
  if (richDnDSvc.isInRichTextEditor(aTargetElement)) {
    // The rich text editor will most likely be in an iFrame, so there's no way
    // to get a direct link to the top document from the target element. We get
    // the document of the current tab instead. We've just dropped onto it, so
    // we know it exists!
    var win = CC["@mozilla.org/appshell/window-mediator;1"]
              .getService(CI.nsIWindowMediator)
              .getMostRecentWindow("navigator:browser")
    var doc = win.getBrowser().contentDocument;

    // In the "New" version, the plain text editor is rendered in a rich text
    // editor. So we want to drop plain text instead of the default rich text in
    // this case.
    if (this._WebDetective
            .detect(this.shortName, "newViewPlainText", doc, null))
    {
      // Get the plain text data object
      var dataObj = {};
      var len = {};
      aFlavours.getTransferData("text/unicode", dataObj, len);

      // Get plain content from it. We'll also put a line break between the URL
      // and the title.
      var content = dataObj.value.QueryInterface(CI.nsISupportsString)
                           .data.replace(/: /, ":<br />");

      // Add the content to the editor
      richDnDSvc.addToRichTextEditor(content, aTargetElement);
      return true;
    }
  }

  // Default handling otherwise
  return false;
};


/**************************************************************************
 * Flock YahooMailService: flockIMigratable Implementation
 **************************************************************************/

YahooMailService.prototype.__defineGetter__("migrationName",
function ymSvc_getter_migrationName() {
  return flockGetString("common/migrationNames", "migration.name.yahoomail");
});

// boolean needsMigration(in string aOldVersion);
YahooMailService.prototype.needsMigration =
function ymSvc_needsMigration(aOldVersion) {
  // Version: major.minor.revision
  var major = parseInt(aOldVersion[0], 10);
  var minor = parseInt(aOldVersion[2], 10);
  if (major == 1 && (minor == 1 || minor == 2)) {
    // Grackle and Grouse: This service was added for Grackle and changed for
    // Ibis.
    return true;
  }
  return false;
}

// nsISupports startMigration(in string aOldVersion,
//                            in flockIMigrationProgressListener aListener);
YahooMailService.prototype.startMigration =
function ymSvc_startMigration(aOldVersion, aFlockMigrationProgressListener) {
  var ctxt = {
    majorVersion: parseInt(aOldVersion[0], 10),
    minorVersion: parseInt(aOldVersion[2], 10),
    listener: aFlockMigrationProgressListener
  };

  return { wrappedJSObject: ctxt };
}

// boolean doMigrationWork(in nsISupports aMigrationContext);
YahooMailService.prototype.doMigrationWork =
function ymSvc_doMigrationWork(aMigrationContext) {
  var context = aMigrationContext.wrappedJSObject;
  if (!context.yahoomailMigrator) {
    context.yahoomailMigrator = this._migrateFromCoop(context);
  }
  if (context.yahoomailMigrator.next()) {
    context.yahoomailMigrator = null;
  }

  return (context.yahoomailMigrator != null);
}

// void finishMigration(in nsISupports aMigrationContext);
YahooMailService.prototype.finishMigration =
function ymSvc_finishMigration(aMigrationContext) {
}


/**************************************************************************
 * Component: Flock YahooMailAccount
 **************************************************************************/

// Constructor
function YahooMailAccount() {
  FlockSvcUtils.getLogger(this).init(CLASS_SHORT_NAME + ":Account");
  this._logger.info(".init()");
  FlockSvcUtils.getACUtils(this);
  FlockSvcUtils.getCoop(this);
  // BC: Do not use FlockSvcUtils.getWD() here, because it would load
  //     the Web detective file a second time
  this._WebDetective = CC["@flock.com/web-detective;1"]
                       .getService(CI.flockIWebDetective);

  var wsa = FlockSvcUtils.flockIWebServiceAccount;
  wsa.addDefaultMethod(this, "getService");
  wsa.addDefaultMethod(this, "isAuthenticated");
  wsa.addDefaultMethod(this, "getParam");
  wsa.addDefaultMethod(this, "setParam");
  wsa.addDefaultMethod(this, "getCustomParam");
  wsa.addDefaultMethod(this, "getAllCustomParams");
  wsa.addDefaultMethod(this, "setCustomParam");

  // TODO XXX: Setting a private _shortName when adding the
  //           flockIWebmailAccount default methods should only be a
  //           temporary measure until the account has reference to its service
  this._shortName = CLASS_SHORT_NAME;
  var mwsa = FlockSvcUtils.flockIWebmailAccount;
  mwsa.addDefaultMethod(this, "getUnreadMessages");
  mwsa.addDefaultMethod(this, "isPrimary");
  mwsa.addDefaultMethod(this, "isSessionExpired");
  mwsa.addDefaultMethod(this, "refreshUnreadMessageList");
  mwsa.addDefaultMethod(this, "setAsPrimary");
}


/**************************************************************************
 * Flock YahooMailAccount: XPCOM Component Creation
 **************************************************************************/

// Use genericComponent() for the goodness it provides (QI, nsIClassInfo, etc),
// but do NOT add YahooMailAccount to the list of constructors passed
// to FlockXPCOMUtils.genericModule().
YahooMailAccount.prototype = new FlockXPCOMUtils.genericComponent(
  CLASS_NAME + " Account",
  "",
  "",
  YahooMailAccount,
  0,
  [
    CI.flockIWebServiceAccount,
    CI.flockIWebmailAccount
  ]
  );


/**************************************************************************
 * Flock YahooMailAccount: flockIWebServiceAccount Implementation
 **************************************************************************/

// readonly attribute AString urn;
YahooMailAccount.prototype.urn = "";

// void login(in flockIListener aFlockListener);
YahooMailAccount.prototype.login =
function ymAcct_login(aFlockListener) {
  this._logger.debug("login()");
  if (!this._acUtils.ensureOnlyAuthenticatedAccount(this)) {
    FlockWebmailUtils.onLogin(this);
  }
  if (aFlockListener) {
    aFlockListener.onSuccess(this, "login");
  }
};

// void logout(in flockIListener aFlockListener);
YahooMailAccount.prototype.logout =
function ymAcct_logout(aFlockListener) {
  this._logger.debug("logout()");
  if (this.isAuthenticated()) {
    this.coopObj.isPollable = false;
    if (this.isPrimary()) {
      FlockWebmailUtils.lightIcon(false);
    }
    this.getService().logout();
  }
  if (aFlockListener) {
    aFlockListener.onSuccess(this, "logout");
  }
}

// void keep();
YahooMailAccount.prototype.keep =
function ymAcct_keep() {
  this._logger.debug("keep()");
  this.coopObj.isTransient = false;
  var urn = this.urn.replace("account:", "service:").replace("flock:", "");
  this._acUtils.makeTempPasswordPermanent(urn);

  // Automatically set the webmail account as the default mail client on the
  // first time the user configures a webmail account
  if (PREFS.getPrefType("flock.webmail.promptAsDefaultMailer") &&
      PREFS.getBoolPref("flock.webmail.promptAsDefaultMailer"))
  {
    PREFS.setBoolPref("flock.webmail.promptAsDefaultMailer", false);
    PREFS.setBoolPref("flock.webmail.useAsDefaultMailer", true);
  }
}

// DEFAULT: void isAuthenticated();


/**************************************************************************
 * Flock YahooMailAccount: flockIWebmailAccount Implementation
 **************************************************************************/

// AString getComposeURL(in AString aMailParams);
YahooMailAccount.prototype.getComposeURL =
function ymAcct_getComposeURL(aMailParams) {
  this._logger.debug("getComposeURL('" + aMailParams + "')");
  var isNewView = !this.getCustomParam(STR_ISCLASSICVIEW);
  var composeParams = isNewView ? "composeParamsNew" : "composeParamsClassic";
  var params =
    FlockWebmailUtils.reformatMailParams(this, aMailParams, ";", composeParams);
  if (isNewView) {
    // The "new" Yahoo! Mail dies if there are newlines and carriage returns
    // in the params, so we need to replace them. We use 0x08, framed by
    // spaces, as it is very unlikely to encounter this sequences in a mailto
    // string (and if we did, we'd want to replace it regardless). The spaces
    // ensure that the 0x08 is not "bound" to its neighbors when Y!Mail parses
    // the content. These new values will get replaced when we call
    // onComposeURLLoaded() later on.
    if (params) {
      params = params.replace(/%0a/gi, "%20%08%20")
                     .replace(/%0d/gi, "%20%08%20");
    } else {
      // Get around query-string issue with no params
      params = "To=";
    }
  }

  // Add params and country code to URL
  var url = this.getCustomParam(STR_COMPOSEURL)
                .replace("%params%", params)
                .replace("%intl%", this.getCustomParam(STR_INTL));
  this._logger.debug("compose URL is: " + url);
  return url;
}

YahooMailAccount.prototype.onComposeURLLoaded =
function ymAcct_onComposeURLLoaded(aComposeURL, aTabBrowser) {
  this._logger.debug("onComposeURLLoaded()");
  // There are two issues to deal with after the compose page has loaded:
  // 1. In the "New" view, we may have replaced linebreaks and carriage returns
  //    with "%20%08%20", so we have to look for those in the message body and
  //    replace with "<br >". Also, as of October 2008, none of the compose URLs
  //    are filling the composer fields (with the exception of the "To:" field),
  //    so we need to do this programatically.
  // 2. In the "Classic" view, newlines are accepted but not displayed when
  //    using the rich text editor, so we need to look for this case and
  //    manipulate the newlines as well.
  if (this.getCustomParam(STR_ISCLASSICVIEW)) {
    this._onComposeURLLoadedClassicView(aComposeURL, aTabBrowser);
  } else {
    this._onComposeURLLoadedNewView(aComposeURL, aTabBrowser);
  }
}

// AString getEmailAddress();
YahooMailAccount.prototype.getEmailAddress =
function ymAcct_getEmailAddress() {
  this._logger.debug("getEmailAddress()");
  var email;
  var accountId = this.coopObj.accountId;
  if (accountId.indexOf("@") != -1) {
    // Bug 11611. Yahoo has partnered with DSL affiliates (such as AT&T/SBC) to
    // provide webmail support for their customers. In this case, users log in
    // with their affiliated email address instead of a Yahoo account id. We
    // don't need to tack on "yahoo.com" to the account in this case.
    email = accountId;
  } else {
    // Determine the proper domain name to add on to the account name to create
    // the email address.
    var domain = "com";
    var country = this.getCustomParam(STR_INTL);
    if (country) {
      domain = this._WebDetective.getString(CLASS_SHORT_NAME, country, "com");
    }
    email = accountId + "@yahoo." + domain;
  }
  this._logger.debug("email address is: " + email);
  return email;
}

// AString getInboxURL();
YahooMailAccount.prototype.getInboxURL =
function ymAcct_getInboxURL() {
  var stringName = this.getCustomParam(STR_ISCLASSICVIEW)
                 ? "classicInboxParam_"
                   + this.getCustomParam(STR_CLASSICFLAVOR)
                 : "newInboxParam";
  var inboxParam = this._WebDetective
                       .getString(CLASS_SHORT_NAME, stringName, "");
  var url = this.getCustomParam(STR_FOLDERURL)
                .replace("%folder%", inboxParam);
  this._logger.debug("getInboxURL(): " + url);
  return url;
}

// DEFAULT: AString getUnreadMessages(in boolean aCheckNow);

// AString getUpgradeAccount();
YahooMailAccount.prototype.getUpgradeAccount =
function ymAcct_getUpgradeAccount() {
  this._logger.debug("getUpgradeAccount()");
  var upTitle = "";
  var upUrl = "";
  if (this.getCustomParam(STR_ISUPGRADEABLE)) {
    upTitle = this._WebDetective
                  .getString(CLASS_SHORT_NAME, "upgradeAccountTitle", "");
    // Load the brandingUtilities utility
    CC["@mozilla.org/moz/jssubscript-loader;1"]
      .getService(CI.mozIJSSubScriptLoader)
      .loadSubScript("chrome://fbranding/content/brandingUtilities.js");
    upUrl = brandingUtilities.buildWebsiteUrl("webmail",
                                              "webmail",
                                              "upgradeyahoopro",
                                              "none");
  }
  var nsJSON = CC["@mozilla.org/dom/json;1"].createInstance(CI.nsIJSON);
  return nsJSON.encode({title: upTitle, url: upUrl});
}

// DEFAULT: boolean isPrimary();
// DEFAULT: boolean isSessionExpired();
// DEFAULT: void refreshUnreadMessageList();
// DEFAULT: void setAsPrimary();

YahooMailAccount.prototype._onComposeURLLoadedClassicView =
function ymAcct__onComposeURLLoadedClassicView(aComposeURL, aTabBrowser) {
  this._logger.debug("_onComposeURLLoadedClassicView()");
  // The Classic view's rich text editor does not display newlines so we need to
  // pound them into the message body after it has loaded, replacing the "%0A"
  // sequences with "<br />".
  var acct = this;
  var results = FlockSvcUtils.newResults();
  var composerListener = {
    onSuccess: function cv_composeListener_onSuccess(aSubj, aTopic) {
      acct._logger.info("cv_composerListener: onSuccess()");
      // Get the rich text composer's iFrame.
      var iFrame = results.getPropertyAsInterface("node", CI.nsIDOMNode);
      if (iFrame) {
        var bodyListener = {
          onSuccess: function cv_bodyListener_onSuccess(aSubj, aTopic) {
            acct._logger.info("cv_bodyListener: onSuccess()");
            // Replace newlines with breaks
            var body = iFrame.contentDocument.body;
            body.innerHTML = body.innerHTML.replace(/\x0A/g, "<br />");
          },
          onError: function cv_bodyListener_onError(aFlockError, aTopic) {
            acct._logger.error("cv_bodyListener: onError()");
          }
        };
        // Wait for the body to get filled
        acct._WebDetective
            .asyncDetect(CLASS_SHORT_NAME, "classicViewComposerBody",
                         iFrame, null, bodyListener, 250, 80);
      }
    },
    onError: function cv_composerListener_onError(aFlockError, aTopic) {
      acct._logger.error("cv_composerListener: onError()");
    }
  };
  // Wait for the rich text editor to load. If this is the plain text editor
  // then we'll time out.
  var stringName = "classicViewComposer_"
                 + this.getCustomParam(STR_CLASSICFLAVOR);
  this._WebDetective
      .asyncDetect(CLASS_SHORT_NAME, stringName,
                   aTabBrowser, results, composerListener, 250, 80);
}

YahooMailAccount.prototype._onComposeURLLoadedNewView =
function ymAcct__onComposeURLLoadedNewView(aComposeURL, aTabBrowser) {
  this._logger.debug("_onComposeURLLoadedNewView()");
  // As of October 2008, the New view doesn't support the mailto params in its
  // URL, so we will fill the fields after the composer loads. If the mailto
  // functionality is restored, this post-processing can be turned off by
  // changing the "onComposeURLLoadedNewView" string in yahoomail.xml to "none".
  var action = this._WebDetective.getString(CLASS_SHORT_NAME,
                                            "onComposeURLLoadedNewView",
                                            "none");
  if (action == "none") {
    this._onComposeURLLoadedNewViewNoFill(aComposeURL, aTabBrowser);
  } else {
    this._onComposeURLLoadedNewViewFill(aComposeURL, aTabBrowser);
  }
};

YahooMailAccount.prototype._onComposeURLLoadedNewViewFill =
function ymAcct__onComposeURLLoadedNewView(aComposeURL, aTabBrowser) {
  this._logger.debug("_onComposeURLLoadedNewViewFill()");
  // Wait for composer tab to load and then fill in the fields
  var inst = this;
  var composerListener = {
    onSuccess: function nvf_composeListener_onSuccess(aSubj, aTopic) {
      inst._logger.info("nvf_composerListener: onSuccess()");
      var results = FlockSvcUtils.newResults();
      var doc = aTabBrowser.contentDocument;
      function _getElement(aType, aName) {
        if (inst._WebDetective.detect(CLASS_SHORT_NAME, aType, doc, results)) {
          return results.getPropertyAsInterface(aName, CI.nsIDOMNode);
        }
        return null;
      }
      // Parse the mailto parameters and values out of the compose URL
      var params = FlockWebmailUtils.parseMailParams(inst,
                                                     aComposeURL,
                                                     "composeParamsNew");
      // To - if there's a "to" parameter, then look for the "To:" field
      // on the page and set its value to the parameter
      if (params.to) {
        var toElement = _getElement("newViewComposerTo", "to");
        if (toElement) {
          toElement.value = params.to;
        }
      }
      // Cc
      if (params.cc) {
        var ccElement = _getElement("newViewComposerCc", "cc");
        if (ccElement) {
          ccElement.value = params.cc;
        }
      }
      // Bcc
      if (params.bcc) {
        // Show the Bcc field. We need to do this before filling the field. If
        // we fill the field first, then Y!Mail will pop a dialog asking the
        // user to confirm.
        var showBccElement = _getElement("newViewComposerShowBcc", "show_bcc");
        if (showBccElement) {
          // Simulate a click on the link
          var win = showBccElement.ownerDocument.defaultView.top;
          var evt = doc.createEvent("MouseEvents");
          evt.initMouseEvent("click", true, true, win, 0, 0, 0, 0, 0,
                             false, false, false, false, 0, null);
          showBccElement.dispatchEvent(evt);
        }
        // Now fill the field
        var bccElement = _getElement("newViewComposerBcc", "bcc");
        if (bccElement) {
          bccElement.value = params.bcc;
        }
      }
      // Subject
      if (params.subject) {
        var subjElement = _getElement("newViewComposerSubj", "subject");
        if (subjElement) {
          // Ensure the subject content is properly URL decoded while we
          // are here; See bugs 11734 & 14386
          subjElement.value = flockXMLDecode(params.subject);
        }
      }
      // Body
      if (params.body) {
        var iFrameElement = _getElement("newViewComposerIFrame", "iframe");
        if (iFrameElement) {
          var body = iFrameElement.contentDocument.body;
          // Restore newlines and returns as breaks in the param body text.
          // Also, prepend to the current body, so we don't wipe out any
          // signature that might be present.
          body.innerHTML = params.body.replace(/ ?\x08 ?/g, "<br />")
                         + body.innerHTML;
        }
      }
    },
    onError: function nvf_composerListener_onError(aFlockError, aTopic) {
      inst._logger.error("nvf_composerListener: onError()");
    }
  };
  // Wait for new message tab to load
  this._WebDetective
      .asyncDetect(CLASS_SHORT_NAME, "newViewComposer",
                   aTabBrowser, null, composerListener, 250, 80);
};

YahooMailAccount.prototype._onComposeURLLoadedNewViewNoFill =
function ymAcct__onComposeURLLoadedNewView(aComposeURL, aTabBrowser) {
  this._logger.debug("_onComposeURLLoadedNewViewNoFill()");
  // The New view cannot handle newlines in the compose URL so we need to pound
  // them into the message body after it has loaded, replacing the "%20%08%20"
  // sequences we inserted in getComposeURL() with "<br />".
  var inst = this;
  var composerListener = {
    onSuccess: function nv_composeListener_onSuccess(aSubj, aTopic) {
      inst._logger.info("nv_composerListener: onSuccess()");
      // Get the composer's iFrame
      var composerIFrameId = inst._WebDetective
                                 .getString(CLASS_SHORT_NAME,
                                            "newViewComposerIFrame",
                                            "compArea_test_1");
      var iFrame = aTabBrowser.contentDocument.getElementById(composerIFrameId);
      if (iFrame && iFrame instanceof CI.nsIDOMHTMLIFrameElement) {
        var bodyListener = {
          onSuccess: function nv_bodyListener_onSuccess(aSubj, aTopic) {
            inst._logger.info("nv_bodyListener: onSuccess()");
            // Restore newlines and returns as breaks
            var body = iFrame.contentDocument.body;
            body.innerHTML = body.innerHTML.replace(/ ?\x08 ?/g, "<br />");
          },
          onError: function nv_bodyListener_onError(aFlockError, aTopic) {
            inst._logger.error("nv_bodyListener: onError()");
          }
        };
        // Wait for the body to get filled
        inst._WebDetective
            .asyncDetect(CLASS_SHORT_NAME, "newViewComposerBody",
                         iFrame, null, bodyListener, 250, 80);
      }

      // Ensure the subject content is properly URL decoded while we
      // are here; See bug 11734 & 14386
      var results = FlockSvcUtils.newResults();
      var subjectFlockListener = {
        onSuccess: function nv_subjectFlockListener_onSuccess(aSubject, aTopic) {
          inst._logger.info("nv_subjectFlockListener: onSuccess()");
          var subject =
            results.getPropertyAsInterface("subject", CI.nsIDOMNode);
          if (subject &&
              subject instanceof CI.nsIDOMHTMLInputElement &&
              subject.value.length > 0)
          {
            subject.value = flockXMLDecode(subject.value);
          }
        },
        onError: function nv_subjectFlockListener_onError(aFlockError, aTopic) {
          inst._logger.error("nv_subjectFlockListener: onError()");
        }
      };
      // Wait for the subject to get filled
      inst._WebDetective
          .asyncDetect(CLASS_SHORT_NAME, "newViewComposerSubject",
                       aTabBrowser, results, subjectFlockListener, 250, 80);
    },
    onError: function nv_composerListener_onError(aFlockError, aTopic) {
      inst._logger.error("nv_composerListener: onError()");
    }
  };
  // Wait for new message tab to load
  this._WebDetective
      .asyncDetect(CLASS_SHORT_NAME, "newViewComposer",
                   aTabBrowser, null, composerListener, 250, 80);
};

/**************************************************************************
 * Component: Flock YahooMailAPI
 **************************************************************************/

// Constructor
function YahooMailAPI() {
  FlockSvcUtils.getLogger(this).info(".init() - API");
}

YahooMailAPI.prototype.appid = "flock-search";

YahooMailAPI.prototype.wssid = "";

YahooMailAPI.prototype.url = "";

YahooMailAPI.prototype.refreshUnreadMessages =
function ymAPI_refreshUnreadMessages(aFlockListener,
                                     aCountryCode,
                                     aIsClassicView)
{
  this._logger.debug("refreshUnreadMessages('" + aCountryCode + "', "
                                               + aIsClassicView + ")");
  var method = "ListMessages";
  // Retrieve a reasonable number of unread messages as retrieving all unread
  // messages may be excessive and unnecessary
  // groupBy=unRead messages with flag unRead=0 will be at top of message list
  // returned by Yahoo
  var param = "&fid=Inbox&groupBy=unRead&numInfo=" + MAX_UNREAD_COUNT;
  this._setYahooSpecificURL(aCountryCode, method, param);
  this._processUnreadMessages(aFlockListener,
                              aCountryCode,
                              method,
                              param,
                              aIsClassicView);
}

YahooMailAPI.prototype._buildUnreadMessagesJSON =
function ymAPI__buildUnreadMessagesJSON(aXML, aIsClassicView) {
  this._logger.debug("_buildUnreadMessagesJSON()");
  var webDetective = CC["@flock.com/web-detective;1"]
                     .getService(CI.flockIWebDetective);
  var encodeMID = webDetective.getString(CLASS_SHORT_NAME,
                                         "encodeMessageIDForBetaView",
                                         null);
  var baseMsgUrl = webDetective.getString(CLASS_SHORT_NAME,
                                          "messageURL",
                                          YAHOOMAIL_MESSAGE_URL + "/msg?");
  var folder = aXML.getElementsByTagName("folder")[0];
  var count = parseInt(folder.getAttribute("unread"));
  var messageInfos = aXML.getElementsByTagName("messageInfo");
  var messages = [];
  var newestMessageDate = 0;
  for (var i = 0; i < messageInfos.length; i++) {
    var msg = messageInfos[i];
    var unreadElement = msg.getElementsByTagName("flags")[0];
    if (unreadElement && unreadElement.hasAttribute("isRead")) {
      var unread = parseInt(unreadElement.getAttribute("isRead"));
      // We reached the end of our unread message this message has been read
      if (unread == 1) {
        break;
      }
    }
    var from = msg.getElementsByTagName("from")[0];
    var fromString = "";
    if (from && from.hasChildNodes()) {
      fromString = from.getElementsByTagName("name")[0].firstChild.nodeValue;
    }
    var mid = msg.getAttribute("mid");
    // Due to a server side issue in Yahoo! Mail's new Beta view, the encoded
    // message id in the URL does not get passed along properly when the page
    // is redirected.  We need to encode the message id twice.
    // Since Yahoo may fix this issue server side, we may need to bypass our
    // initial encoding altogether.
    if (!aIsClassicView && encodeMID) {
      mid = encodeURIComponent(mid);
    }
    var msgUrl = baseMsgUrl + "fid=Inbox&mid=" + encodeURIComponent(mid);
    this._logger.debug("  from = " + fromString
                     + "\n  subject = " + msg.getAttribute("subject")
                     + "\n  message URL = " + msgUrl);
    messages.push({
      from: fromString,
      subject: msg.getAttribute("subject"),
      url: msgUrl
    });

    // Grab the date of the newest message
    if (!newestMessageDate) {
      newestMessageDate = msg.getAttribute("receivedDate");
    }
  }

  var unreadMessages = {
    lastCheckedDate: Date.now(),
    newestMessageDate: newestMessageDate,
    unreadMessageCount: count,
    messages: messages
  };
  var nsJSON = CC["@mozilla.org/dom/json;1"].createInstance(CI.nsIJSON);
  return nsJSON.encode(unreadMessages);
}

YahooMailAPI.prototype._setYahooSpecificURL =
function ymAPI__setYahooSpecificURL(aCountryCode, aMethod, aParams) {
  this._logger.debug("_setYahooSpecificURL('" + aCountryCode + "', '"
                                              + aMethod + "', '"
                                              + aParams + "')");
  var webDetective = CC["@flock.com/web-detective;1"]
                     .getService(CI.flockIWebDetective);
  var apiUrl = webDetective.getString(CLASS_SHORT_NAME,
                                      "mailAPIUrl",
                                      YAHOOMAIL_URL);
  apiUrl = "http://" + aCountryCode + "." + apiUrl;
  this.url = apiUrl + aMethod + "&appid=" + this.appid
           + aParams + "&wssid=" + this.wssid;
}

YahooMailAPI.prototype._processUnreadMessages =
function ymAPI__processUnreadMessages(aFlockListener,
                                      aCountryCode,
                                      aMethod,
                                      aParam,
                                      aIsClassicView)
{
  this._logger.debug("_processUnreadMessages('" + aCountryCode + "', '"
                                                + aMethod + "', '"
                                                + aParam + "', "
                                                + aIsClassicView + ")");
  var req = CC["@mozilla.org/xmlextras/xmlhttprequest;1"]
            .createInstance(CI.nsIXMLHttpRequest);
  req instanceof Components.interfaces.nsIJSXMLHttpRequest;
  req.mozBackgroundRequest = true;
  req.open('GET', this.url, true);
  var inst = this;
  var count = 0;
  var messages = [];
  req.onreadystatechange = function onready(aEvt) {
    if (req.readyState == 4) {
      var status = req.status; 
      var responseXML = req.responseXML;
      if (status == 200 || status == 201) {
        try {
          var msgs = inst._buildUnreadMessagesJSON(responseXML,
                                                   aIsClassicView);
          aFlockListener.onSuccess(msgs, "processUnreadMessages");
        } catch (e) {
          inst._logger
              .debug("_processUnreadMessages: " + e + " " + e.lineNumber);
          var error = CC[FLOCK_ERROR_CONTRACTID].createInstance(CI.flockIError);
          // XXX See bug 10749 - flockIError cleanup
          error.errorCode = CI.flockIError.WEBMAIL_API_CANNOT_PARSE;
          aFlockListener.onError(error, "processUnreadMessages");
        }
      } else {
        // HTTP errors (0 for connection lost)
        inst._logger.debug("_processUnreadMessages: Failed! Status: " + status);
        var serviceError = null;
        if (responseXML) {
          var element = responseXML.getElementsByTagName("url")[0];
          if (element) {
            // Extract wssid from URL and try again
            inst._logger.debug("_processUnreadMessages: Try retrieving unread "
                               + "messages again");
            var url = element.firstChild.nodeValue;
            var urlString = "&wssid=";
            var index = url.lastIndexOf(urlString) + urlString.length;
            inst.wssid = url.substring(index, url.length);
            inst._setYahooSpecificURL(aCountryCode, aMethod, aParam);
            return inst._processUnreadMessages(aFlockListener,
                                               aCountryCode,
                                               aMethod,
                                               aParam,
                                               aIsClassicView);
          }
          element = responseXML.getElementsByTagName("code")[0];
          if (element) {
            // This will be used to determine if login credentials have expired
            serviceError = element.firstChild.nodeValue;
            inst._logger.info("_processUnreadMessages: " + serviceError);
          } else {
            inst._logger.error("_processUnreadMessages: Unexpected error: "
                               + req.responseText);
          }
        }
        var error = CC[FLOCK_ERROR_CONTRACTID].createInstance(CI.flockIError);
        error.errorCode = status;
        error.serviceErrorString = serviceError;
        aFlockListener.onError(error, "_processUnreadMessages");
      }
    }
  }
  req.send(null);
}


/**************************************************************************
 * XPCOM Support - Module Construction
 **************************************************************************/

// Create array of components.
var gComponentsArray = [YahooMailService];

// Generate a module for XPCOM to find.
var NSGetModule = FlockXPCOMUtils.generateNSGetModule(MODULE_NAME,
                                                      gComponentsArray);

/**************************************************************************
 * END XPCOM Support
 **************************************************************************/

