// 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/FlockXMLUtils.jsm");

const BLOGSOME_CID = Components.ID('{c36e232f-9a3e-4848-b9a8-d7a880a7544f}');
const BLOGSOME_CONTRACTID = '@flock.com/service/blogsome;1';

const BLOGSOME_FAVICON = "chrome://flock/content/services/blogsome/favicon.png";
const SERVICE_ENABLED_PREF = "flock.service.blogsome.enabled";
const CLASS_NAME = "Blogsome";
const CLASS_SHORT_NAME = "blogsome";
const MODULE_NAME = "blogsome"

function userBlogsListener(aBlogListener){
  this.blogListener = aBlogListener;
}

userBlogsListener.prototype =
{
  // aSimpleEnum is an Array (simpleEnumerator) of struct objects
  onResult: function ubl_onResult(aSimpleEnum) {
    var result = new Array();
    for each (var entry in aSimpleEnum){
      var blog = CC["@mozilla.org/hash-property-bag;1"]
                 .createInstance(CI.nsIWritablePropertyBag2);
      blog.setPropertyAsAString("title", entry.blogName);
      blog.setPropertyAsAString("blogID", entry.blogid);
      blog.setPropertyAsAString("APILink", "");
      blog.setPropertyAsAString("URL", entry.url);
      result.push(blog);
    }
    this.blogListener.onResult(new simpleEnumerator(result));
  },
  onError: function ubl_onError(aFlockError) {
    // Stupid server doesn't return any correct XML, but just a plain text error message
    if (aFlockError.serviceErrorString.match("blog doesn't exist")) {
      aFlockError.errorCode = CI.flockIError.BLOG_INVALID_AUTH;
    } else {
      aFlockError.errorCode = CI.flockIError.BLOG_UNKNOWN_ERROR;
    }
    this.blogListener.onError(aFlockError);
  },
  onFault: function ubl_onFault(aFlockError) {
    switch (aFlockError.serviceErrorCode) {
      case 403: // Invalid login/pass
        aFlockError.errorCode = CI.flockIError.BLOG_INVALID_AUTH;
        break;
      default: // Unknown error code
        aFlockError.errorCode = CI.flockIError.BLOG_UNKNOWN_ERROR;
    }
    this.blogListener.onFault(aFlockError);
  }
}

/*************************************************************************
 * Component: flockBSService
 *************************************************************************/

function flockBSService () {
  var loader = CC["@mozilla.org/moz/jssubscript-loader;1"]
               .getService(CI.mozIJSSubScriptLoader);
  loader.loadSubScript('chrome://browser/content/utilityOverlay.js');
  loader.loadSubScript("chrome://flock/content/xmlrpc/xmlrpchelper.js");
  loader.loadSubScript("chrome://flock/content/blog/blogBackendLib.js");

  this._profiler = CC["@flock.com/profiler;1"].getService(CI.flockIProfiler);

  this._init();

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

  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, "getSessionValue");
  lws.addDefaultMethod(this, "removeAccount");
  lws.addDefaultMethod(this, "docRepresentsSuccessfulLogin");
  lws.addDefaultMethod(this, "getCredentialsFromForm");
  lws.addDefaultMethod(this, "ownsDocument");
  lws.addDefaultMethod(this, "ownsLoginForm");
}

/*************************************************************************
 * flockBSService: XPCOM Component Creation
 *************************************************************************/

flockBSService.prototype = new FlockXPCOMUtils.genericComponent(
  CLASS_NAME,
  BLOGSOME_CID,
  BLOGSOME_CONTRACTID,
  flockBSService,
  CI.nsIClassInfo.SINGLETON,
  [
    CI.flockIWebService,
    CI.flockILoginWebService,
    CI.flockIBlogWebService
  ]
);

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

/*************************************************************************
 * flockBSService: Private Data and Functions
 *************************************************************************/

flockBSService.prototype._init =
function flockBSService__init() {
  FlockSvcUtils.getLogger(this);
  this._logger.debug(".init()");

  var evtID = this._profiler.profileEventStart("blogsome-init");

  this.prefService = CC["@mozilla.org/preferences-service;1"]
                     .getService(CI.nsIPrefBranch);
  if (this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
      !this.prefService.getBoolPref(SERVICE_ENABLED_PREF))
  {
    this._logger.debug("Pref "
                       + SERVICE_ENABLED_PREF
                       + " set to FALSE... not initializing.");
    this.deleteCategories();
    return;
  }

  // Attributes of flockIBlogWebService
  this.supportsCategories = CI.flockIBlogWebService.CATEGORIES_MULTIPLE;
  this.supportsPostReplace = true;
  this.metadataOverlay = "";

  // getWD() also adds the "_acUtils" property.
  FlockSvcUtils.getWD(this);
  FlockSvcUtils.initService(this);

  this._accountClassCtor = flockBSAccount;

  this._coopObj.logoutOption = false;

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

  this._profiler.profileEventEnd(evtID, "");
}

