// BEGIN FLOCK GPL
// 
// Copyright Flock Inc. 2005-2006
// http://flock.com
// 
// This file may be used under the terms of 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
//

// Constants
const DEBUG = false;                // set to false to suppress debug messages
const FLOCK_BLOG_CID                = Components.ID('{3ebdb169-dbf3-441e-a461-5567bc82cc0c}');
const FLOCK_BLOG_FNAME              = "flock_blog.rdf";
const BLOG_RECENT_COUNT             = 5;

const nsISupports                   = Components.interfaces.nsISupports;
const nsIClassInfo                  = Components.interfaces.nsIClassInfo;
const nsIFactory                    = Components.interfaces.nsIFactory;
const nsIProperties                 = Components.interfaces.nsIProperties;
const nsILocalFile                  = Components.interfaces.nsILocalFile;
const nsIFile                       = Components.interfaces.nsIFile;
const nsIIOService                  = Components.interfaces.nsIIOService;
const nsIFileProtocolHandler        = Components.interfaces.nsIFileProtocolHandler;
const flockIBlogService             = Components.interfaces.flockIBlogService;
const nsIXMLHttpRequest             = Components.interfaces.nsIXMLHttpRequest;
const nsIRDFCompositeDataSource            = Components.interfaces.nsIRDFCompositeDataSource;
const nsIRDFRemoteDataSource               = Components.interfaces.nsIRDFRemoteDataSource;
const nsIRDFDataSource                     = Components.interfaces.nsIRDFDataSource;
const nsIRDFObserver                       = Components.interfaces.nsIRDFObserver;
const nsIRDFService                        = Components.interfaces.nsIRDFService;
const nsIRDFContainerUtils                 = Components.interfaces.nsIRDFContainerUtils;
const nsIRDFContainer                      = Components.interfaces.nsIRDFContainer;
const nsIRDFNode                           = Components.interfaces.nsIRDFNode;
const nsIRDFResource                       = Components.interfaces.nsIRDFResource;
const nsIRDFLiteral                        = Components.interfaces.nsIRDFLiteral;
const nsIRDFInt                            = Components.interfaces.nsIRDFInt;
const nsIRDFDate                           = Components.interfaces.nsIRDFDate;
const nsIRDFBlob                           = Components.interfaces.nsIRDFBlob;


const FLOCK_BLOG_CONTRACTID         = '@mozilla.org/rdf/datasource;1?name=flock-blog';
const DIRECTORY_SERVICE_CONTRACTID  = '@mozilla.org/file/directory_service;1';
const LOCAL_FILE_CONTRACTID         = '@mozilla.org/file/local;1';
const PREFERENCES_CONTRACTID        = '@mozilla.org/preferences-service;1';
const IO_SERVICE_CONTRACTID         = '@mozilla.org/network/io-service;1';
const XMLHTTPREQUEST_CONTRACTID     = '@mozilla.org/xmlextras/xmlhttprequest;1';
const RDFSERVICE_CONTRACTID         = '@mozilla.org/rdf/rdf-service;1';
const RDFCONTUTILS_CONTRACTID       = '@mozilla.org/rdf/container-utils;1';
const CONTAINER_CONTRACTID          = '@mozilla.org/rdf/container;1';

const FLOCK_NS                      = 'http://www.flock.com/rdf#';
const FLOCK_BLOG_RDF_RECENT_ROOT    = 'urn:flock:blog:recent';
const FLOCK_BLOG_RDF_SETTINGS       = 'urn:flock:blog:settings';
const FLOCK_BLOG_RDF_PREF_ROOT      = "urn:flock:blog:preferences";
const NOTIFICATIONS_ROOT = FLOCK_BLOG_RDF_PREF_ROOT + ":notifications";

// Services
const RDFS = Components.classes[RDFSERVICE_CONTRACTID].getService (nsIRDFService);
const RDFCU = Components.classes[RDFCONTUTILS_CONTRACTID].getService (nsIRDFContainerUtils);
const DIRS = Components.classes[DIRECTORY_SERVICE_CONTRACTID].getService (nsIProperties);

