const Cc = Components.classes;
const Ci = Components.interfaces;

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

const gPrefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService);
const gPrefBranch = gPrefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
const gDefPrefBranch = gPrefService.getDefaultBranch(null);

const gObserver = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
const gConsoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);   
const gBundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService	);
const gPermMgr = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
const gIOService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);	  

const gCCKBundle = gBundleService.createBundle("chrome://cck/content/cck.properties");

function CCKService() {
}

CCKService.prototype = {
	bookmarks: null,
	livemarks: null,
	annoService: null,
	observe: function(aSubject, aTopic, aData) {
		switch(aTopic) {
			case "app-startup":
				gObserver.addObserver(this,"profile-after-change",false);
				gObserver.addObserver(this,"profile-before-change",false);
				gObserver.addObserver(this,"xpcom-shutdown",false);
				gObserver.addObserver(this,"final-ui-startup",false);
				break;
			case "xpcom-shutdown":
				gObserver.removeObserver(this,"profile-after-change");
				gObserver.removeObserver(this,"profile-before-change");
				gObserver.removeObserver(this,"final-ui-startup");
				gObserver.removeObserver(this,"xpcom-shutdown");
				break;
			case "profile-before-change":
				this.uninit();
				break;
			case "profile-after-change":
				this.bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
        this.livemarks = Cc["@mozilla.org/browser/livemark-service;2"].getService(Ci.nsILivemarkService);
        this.annoService = Cc["@mozilla.org/browser/annotation-service;1"].getService(Ci.nsIAnnotationService);
				break;
			case "final-ui-startup":
				this.init();
				break;
		}
	},

	init: function() {
		var i;

		var id = this.getString("id");
		var version = this.getString("version");

		var prefName;
		i=1;
		do {
			prefName = this.getString("LockPref" + i);
			if (prefName) {
				if ((prefName == "browser.startup.homepage") ||
						(prefName == "browser.search.defaultenginename") ||
						(prefName == "browser.search.order.1") ||
						(prefName == "browser.throbber.url")) {
					var url = this.getString(prefName);
					if (url) {
						gDefPrefBranch.setCharPref(prefName, url);
					} else {
						url = gDefPrefBranch.getComplexValue(prefName, Ci.nsIPrefLocalizedString).data;
						gDefPrefBranch.setCharPref(prefName, url);
					}
				}
				gPrefBranch.lockPref(prefName);
			}
			i++;
		} while (prefName);

		/* Windows only */
		if ("@mozilla.org/windows-registry-key;1" in Cc) {
			var RegName, RootKey, Key, Name, NameValue, Type;
			i=1;
			do {
				RegName = this.getString("RegName" + i);
				if (!RegName) {
					break;
				}
				RootKey = this.getString("RootKey" + i);
				Key = this.getString("Key" + i);
				Name = this.getString("Name" + i);
				NameValue = this.getString("NameValue" + i);
				Type = this.getString("Type" + i);
				this.addRegistryKey(RootKey, Key, Name, NameValue, Type);
				i++;
			} while (RegName)
		} 

    var CertName, CertTrust;
		i=1;
    do {
      CertName = this.getString("Cert" + i);
			if (!CertName)
				break;
      CertTrust = this.getString("CertTrust" + i);
			if (!CertTrust)
        CertTrust = "C,C,C";
      this.addCertificate(CertName, CertTrust);
      i++;
    } while (CertName)

    var sites;
    sites = this.getString("PopupAllowedSites");
		if (sites)
      this.updatePermissions(sites, "popup", 1);
    sites = this.getString("InstallAllowedSites");
		if (sites)
      this.updatePermissions(sites, "install", 1);
    sites = this.getString("CookieAllowedSites");
		if (sites)
      this.updatePermissions(sites, "cookie", 1);
    sites = this.getString("PopupDeniedSites");
		if (sites)
      this.updatePermissions(sites, "popup", 2);
    sites = this.getString("InstallDeniedSites");
		if (sites)
      this.updatePermissions(sites, "install", 2);
    sites = this.getString("CookieDeniedSites");
		if (sites)
      this.updatePermissions(sites, "cookie", 2);

    var mybookmarks = this.annoService.getItemsWithAnnotation(id + "/" + version, {});
    /* Don't recreate any bookmarks if some are already there. Note that */
    /* if the user delete them all, we recreate */
    if (mybookmarks.length > 0) {
      return;
    }

		var ToolbarLocation;
		ToolbarLocation = this.getString("ToolbarLocation");
		if ((ToolbarLocation) && (ToolbarLocation == "First")) {
			this.addBookmarks("Toolbar", this.bookmarks.toolbarFolder, 1, id + "/" + version);
			this.addFolder("Toolbar", this.bookmarks.toolbarFolder, 1, id + "/" + version);
		} else {
			this.addFolder("Toolbar", this.bookmarks.toolbarFolder, -1, id + "/" + version);
			this.addBookmarks("Toolbar", this.bookmarks.toolbarFolder, -1, id + "/" + version);
		}

		var BookmarkLocation;
		BookmarkLocation = this.getString("BookmarkLocation");
		if ((BookmarkLocation) && (BookmarkLocation == "First")) {
			this.addBookmarks("", this.bookmarks.bookmarksMenuFolder, 1, id + "/" + version);
			this.addFolder("Bookmark", this.bookmarks.bookmarksMenuFolder, 1, id + "/" + version);
		} else {
			this.addFolder("Bookmark", this.bookmarks.bookmarksMenuFolder, -1, id + "/" + version);
			this.addBookmarks("", this.bookmarks.bookmarksMenuFolder, -1, id + "/" + version);
		}
	},
	/* This function cleans up SOME CCK stuff if we are disabled or uninstalled */
	uninit: function() {
		var id = this.getString("id");
		var version = this.getString("version");

		var rdfs = Components.classes["@mozilla.org/rdf/rdf-service;1"]
												 .getService(Components.interfaces.nsIRDFService);
		var extensionDS= Components.classes["@mozilla.org/extensions/manager;1"]
															 .getService(Components.interfaces.nsIExtensionManager).datasource;
		var extension = rdfs.GetResource("urn:mozilla:item:" + id);  

		var opTypeArc = rdfs.GetResource("http://www.mozilla.org/2004/em-rdf#opType");

		var opType = extensionDS.GetTarget(extension, opTypeArc, true);
		if (opType) {
			opType = opType.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
			if (opType && ((opType == "needs-uninstall") || (opType == "needs-disable") || (opType == "needs-upgrade"))) {
				var i = 1;
				var prefName;
				do {
					prefName = this.getString("LockPref" + i);
					if (prefName && prefName.length) {
						gPrefBranch.unlockPref(prefName);
					}
					i++;
				} while (prefName && prefName.length);
				var mybookmarks = this.annoService.getItemsWithAnnotation(id + "/" + version, {});
				for (var i = 0; i < mybookmarks.length; i++) {
					try {
					  this.bookmarks.removeItem(mybookmarks[i]);
					} catch (ex) {
						/* This could fail if we removed the folder before the boomark */
					}
				}
			}
		}
	},
	addRegistryKey: function(RootKey, Key, Name, NameValue, Type) {
    const nsIWindowsRegKey = Ci.nsIWindowsRegKey;
		try {
			var key = Cc["@mozilla.org/windows-registry-key;1"]
									.createInstance(nsIWindowsRegKey);
			var rootKey;
			switch (RootKey) {
				case "HKEY_CLASSES_ROOT":
					rootKey = nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT;
					break;
				case "HKEY_CURRENT_USER":
					rootKey = nsIWindowsRegKey.ROOT_KEY_CURRENT_USER;
					break;
				default:
					rootKey = nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE;
					break;
			}

			key.create(rootKey, Key, nsIWindowsRegKey.ACCESS_WRITE);

			switch (Type) {
				case "REG_DWORD":
					key.writeIntValue(Name, NameValue);
					break;
				case "REG_QWORD":
					key.writeInt64Value(Name, NameValue);
					break;
				case "REG_BINARY":
					key.writeBinaryValue(Name, NameValue);
					break;
				case "REG_SZ":
				default:
					key.writeStringValue(Name, NameValue);
					break;
			}
			key.close();
		} catch (ex) {
			/* This could fail if you don't have the right authority on Windows */
		}
	},
  addCertificate: function(CertName, CertTrust) {
    var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB2);
		var scriptableStream=Cc["@mozilla.org/scriptableinputstream;1"].getService(Ci.nsIScriptableInputStream);
		var channel = gIOService.newChannel("chrome://cck/content/" + CertName, null, null);
		var input=channel.open();
		scriptableStream.init(input);
		var certfile=scriptableStream.read(input.available());
		scriptableStream.close();
		input.close();
		
		var beginCert = "-----BEGIN CERTIFICATE-----";
		var endCert = "-----END CERTIFICATE-----";

		certfile = certfile.replace(/[\r\n]/g, "");
		var begin = certfile.indexOf(beginCert);
		var end = certfile.indexOf(endCert);
		var cert = certfile.substring(begin + beginCert.length, end);
		certDB.addCertFromBase64(cert, CertTrust, "");
	},
	updatePermissions: function(sites, type, permission) {
		var sitesArray = sites.split(",");
		for (var i=0; i < sitesArray.length; i++) {
			try {
				var uri = gIOService.newURI("http://" + sitesArray[i], null, null);
				gPermMgr.add(uri, type, permission);
			} catch (ex) {}
		}		
	},

  addBookmarks:	function(prefix, container, location, inId) {
		var BookmarkTitle;
		var BookmarkURL;
	
		// items are added in reverse order if we are set to "First"
		var start, end, increment;
		
		var i = 1;
		var numBookmarks = 0;
		var curtem;
		while (1) {
			var title = this.getString(prefix + "BookmarkTitle" + i);
			if (!title)
				break;
			i++;
			numBookmarks++;
		}
		
		if (location == -1) {
			start = 1;
			end = numBookmarks+1;
			increment = 1;
		} else {
			start = numBookmarks;
			end = 0;
			increment = -1;
		}
	
		for (var i=start; i!=end; i+=increment) {
			BookmarkTitle = this.getString(prefix + "BookmarkTitle" + i);
			if (BookmarkTitle) {
				BookmarkURL = this.getString(prefix + "BookmarkURL" + i);
				var bmtype = this.getString(prefix + "BookmarkType" + i);
				var curitem;
				if (bmtype == "separator") {
					curitem = this.bookmarks.insertSeparator(container, location);
				} else if (BookmarkURL) {
					if (bmtype == "live") {
						curitem = this.livemarks.createLivemark(container, BookmarkTitle, null, this.makeURI(BookmarkURL), location);
					}else {
						curitem = this.bookmarks.insertBookmark(container, this.makeURI(BookmarkURL), location, BookmarkTitle);
					}
				}
				if (curitem) {
					this.annoService.setItemAnnotation(curitem, inId, "true", 0, this.annoService.EXPIRE_NEVER);
				}
			}
		}
	},

	addFolder: function(prefix, container, location, inId) {
		var BookmarkFolder;
		
		// items are added in reverse order if we are set to "First"
		var start, end, increment;
		
		if (location == -1) {
			start = 1;
			end = 6;
			increment = 1;
		} else {
			start = 5;
			end = 0;
			increment = -1;
		}
				
		// Bookmarks folder with bookmarks
		for (var i=start; i!=end; i+=increment) {
			BookmarkFolder = this.getString(prefix + "Folder" + i);
			if (BookmarkFolder) {
				var newfolder = this.bookmarks.createFolder(container, BookmarkFolder, location);
				this.annoService.setItemAnnotation(newfolder, inId, "true", 0, this.annoService.EXPIRE_NEVER);
				this.addBookmarks(prefix + "Folder" + i + ".", newfolder, 0, inId);
			}
		}
	},
	getString: function(id) {
		try {
			var string = gCCKBundle.GetStringFromName(id);
			if (string.length) {
				return string;
			}
		} catch (ex) {}
		return undefined;
	},
  makeURI: function(aURL, aOriginCharset, aBaseURI) {
		try {
		  var uri = gIOService.newURI(aURL, aOriginCharset, aBaseURI);
			return uri;
		} catch (ex) {
			this.log(ex);
			this.log(aURL);
		}
		return null;
	},
	log: function(string) {
		gConsoleService.logStringMessage(string);
	},

	classDescription: "CCK Service - %OrganizationName%",
	contractID: "@mozilla.org/cck-service-%OrganizationName%;2",
	classID: Components.ID("%uuid%"),
	QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
	_xpcom_categories: [{
		category: "app-startup",
		service: true
	}]
}

function NSGetModule(compMgr, fileSpec) {
	return XPCOMUtils.generateModule([CCKService]);
}