/*************************************************************************
 * flockBSService: flockIWebService Implementation
 *************************************************************************/

// readonly attribute AString contractId;
// ALMOST duplicated by nsIClassInfo::contractID
// with different case: contractId != contractID
flockBSService.prototype.contractId = BLOGSOME_CONTRACTID;

// readonly attribute AString icon;
flockBSService.prototype.icon = BLOGSOME_FAVICON;

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

// readonly attribute AString title;
flockBSService.prototype.title = CLASS_NAME;

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

/*************************************************************************
 * flockBSService: flockIBlogWebService interface
 *************************************************************************/

// readonly attribute AString authtoken;
flockBSService.prototype.authtoken = null;

flockBSService.prototype.newPost =
function(aListener, aBlogId, aPost, aPublish, aNotifications)
{
  var mtService = CC["@flock.com/blog/service/movabletype;1"]
                  .getService(CI.flockIBlogWebService);
  mtService.newPost(aListener, aBlogId, aPost, aPublish, aNotifications);
}

flockBSService.prototype.editPost =
function(aPublishListener, aBlogId, aPost, aPublish, aNotifications)
{
  var mtService = CC["@flock.com/blog/service/movabletype;1"]
                  .getService(CI.flockIBlogWebService);
  mtService.editPost(aPublishListener, aBlogId, aPost, aPublish, aNotifications);
}

flockBSService.prototype.getUsersBlogs =
function flockBSService_getUsersBlogs(aBlogListener, aAPILink, aUsername, aPassword)
{
  var rpcListener = new userBlogsListener(aBlogListener, false);
  var xmlrpcServer = new flockXmlRpcServer (aAPILink);
  var args = ["flockbrowser", aUsername, aPassword];
  xmlrpcServer.call("blogger.getUsersBlogs", args, rpcListener);
}

flockBSService.prototype.getRecentPosts =
function(aListener, aBlogId, aNumber)
{
  var mtService = CC["@flock.com/blog/service/movabletype;1"]
                  .getService(CI.flockIBlogWebService);
  mtService.getRecentPosts(aListener, aBlogId, aNumber);
}

flockBSService.prototype.getCategoryList =
function(aBlogListener, aBlogId)
{
  var mtService = CC["@flock.com/blog/service/movabletype;1"]
                  .getService(CI.flockIBlogWebService);
  mtService.getCategoryList(aBlogListener, aBlogId);
}

/*************************************************************************
 * flockBSService: flockILoginWebService interface
 *************************************************************************/

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

// readonly attribute AString domains;
flockBSService.prototype.__defineGetter__("domains",
function flockBSService_getdomains() {
  this._logger.debug("Getting property: domains");
  return this._WebDetective.getString("blogsome", "domains", "blogsome.com");
});

flockBSService.prototype.addAccount =
function flockBSService_addAccount(aUsername, aIsTransient, aFlockListener)
{
  var accountURN = this._acUtils.createAccount(this,
                                              aUsername,
                                              aUsername,
                                              null,
                                              aIsTransient);
  var account = this.getAccount(accountURN);
  account.setParam("service", this._coopObj);
  this.USER = accountURN;

  // Add the blog account
  var bssvc = this;
  var blogListener = {
    onResult: function(aResult) {
      var theBlog;
      while (aResult.hasMoreElements()) {
        theBlog = aResult.getNext();
        theBlog.QueryInterface(CI.nsIPropertyBag2);
        var blogID = theBlog.getPropertyAsAString("blogID");
        var URL = theBlog.getPropertyAsAString("URL");
        var blogURN = accountURN + ":" + blogID;
        theCoopBlog = new bssvc._coop.Blog(blogURN, {
          name: theBlog.getPropertyAsAString("title"),
          title: theBlog.getPropertyAsAString("title"),
          blogid: blogID,
          URL: URL,
          apiLink: URL + "xmlrpc.php"
        });
        account.coopObj.children.addOnce(theCoopBlog);

        // Comments: create the "My Blogs" folder if needed
        var feedManager = CC["@flock.com/feed-manager;1"]
                          .getService(CI.flockIFeedManager);
        var blogFolder = null;
        var _enum = feedManager.getFeedContext("news").getRoot().getChildren();
        while (_enum.hasMoreElements() && !blogFolder) {
          var candidate = _enum.getNext();
          if (candidate.getTitle() == "My Blogs") {
            blogFolder = candidate;
          }
        }
        if (!blogFolder) {
          blogFolder = feedManager.getFeedContext("news")
                                  .getRoot()
                                  .addFolder("My Blogs");
        }
        // Comments: add the stream
        var ios = CC["@mozilla.org/network/io-service;1"]
                  .getService(CI.nsIIOService);
        var uri = ios.newURI(URL + "comments/feed/", null, null);
        var feedManagerListener = {
          onGetFeedComplete: function(feed) {
            blogFolder.subscribeFeed(feed);
            CC["@flock.com/metrics-service;1"]
            .getService(CI.flockIMetricsService)
            .report("FeedsSidebar-AddFeed", "BlogsomeNewAccount");
          },
          onError: function(error) {
          }
        }
        feedManager.getFeed(uri, feedManagerListener);
      }
      if (aFlockListener) {
        aFlockListener.onSuccess(account, "addAccount");
      }
    },
    onFault: function(aError) {
      bssvc._logger.debug("flockBlogsomeService: fault in addAccount: " + aError);
      account.coopObj.destroy();
      if (aFlockListener) {
        aFlockListener.onError(aError, "FAULT");
      }
    },
    onError: function(aError) {
      bssvc._logger.debug("flockBlogsomeService: error in addAccount: "+aError);
      account.coopObj.destroy();
      if (aFlockListener) {
        aFlockListener.onError(aError, "ERROR");
      }
    }
  };

  var apiLink = "http://" + aUsername + ".blogsome.com/xmlrpc.php";
  var pw = this._acUtils.getPassword(this.urn + ":" + aUsername);
  this.getUsersBlogs(blogListener, apiLink, aUsername, pw.password);

  return account;
}