// rdf namespaces
function Namespace (ns) { return function (arg) { return RDFS.GetResource (ns+arg); } }
const W3RDF                     = Namespace ('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
const NSRDF                     = Namespace ('http://home.netscape.com/NC-rdf#');
const WBRDF                     = Namespace ('http://home.netscape.com/WEB-rdf#');
const FLRDF                     = Namespace ('http://www.flock.com/rdf#');


function BlogAPIDetector() {
  this.mReq = null;
  this.mTitle = null;
}

BlogAPIDetector.prototype.getTitle = function()
{
  return this.mTitle;
}

BlogAPIDetector.prototype.doRequest = function(listener, url, processor)
{
  var inst = this;
  var rootUrl = 'http://'+url.split('/')[2];
  this.mReq = Components.classes[XMLHTTPREQUEST_CONTRACTID].createInstance(nsIXMLHttpRequest);
  this.mReq.onreadystatechange = function (aEvt) {
    try {
      if(inst.mReq.readyState == 4) {
        if(inst.mReq.status == 200) {
          // dump(inst.mReq.responseText + "\n");
          try {
            processor(listener, rootUrl, inst);
          }
          catch(e) {
            dump("Error "+e+" "+e.fileName+" line "+e.lineNumber+"\n");
            listener.onError(e);
          }
        }
        else {
          dump("\nRESPONSE\n" + inst.mReq.responseText);
          listener.onError(inst.mReq.status);
        }
      }
    }
    catch(e) {
      dump("Error "+e+" "+e.fileName+" line "+e.lineNumber+"\n");
      listener.onError(-1);
    }
  }
  this.mReq.open('GET', url, true);
  this.mReq.send(null); 
}

BlogAPIDetector.prototype.onHomePage = function (listener, rootUrl, inst) {
  // The following code is to clean up dirty html to make it parser
  // friendly

  var req = inst.mReq;          
  var text = req.responseText;
  text = text.replace(/[\r\n]/g,"");
       
  if(text.match(/<title.*>(.+?)</)) {
    inst.mTitle = RegExp.$1;
  }

  var linkObj = function () {
  }

  linkObj.prototype = {
    href:null,
    rel:null,
    type:null,
    title:null,
    QueryInterface: function (iid) {
      if (!iid.equals(Components.interfaces.flockIBlogLink)) 
          throw Components.results.NS_ERROR_NO_INTERFACE;
      return this;
    }
  }

  var linklist = new Array();

  var R = /<link.+?>/g;
  var ar = R.exec(text);
  while(ar) {
    var cur = ar[0] + "";
    var link = new linkObj();
    if(cur.match(/href=\"(.+?)\"/)) {
      link.href = RegExp.$1;
    }
    if(cur.match(/rel=\"(.+?)\"/)) {
      link.rel = RegExp.$1;
    }
    if(cur.match(/title=\"(.+?)\"/)) {
      link.title = RegExp.$1;
    }
    if(cur.match(/type=\"(.+?)\"/)) {
      link.type = RegExp.$1;
    }
    linklist.push(link);
    ar = R.exec(text);
  }

  var accountList = new Array();
  var blogService = Components.classes['@mozilla.org/rdf/datasource;1?name=flock-blog']
        .getService(Components.interfaces['flockIBlogService']);
  var webServices = blogService.services;
  while(webServices.hasMoreElements()) {
    var service = webServices.getNext();
    service.QueryInterface(Components.interfaces['flockIBlogAPI']);
    // Clone the array because our simpleEnumerator destroy it
    var linklistClone = new Array();
    for (i = 0; i < linklist.length; i++)
      linklistClone[i] = linklist[i];
    var account = service.detectAccount(rootUrl, simpleEnumerator(linklistClone));
    if (account) {
      dump(" ==> we found support for "+service.shortName+"\n");
      accountList.push(account);
    }
  }
  // Return an account
  // TODO: use the "preferred" attribute to select the account
  listener.onResult(accountList[0]); 
}

BlogAPIDetector.prototype.detect = function(listener, url) {
  this.doRequest(listener, url, this.onHomePage);
}

function flockBlogService() {
  debug ('CONSTRUCTOR: flockBlogService\n');
  this.mName2ServiceMap = {};
  this.mService2NameMap = {};

  var RDFService = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService().QueryInterface(Components.interfaces.nsIRDFService);
  var dirService = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties);
  var profileDir = dirService.get('ProfD', Components.interfaces.nsILocalFile);
  
  // Prepare the RDF datasource   
  var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
  file.initWithPath(profileDir.path);
  file.append(FLOCK_BLOG_FNAME);
  if(!file.exists()) file.createUnique(0,0600);
  var ds = new RDFDataSource('file://'+file.path);
  var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
  settings.makeSeq();
  this.ds = ds;
  this.dataSource = ds.getRawDataSource().QueryInterface (nsIRDFRemoteDataSource);
  this.dataSource.Flush();

  // Add Technorati.com on first run
  var prefsService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
  var blogBranch = prefsService.getBranch("flock.blog");
  try {
    var firstRun = blogBranch.getIntPref("firstRun");
  } catch(e) {}
  if (!firstRun) {
    this.addNotification("http://www.technorati.com")
    blogBranch.setIntPref("firstRun", 1);
    blogBranch.setBoolPref("notify", true);
  }

  // Search for registered blog web services
  var catmgr = Components.classes["@mozilla.org/categorymanager;1"]
      .getService (Components.interfaces.nsICategoryManager);
  var enum = catmgr.enumerateCategory("flockIBlogAPI");
  while(enum.hasMoreElements()) {
    var obj = enum.getNext();
    supportsString = obj.QueryInterface(Components.interfaces.nsISupportsCString);
    var shortName = supportsString.toString();
    var cid = catmgr.getCategoryEntry("flockIBlogAPI", shortName); 
    var svc = Components.classes[cid].getService(Components.interfaces.flockIBlogAPI);
    this.registerAPI(svc, shortName);
  }
}

// nsISimpleEnumerator implementation
function simpleEnumerator (aArray) {
  aArray.hasMoreElements = function () { 
    return this.length != 0; 
  }
  aArray.getNext = function () { 
    var rval = this.shift (); 
    dump (rval+"\n");
    return rval;
  }
  return aArray;
}

// nsIStringEnumerator implementation
function stringEnumerator (aArray) {
  aArray.hasMore = function () { 
    return this.length != 0; 
  }
  aArray.getNext = function () { 
    var rval = this.shift (); 
    dump (rval+"\n");
    return rval;
  }
  return aArray;
}

loadLibraryFromSpec('chrome://browser/content/flock/contrib/rdfds.js');

function loadLibraryFromSpec(aSpec) {
    var loader = Components.classes['@mozilla.org/moz/jssubscript-loader;1']
        .getService(Components.interfaces.mozIJSSubScriptLoader);

    loader.loadSubScript(aSpec);
}

function BlogAccount(aTitle) {
  this.title = aTitle;
  this.uid = FLOCK_BLOG_RDF_SETTINGS + ":" + aTitle;
  this.url = " ";
  this.username = "";
  this.password = "";
  this.pwhost = "";
  this.api = "";
}

BlogAccount.prototype = {
  getInterfaces: function (count) {
    var interfaceList = [flockIBlogAccount, nsIClassInfo];
    count.value = interfaceList.length;
    return interfaceList;
  }
}

function getPassword(aHost, aUsername) {
  var psm = Components.classes["@mozilla.org/passwordmanager;1"].getService(Components.interfaces.nsIPasswordManager);
  var enum = psm.enumerator;
  while(enum.hasMoreElements()) {
    var pw = enum.getNext();
    pw = pw.QueryInterface(Components.interfaces.nsIPassword);
    if(pw.host==aHost && aUsername==pw.user) {
      return pw.password;
    }
  }
  return null;
}


flockBlogService.prototype.URI = 'rdf:flock-blog';
flockBlogService.prototype.GetSource = function (aProperty, aTarget, aTruthValue) { 
  return this.dataSource.GetSource (aProperty, aTarget, aTruthValue); 
}
flockBlogService.prototype.GetSources = function (aProperty, aTarget, aTruthValue) { 
  return this.dataSource.GetSources (aProperty, aTarget, aTruthValue); 
}
flockBlogService.prototype.GetTarget = function (aSource, aProperty, aTruthValue) { 
  return this.dataSource.GetTarget (aSource, aProperty, aTruthValue); 
}
flockBlogService.prototype.GetTargets = function (aSource, aProperty, aTruthValue) { 
  return this.dataSource.GetTargets (aSource, aProperty, aTruthValue); 
}
flockBlogService.prototype.Assert = function (aSource, aProperty, aTarget, aTruthValue) { 
  this.dataSource.Assert (aSource, aProperty, aTarget, aTruthValue); 
}
flockBlogService.prototype.Unassert = function (aSource, aProperty, aTarget) { 
  this.dataSource.Unassert (aSource, aProperty, aTarget); 
}
flockBlogService.prototype.Change = function (aSource, aProperty, aOldTarget, aNewTarget) { 
  this.dataSource.Change (aSource, aProperty, aOldTarget, aNewTarget); 
}
flockBlogService.prototype.Move = function (aOldSource, aNewSource, aProperty, aTarget) { 
  this.dataSource.Move (aOldSource, aNewSource, aProperty, aTarget); 
}
flockBlogService.prototype.HasAssertion = function (aSource, aProperty, aTarget, aTruthValue) { 
  return this.dataSource.HasAssertion (aSource, aProperty, aTarget, aTruthValue); 
}
flockBlogService.prototype.AddObserver = function (aObserver) { 
  this.dataSource.AddObserver (aObserver); 
}
flockBlogService.prototype.RemoveObserver = function (aObserver) { 
  this.dataSource.RemoveObserver (aObserver); 
}
flockBlogService.prototype.ArcLabelsIn = function (aNode) { 
  return this.dataSource.ArcLabelsIn (aNode); 
}
flockBlogService.prototype.ArcLabelsOut = function (aNode) { 
  return this.dataSource.ArcLabelsOut (aNode); 
}
flockBlogService.prototype.GetAllResources = function () { 
  return this.dataSource.GetAllResources (); 
}
flockBlogService.prototype.IsCommandEnabled = function (aSources, aCommand, aArguments) { 
  return this.dataSource.IsCommandEnabled (aSources, aCommand, aArguments); 
}
flockBlogService.prototype.DoCommand = function (aSources, aCommand, aArguments) { 
  this.dataSource.DoCommand (aSources, aCommand, aArguments); 
}
flockBlogService.prototype.GetAllCmds = function (aSource) { 
  return this.dataSource.GetAllCmds (aSource); 
}
flockBlogService.prototype.hasArcIn = function (aNode, aArc) { 
  return this.dataSource.hasArcIn (aNode, aArc); 
}
flockBlogService.prototype.hasArcOut = function (aNode, aArc) { 
  return this.dataSource.hasArcOut (aNode, aArc); 
}
flockBlogService.prototype.beginUpdateBatch = function () { 
  this.dataSource.beginUpdateBatch (); 
}
flockBlogService.prototype.endUpdateBatch = function () { 
  this.dataSource.endUpdateBatch (); 
}
flockBlogService.prototype.Flush = function () { 
  this.dataSource.Flush (); 
}
flockBlogService.prototype.FlushTo = function (aURI) { 
  this.dataSource.FlushTo (aURI); 
}
flockBlogService.prototype.Init = function (aURI) { 
  this.dataSource.Init (aURI); 
}
flockBlogService.prototype.Refresh = function (aBlocking) { 
  this.dataSource.Refresh (aBlocking); 
}

// Ensure that the "recent" list as no more than BLOG_RECENT_COUNT elements
flockBlogService.prototype.cleanRecent = function () {
  var root = this.ds.getNode(FLOCK_BLOG_RDF_RECENT_ROOT);
  root.makeSeq();
  while (root.getChildCount() > BLOG_RECENT_COUNT) {
    debug(root.getChildCount()+" Let's remove an element.\n");
     root.removeChildAt(1);
  }
  this.ds.save();
}

flockBlogService.prototype.Seq = function (node) {
  // make the resource a sequence if its not and return a container object
  if (!this.isSeq (node)) {
    return RDFCU.MakeSeq (this.dataSource, node);
  }
  var container = Components.classes[CONTAINER_CONTRACTID].createInstance (nsIRDFContainer);
  container.Init (this.dataSource, node);
  return container;
}
flockBlogService.prototype.isSeq = function (node) {
  // is the node a sequence
  return RDFCU.IsSeq (this.dataSource, node);
}

// the flockIBlogService implementation

flockBlogService.prototype.accountExists = function (aTitle) { 
    var root = this.ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    var node = this.ds.getNode(FLOCK_BLOG_RDF_SETTINGS + ":" + aTitle);

    return (root.getChildIndex(node) > -1);
}

flockBlogService.prototype.accountCount = function () {
    var ds = this.ds;
    var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    var accounts = settings.getChildren();
    var count = 0;
    while(accounts.hasMoreElements()) {
        var account = accounts.getNext();
        ++count;
    }
    return count;
}

flockBlogService.prototype.getAccountList = function () {
  debug("GET ACCOUNT LIST\n");
  var ds = this.ds;
  var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
  var accounts = settings.getChildren();
  var result = "";
  while(accounts.hasMoreElements()) {
    var account = accounts.getNext();
    var title = FLOCK_BLOG_RDF_SETTINGS+":"+account.getTarget(FLOCK_NS+"title").getValue();
    if (result != "")
      result = result + "|";
    result = result + title;
  }
  return result;
}

flockBlogService.prototype.addRecent = function (aName, aFile) { 
    var date=new Date();
    var root = this.ds.getNode(FLOCK_BLOG_RDF_RECENT_ROOT);
    root.makeSeq();

    var entry = this.ds.getNode(unescape(aFile));
    entry.addTargetOnce(FLOCK_NS+"filename", aFile+"");
    entry.addTargetOnce(FLOCK_NS+"shortname", aName+"");
    entry.addTargetOnce(FLOCK_NS+"date", date.getTime()+"");

    root.addChild(entry, false);

    this.ds.save();
    this.cleanRecent();
}

flockBlogService.prototype.recentIsEmpty = function () { 
    var root = this.ds.getNode(FLOCK_BLOG_RDF_RECENT_ROOT);
    root.makeSeq();
    try {
        // XXX: why does this fail sometimes?
        return (root.getChildCount() < 1);
    } catch (e) {
        return true;
    }
}

flockBlogService.prototype.registerAPI = function(aService, aShortname) {
  this.mName2ServiceMap[aShortname] = aService;
  this.mService2NameMap[aService] = aShortname;
}

flockBlogService.prototype.getAPIFromShortname = function(aShortname) {
  return this.mName2ServiceMap[aShortname];
}

flockBlogService.prototype.getShortnameFromAPI = function(aService) {
  return this.mService2NameMap[aService];
}

flockBlogService.prototype.__defineGetter__('services', function () { 
  var ar = new Array();
  for(var p in this.mName2ServiceMap) {
    ar.push(this.mName2ServiceMap[p]);
  }
  var rval = {
    getNext: function() {
      var rval = ar.shift();
      return rval;
    },
    hasMoreElements: function() {
      return (ar.length>0);
    },
  }
  return rval;
})


flockBlogService.prototype.getCandidatesAPI = function(aListener, aUrl) {
  var detector = new BlogAPIDetector();
  detector.detect(aListener, aUrl);
}

flockBlogService.prototype.saveAccount = function (aTitle, aBlogAccount) {
  var ds = this.ds;
  var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);

  var bloguri = FLOCK_BLOG_RDF_SETTINGS + ":" + aTitle;

  var profile = ds.getNode(bloguri);
  var api = this.ds.getNode(bloguri + ":" + "api");

  var uri = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURI);
  if (aBlogAccount.url) {
    profile.addTargetOnce(FLOCK_NS+"url", aBlogAccount.url + "");
    uri.spec = aBlogAccount.url;
  }
  else
    uri.spec = profile.getTarget(FLOCK_NS+"url").getValue();
    
  var pwhost = "flockinternal." + uri.host;
  profile.addTargetOnce(FLOCK_NS+"pwhost", pwhost);
  profile.addTargetOnce(FLOCK_NS+"title", aBlogAccount.title + "");
  profile.addTargetOnce(FLOCK_NS+"username", aBlogAccount.username + "");

  if(aBlogAccount.username && aBlogAccount.username.length) {
    var psm = Components.classes["@mozilla.org/passwordmanager;1"].getService(Components.interfaces.nsIPasswordManager);
    psm.addUser(pwhost, aBlogAccount.username, aBlogAccount.password);
  }
  profile.addTargetOnce(FLOCK_NS+"api", api);
  profile.addTargetOnce(FLOCK_NS+"init", false + "");

  if (aBlogAccount.api)
    api.addTargetOnce(FLOCK_NS+"apiname", aBlogAccount.api);
  if (aBlogAccount.apiLink)
    api.addTargetOnce(FLOCK_NS+"apiLink", aBlogAccount.apiLink);
  if (aBlogAccount.editLink)
    api.addTargetOnce(FLOCK_NS+"editLink", aBlogAccount.editLink);
  if (aBlogAccount.blogid)
    api.addTargetOnce(FLOCK_NS+"blogid", aBlogAccount.blogid);

  settings.makeSeq();
  settings.addChild(profile);
  var categoriesNode = ds.getNode(bloguri + ":" + "categories");
  categoriesNode.makeSeq();
  ds.save();
}
 
flockBlogService.prototype.deleteAccount = function (aBlogID) {
    var ds = this.ds;
    var profile = ds.getNode(FLOCK_BLOG_RDF_SETTINGS + ":" + aBlogID);  
    try {
        var pwhost = profile.getTarget(FLOCK_NS+"pwhost").getValue();
        var user = profile.getTarget(FLOCK_NS+"username").getValue();
        var psm = Components.classes["@mozilla.org/passwordmanager;1"].getService(Components.interfaces.nsIPasswordManager);
        psm.removeUser(pwhost, user);
    } catch(e) {
        debug("Blog service: Error removing the login/password.\n"+pwhost+" - "+user+"\n");
    }
    ds.deleteRecursive(profile);
    var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    settings.removeChild(profile);

    var categories = ds.getNode(aBlogID+":categories");
    ds.deleteRecursive(categories);

    ds.save();
}


flockBlogService.prototype.getAccount = function (aBlogID) {
  if(!aBlogID) throw new Error("Error in BlogSettings.getAccount()");

  dump(aBlogID+"\n");
  var rval = new BlogAccount(aBlogID);
  var account = this.ds.getNode(FLOCK_BLOG_RDF_SETTINGS + ":" + aBlogID);
  try {
    rval.title = account.getTarget(FLOCK_NS+"title").getValue();
    rval.username = account.getTarget(FLOCK_NS+"username").getValue();
    rval.pwhost = account.getTarget(FLOCK_NS+"pwhost").getValue();
    rval.password = getPassword(rval.pwhost, rval.username);
    rval.uid = aBlogID;
    rval.url = account.getTarget(FLOCK_NS+"url").getValue();

    var api = account.getTarget(FLOCK_NS+"api");
    rval.api = api.getTarget(FLOCK_NS+"apiname").getValue();
    rval.blogid = api.getTarget(FLOCK_NS+"blogid").getValue();
    rval.apiLink = api.getTarget(FLOCK_NS+"apiLink").getValue();
  } catch(e) {
    debug("Error in getAccount(): "+e+"\n");
  }
  return rval;
};


flockBlogService.prototype.getBlogAPI = function (aBlogID) {
  try {
    var ds = this.ds;
    var profile = ds.getNode(FLOCK_BLOG_RDF_SETTINGS + ":" + aBlogID);
    var u = profile.getTarget(FLOCK_NS+"username").getValue();
    var t = profile.getTarget(FLOCK_NS+"title").getValue();
    var pwhost = profile.getTarget(FLOCK_NS+"pwhost").getValue();
    var p = getPassword(pwhost, u);

    var api = profile.getTarget(FLOCK_NS+"api");
    var apiname = api.getTarget(FLOCK_NS+"apiname").getValue();

    return this.getAPIFromShortname(apiname);
  }
  catch(e) {
    debug("Caught exception: "+e+" "+e.fileName+" "+e.lineNumber+"\n");
    return null;
  }
};

flockBlogService.prototype.setBlogAPI = function (aBlogID, aBlogAPI) {
  var ds = this.ds;
  var api = this.ds.getNode(aBlogID + ":" + "api");
  debug(aBlogID + ":" + "api\n");
  api.addTargetOnce(FLOCK_NS+"apiname", aBlogAPI.name + "");
  api.addTargetOnce(FLOCK_NS+"apiLink", aBlogAPI.apiLink + "");
  api.addTargetOnce(FLOCK_NS+"editLink", aBlogAPI.editLink + "");
  api.addTargetOnce(FLOCK_NS+"blogid", aBlogAPI.blogID + "");
  ds.save();
};

flockBlogService.prototype.addCategory = function (aBlogID, aCategoryId, aCategoryName){
  var ds = this.ds;
  var categoriesNode = ds.getNode(aBlogID + ":" + "categories");
  categoriesNode.makeSeq();
  var categoryUID =  aBlogID + ":categories:" + aCategoryId;
  var categoryNode = ds.getNode(categoryUID);
  categoryNode.addTargetOnce(FLOCK_NS+"categoryName", aCategoryName + "");
  categoryNode.addTargetOnce(FLOCK_NS+"categoryId", aCategoryId + "");
  categoriesNode.addChild(categoryNode);
  ds.save();
};

flockBlogService.prototype.flushCategories = function (aBlogAccount){
  var ds = this.ds;
  var categoriesNode = ds.getNode(aBlogAccount.title + ":" + "categories");
  ds.deleteRecursive (categoriesNode);
  ds.save();
};

flockBlogService.prototype.addNotification = function (aURL){
  var ds = this.ds;
  var root = ds.getNode(NOTIFICATIONS_ROOT);
  root.makeSeq();
  var node = ds.getNode(NOTIFICATIONS_ROOT + ":" + aURL);
  node.addTargetOnce(FLOCK_NS + "url", aURL);
  root.addChild(node);
  ds.save();
};

flockBlogService.prototype.removeNotification = function (aURL){
  var ds = this.ds;
  var root = ds.getNode(NOTIFICATIONS_ROOT);
  var node = ds.getNode(NOTIFICATIONS_ROOT + ":" + aURL);
  ds.deleteRecursive(node);
  root.removeChild(node);
  ds.save();
};


flockBlogService.prototype.getNotifications = function (){
  var ds = this.ds;

  var notifs = new Array ();
  var notificationsRoot = this.Seq (RDFS.GetResource ("urn:flock:blog:preferences:notifications"));
  var elements = notificationsRoot.GetElements ();
  while (elements.hasMoreElements ()) {
    var notification = elements.getNext ().QueryInterface (nsIRDFResource);
    var urlNode = this.dataSource.GetTarget (notification, FLRDF('url'), true);
    if (urlNode instanceof Components.interfaces.nsIRDFLiteral) {
      var url = urlNode.Value;
      notifs.push (url);
    }
  }

  return stringEnumerator(notifs);
};

flockBlogService.prototype.flags = nsIClassInfo.SINGLETON;
flockBlogService.prototype.classDescription = "Flock Blog Service";
flockBlogService.prototype.getInterfaces = function (count) {
    var interfaceList = [flockIBlogService, nsIClassInfo];
    count.value = interfaceList.length;
    return interfaceList;
}
flockBlogService.prototype.getHelperForLanguage = function (count) {return null;}

// the nsISupports implementation
flockBlogService.prototype.QueryInterface =
function (iid) {
    if (!iid.equals(flockIBlogService) && 
        !iid.equals(nsIRDFDataSource) &&
        !iid.equals(nsIClassInfo) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    if (iid.equals(nsIRDFDataSource) && !this.dataSourceSetup) {
    }
    return this;
}

// Module implementation
var BlogModule = new Object();

BlogModule.registerSelf =
function (compMgr, fileSpec, location, type)
{
    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);

    compMgr.registerFactoryLocation(FLOCK_BLOG_CID, 
                                    "Flock Blog JS Component",
                                    FLOCK_BLOG_CONTRACTID, 
                                    fileSpec, 
                                    location,
                                    type);
}

BlogModule.getClassObject =
function (compMgr, cid, iid) {
    if (!cid.equals(FLOCK_BLOG_CID))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    
    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    
    return BlogServiceFactory;
}

BlogModule.canUnload =
function(compMgr)
{
    debug("Unloading blog component.\n");
    return true;
}
    
/* factory object */
var BlogServiceFactory = new Object();

BlogServiceFactory.createInstance =
function (outer, iid) {
    debug("CI: " + iid + "\n");
    if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;

    return (new flockBlogService()).QueryInterface(iid);
}

/* entrypoint */
function NSGetModule(compMgr, fileSpec) {
    return BlogModule;
}