flockBSService.prototype.getAccountIDFromDocument =
function flockBSService_getAccountIDFromDocument(aDocument)
{
  this._logger.debug(".getAccountIDFromDocument(...)");
  aDocument.QueryInterface(Components.interfaces.nsIDOMHTMLDocument);

  // The URL is the form http://<username>.blogsome.com
  var ios = Components.classes["@mozilla.org/network/io-service;1"]
                      .getService(Components.interfaces.nsIIOService);
  var uri = ios.newURI(aDocument.URL, null, null);
  return uri.host.split(".")[0];
}

flockBSService.prototype.updateAccountStatusFromDocument =
function flockBSService_updateAccountStatusFromDocument(aDocument,
                                                        aAccount,
                                                        aFlockListener)
{
  if (aAccount) {
    this._acUtils.ensureOnlyAuthenticatedAccount(aAccount);
  } else {
    var login = aDocument.getElementById("Login");
    if (login) {
      this._acUtils.markAllAccountsAsLoggedOut(this);
    }
  }
}

flockBSService.prototype.logout =
function flockBSService_logout()
{
  this._logger.debug(".logout()");
  this._acUtils.markAllAccountsAsLoggedOut(this);
  var cookieManager = CC["@mozilla.org/cookiemanager;1"]
                      .getService(CI.nsICookieManager);
  var allCookies = cookieManager.enumerator;
  while (allCookies.hasMoreElements()) {
    var cookie = allCookies.getNext()
                           .QueryInterface(CI.nsICookie);
    if (cookie.host.match("blogsome.com")) {
      cookieManager.remove(cookie.host, cookie.name, cookie.path, false);
    }
  }
}

/*************************************************************************
 * Component: flockBSAccount
 *************************************************************************/

function flockBSAccount() {
  FlockSvcUtils.getLogger(this).info(".init()");
  FlockSvcUtils.getACUtils(this);
  FlockSvcUtils.getCoop(this);

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

/**************************************************************************
 * flockBSAccount: XPCOM Component Creation
 **************************************************************************/

flockBSAccount.prototype = new FlockXPCOMUtils.genericComponent(
  CLASS_NAME + " Account",
  "",
  "",
  flockBSAccount,
  0,
  [
    CI.flockIWebServiceAccount,
    CI.flockIBlogAccount
  ]
);

/**************************************************************************
 * flockBSAccount: flockIWebServiceAccount Implementation
 **************************************************************************/

flockBSAccount.prototype.login = function BSAccount_login(aFlockListener) {
  this._logger.info("{flockIWebServiceAccount}.login()");
  if (aFlockListener) {
    aFlockListener.onSuccess(this, "login");
  }
}

flockBSAccount.prototype.logout = function BSAccount_logout(aFlockListener) {
  this._logger.info("{flockIWebServiceAccount}.logout()");
  if (this.coopObj.isAuthenticated) {
    var cookieManager = Components.classes["@mozilla.org/cookiemanager;1"]
                                  .getService(Components.interfaces.nsICookieManager);
    var allCookies = cookieManager.enumerator;
    var cookieHost = this.coopObj.accountId+".blogsome.com";
    while (allCookies.hasMoreElements()) {
      var cookie = allCookies.getNext()
                             .QueryInterface(Components.interfaces.nsICookie);
      if (cookie.host == cookieHost) {
        cookieManager.remove(cookieHost, cookie.name, cookie.path, false);
      }
    }
  }
  if (aFlockListener) {
    aFlockListener.onSuccess(this, "logout");
  }
}

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

// Create array of components.
var componentsArray = [flockBSService];

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