diff --git a/chrome/content/zotero/xpcom/attachments.js b/chrome/content/zotero/xpcom/attachments.js
index 0b0796cc2..a7ab63212 100644
--- a/chrome/content/zotero/xpcom/attachments.js
+++ b/chrome/content/zotero/xpcom/attachments.js
@@ -200,7 +200,7 @@ Zotero.Attachments = new function(){
 	
 	
 	function importFromURL(url, sourceItemID, forceTitle, forceFileBaseName, parentCollectionIDs,
-			mimeType, libraryID, callback) {
+			mimeType, libraryID, callback, cookieSandbox) {
 		Zotero.debug('Importing attachment from URL');
 		
 		if (sourceItemID && parentCollectionIDs) {
@@ -222,6 +222,7 @@ Zotero.Attachments = new function(){
 		// Save using a hidden browser
 		var nativeHandlerImport = function () {
 			var browser = Zotero.Browser.createHiddenBrowser();
+			if(cookieSandbox) cookieSandbox.attachToBrowser(browser);
 			var imported = false;
 			var onpageshow = function() {
 				// ignore spurious about:blank loads
@@ -263,6 +264,7 @@ Zotero.Attachments = new function(){
 				.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
 				.createInstance(nsIWBP);
 			wbp.persistFlags = nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+			if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(wbp);
 			var encodingFlags = false;
 			
 			Zotero.DB.beginTransaction();
@@ -396,7 +398,7 @@ Zotero.Attachments = new function(){
 		else {
 			Zotero.MIME.getMIMETypeFromURL(url, function (mimeType, hasNativeHandler) {
 				process(mimeType, hasNativeHandler);
-			});
+			}, cookieSandbox);
 		}
 	}
 	
diff --git a/chrome/content/zotero/xpcom/connector/translate_item.js b/chrome/content/zotero/xpcom/connector/translate_item.js
index febac63e2..e24c65caa 100644
--- a/chrome/content/zotero/xpcom/connector/translate_item.js
+++ b/chrome/content/zotero/xpcom/connector/translate_item.js
@@ -1,7 +1,7 @@
 /*
     ***** BEGIN LICENSE BLOCK *****
     
-    Copyright © 2009 Center for History and New Media
+    Copyright © 2012 Center for History and New Media
                      George Mason University, Fairfax, Virginia, USA
                      http://zotero.org
     
@@ -23,10 +23,15 @@
     ***** END LICENSE BLOCK *****
 */
 
-Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) {
+Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType, document,
+		cookieSandbox) {
 	this.newItems = [];
-	
 	this._timeoutID = null;
+	
+	if(document) {
+		this._uri = document.location.toString();
+		this._cookie = document.cookie;
+	}
 }
 
 Zotero.Translate.ItemSaver.ATTACHMENT_MODE_IGNORE = 0;
@@ -40,7 +45,13 @@ Zotero.Translate.ItemSaver.prototype = {
 	"saveItems":function(items, callback) {
 		var me = this;
 		// first try to save items via connector
-		Zotero.Connector.callMethod("saveItems", {"items":items}, function(success, status) {
+		var payload = {"items":items};
+		if(this._uri && this._cookie) {
+			payload.uri = this._uri;
+			payload.cookie = this._cookie;
+		}
+		
+		Zotero.Connector.callMethod("saveItems", payload, function(success, status) {
 			if(success !== false) {
 				Zotero.debug("Translate: Save via Standalone succeeded");
 				callback(true, items);
diff --git a/chrome/content/zotero/xpcom/cookieSandbox.js b/chrome/content/zotero/xpcom/cookieSandbox.js
index 6682ff93c..2f68f1853 100755
--- a/chrome/content/zotero/xpcom/cookieSandbox.js
+++ b/chrome/content/zotero/xpcom/cookieSandbox.js
@@ -27,16 +27,12 @@
  * Manage cookies in a sandboxed fashion
  *
  * @constructor
- * @param {browser} browser Hidden browser object
+ * @param {browser} [browser] Hidden browser object
  * @param {String|nsIURI} uri URI of page to manage cookies for (cookies for domains that are not 
  *                     subdomains of this URI are ignored)
  * @param {String} cookieData Cookies with which to initiate the sandbox
  */
 Zotero.CookieSandbox = function(browser, uri, cookieData) {
-	this._webNav = browser.webNavigation;
-	this._browser = browser;
-	this._watchedBrowsers = [browser];
-	this._watchedXHRs = [];
 	this._observerService = Components.classes["@mozilla.org/observer-service;1"].
 		getService(Components.interfaces.nsIObserverService);
 	
@@ -58,30 +54,13 @@ Zotero.CookieSandbox = function(browser, uri, cookieData) {
 		}
 	}
 	
-	// register with observer
-	Zotero.CookieSandbox.Observer.register(this);
+	if(browser) {
+		this.attachToBrowser(browser);
+	}
+	Zotero.CookieSandbox.Observer.register();
 }
 
 Zotero.CookieSandbox.prototype = {
-	/**
-	 * Check whether we track a browser for this document
-	 */
-	"isDocumentTracked":function(doc) {	
-		var i = this._watchedBrowsers.length;
-		while(i--) {
-			var browser = this._watchedBrowsers[i];
-			if(doc == browser.contentDocument) return true;
-		}
-		return false;
-	},
-	
-	/**
-	 * Check whether we track an XHR for this document
-	 */
-	"isXHRTracked":function(xhr) {
-		return this._watchedXHRs.indexOf(xhr) !== -1;
-	},
-	
 	/**
 	 * Adds cookies to this CookieSandbox based on a cookie header
 	 * @param {String} cookieString;
@@ -108,27 +87,19 @@ Zotero.CookieSandbox.prototype = {
 	},
 	
 	/**
-	 * Attach CookieSandbox to a specific XMLHttpRequest
-	 * @param {XMLHttpRequest} xhr
+	 * Attach CookieSandbox to a specific browser
+	 * @param {Browser} browser
 	 */
 	"attachToBrowser":function(browser) {
-		this._watchedBrowsers.push(browser);
+		Zotero.CookieSandbox.Observer.trackedBrowsers.set(browser, this);
 	},
 	
 	/**
 	 * Attach CookieSandbox to a specific XMLHttpRequest
-	 * @param {XMLHttpRequest} xhr
+	 * @param {nsIInterfaceRequestor} ir
 	 */
-	"attachToXHR": function(xhr) {
-		this._watchedXHRs.push(xhr);
-	},
-	
-	/**
-	 * Destroys this CookieSandbox (intended to be executed when the browser is destroyed)
-	 */
-	"destroy": function() {
-		// unregister with observer
-		Zotero.CookieSandbox.Observer.unregister(this);
+	"attachToInterfaceRequestor": function(ir) {
+		Zotero.CookieSandbox.Observer.trackedInterfaceRequestors.set(ir.QueryInterface(Components.interfaces.nsIInterfaceRequestor), this);
 	}
 }
 
@@ -144,36 +115,21 @@ Zotero.CookieSandbox.Observer = new function() {
 	
 	var observerService = Components.classes["@mozilla.org/observer-service;1"].
 			getService(Components.interfaces.nsIObserverService),
-		observing = false,
-		cookieSandboxes = [];
+		observing = false;
+	
+	this.trackedBrowsers = new WeakMap();
+	this.trackedInterfaceRequestors = new WeakMap();
 	
 	/**
 	 * Registers cookie manager and observer, if necessary
 	 */
 	this.register = function(CookieSandbox) {
-		cookieSandboxes.push(CookieSandbox);
-		
 		if(!observing) {
 			Zotero.debug("CookieSandbox: Registering observers");
 			for each(var topic in observeredTopics) observerService.addObserver(this, topic, false);
 			observing = true;
 		}
-	}
-	
-	/**
-	 * Unregisters cookie manager and observer
-	 */
-	this.unregister = function(CookieSandbox) {
-		// remove cookie manager from list
-		cookieSandboxes.splice(cookieSandboxes.indexOf(CookieSandbox), 1);
-		
-		// remove observer if this is the last and this is not translation-server
-		if(cookieSandboxes.length === 0 && !Zotero.isServer) {
-			Zotero.debug("CookieSandbox: Unregistering observers");
-			for each(var topic in observeredTopics) observerService.removeObserver(this, topic);
-			observing = false;
-		}
-	}
+	};
 	
 	/**
 	 * Implements nsIObserver to watch for new cookies and to add sandboxed cookies
@@ -185,50 +141,48 @@ Zotero.CookieSandbox.Observer = new function() {
 		}
 		
 		channel.QueryInterface(Components.interfaces.nsIHttpChannel);
-		var trackedBy, tested, doc, xhr,
+		var trackedBy, tested, browser, callbacks,
 			channelURI = channel.URI.spec,
 			notificationCallbacks = channel.notificationCallbacks;
 		
-		// try the document
-		try {
-			doc = notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document;
-		} catch(e) {}
-		if(doc) {
+		// try the notification callbacks
+		trackedBy = this.trackedInterfaceRequestors.get(notificationCallbacks);
+		if(trackedBy) {
 			tested = true;
-			for(var i=0, n=cookieSandboxes.length; i<n; i++) {
-				if(cookieSandboxes[i].isDocumentTracked(doc)) {
-					trackedBy = cookieSandboxes[i];
-				}
-			}
 		} else {
-			// try the document for the load group
+			// try the browser
 			try {
-				doc = channel.loadGroup.notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document;
+				browser = notificationCallbacks.getInterface(Ci.nsIWebNavigation)
+					.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
 			} catch(e) {}
-			if(doc) {
+			if(browser) {
 				tested = true;
-				for(var i=0, n=cookieSandboxes.length; i<n; i++) {
-					if(cookieSandboxes[i].isDocumentTracked(doc)) {
-						trackedBy = cookieSandboxes[i];
-					}
-				}
+				trackedBy = this.trackedBrowsers.get(browser);
 			} else {
-				// try getting as an XHR
+				// try the document for the load group
 				try {
-					xhr = notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest);
+					browser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
+						.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
 				} catch(e) {}
-				if(xhr) {
+				if(browser) {
 					tested = true;
-					for(var i=0, n=cookieSandboxes.length; i<n; i++) {
-						if(cookieSandboxes[i].isXHRTracked(xhr)) {
-							trackedBy = cookieSandboxes[i];
-						}
+					trackedBy = this.trackedBrowsers.get(browser);
+				} else {
+					// try getting as an XHR or nsIWBP
+					try {
+						notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest);
+						tested = true;
+					} catch(e) {}
+					if(!tested) {
+						try {
+							notificationCallbacks.QueryInterface(Components.interfaces.nsIWebBrowserPersist);
+							tested = true;
+						} catch(e) {}
 					}
 				}
 			}
 		}
 		
-		// isTracked is now either true, false, or null
 		// trackedBy => we should manage cookies for this request
 		// tested && !trackedBy => we should not manage cookies for this request
 		// !tested && !trackedBy => this request is of a type we couldn't match to this request.
@@ -256,7 +210,6 @@ Zotero.CookieSandbox.Observer = new function() {
 			}
 			
 			// add cookies to be sent to this domain
-			Zotero.debug(trackedBy.cookieString);
 			channel.setRequestHeader("Cookie", trackedBy.cookieString, false);
 			Zotero.debug("CookieSandbox: Added cookies for request to "+channelURI, 5);
 		} else if(topic == "http-on-examine-response") {
@@ -276,7 +229,6 @@ Zotero.CookieSandbox.Observer = new function() {
 			}
 			
 			// put new cookies into our sandbox
-			Zotero.debug(cookieHeader);
 			if(cookieHeader) trackedBy.addCookiesFromHeader(cookieHeader);
 			
 			Zotero.debug("CookieSandbox: Slurped cookies from "+channelURI, 5);
diff --git a/chrome/content/zotero/xpcom/http.js b/chrome/content/zotero/xpcom/http.js
index 42ff2d2b5..c1ae6612d 100644
--- a/chrome/content/zotero/xpcom/http.js
+++ b/chrome/content/zotero/xpcom/http.js
@@ -52,7 +52,7 @@ Zotero.HTTP = new function() {
 			_stateChange(xmlhttp, onDone, responseCharset);
 		};
 		
-		if(cookieSandbox) cookieSandbox.attachToXHR(xmlhttp);
+		if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(xmlhttp);
 		xmlhttp.send(null);
 		
 		return xmlhttp;
@@ -131,7 +131,7 @@ Zotero.HTTP = new function() {
 			_stateChange(xmlhttp, onDone, responseCharset);
 		};
 		
-		if(cookieSandbox) cookieSandbox.attachToXHR(xmlhttp);
+		if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(xmlhttp);
 		xmlhttp.send(body);
 		
 		return xmlhttp;
@@ -143,9 +143,10 @@ Zotero.HTTP = new function() {
 	* @param {String} url URL to request
 	* @param {Function} onDone Callback to be executed upon request completion
 	* @param {Object} requestHeaders HTTP headers to include with request
+	* @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object
 	* @return {Boolean} True if the request was sent, or false if the browser is offline
 	*/
-	this.doHead = function(url, onDone, requestHeaders) {
+	this.doHead = function(url, onDone, requestHeaders, cookieSandbox) {
 		if (url instanceof Components.interfaces.nsIURI) {
 			// Don't display password in console
 			var disp = url.clone();
@@ -190,6 +191,7 @@ Zotero.HTTP = new function() {
 			_stateChange(xmlhttp, onDone);
 		};
 		
+		if(cookieSandbox) cookieSandbox.attachToInterfaceRequestor(xmlhttp);
 		xmlhttp.send(null);
 		
 		return xmlhttp;
diff --git a/chrome/content/zotero/xpcom/mime.js b/chrome/content/zotero/xpcom/mime.js
index f499c377b..87866fe09 100644
--- a/chrome/content/zotero/xpcom/mime.js
+++ b/chrome/content/zotero/xpcom/mime.js
@@ -280,7 +280,7 @@ Zotero.MIME = new function(){
 	}
 	
 	
-	this.getMIMETypeFromURL = function (url, callback) {
+	this.getMIMETypeFromURL = function (url, callback, cookieSandbox) {
 		Zotero.HTTP.doHead(url, function(xmlhttp) {
 			if (xmlhttp.status != 200 && xmlhttp.status != 204) {
 				Zotero.debug("Attachment HEAD request returned with status code "
@@ -308,7 +308,7 @@ Zotero.MIME = new function(){
 			var hasNativeHandler = Zotero.MIME.hasNativeHandler(mimeType, ext)
 			
 			callback(mimeType, hasNativeHandler);
-		});
+		}, undefined, cookieSandbox);
 	}
 	
 	
diff --git a/chrome/content/zotero/xpcom/server_connector.js b/chrome/content/zotero/xpcom/server_connector.js
index 7caa1b617..d259249d0 100755
--- a/chrome/content/zotero/xpcom/server_connector.js
+++ b/chrome/content/zotero/xpcom/server_connector.js
@@ -156,7 +156,6 @@ Zotero.Server.Connector.Detect.prototype = {
 		}
 		this.sendResponse(200, "application/json", JSON.stringify(jsons));
 		
-		this._translate.cookieSandbox.destroy();
 		Zotero.Browser.deleteHiddenBrowser(this._browser);
 	}
 }
@@ -225,7 +224,6 @@ Zotero.Server.Connector.SavePage.prototype = {
 	"_translatorsAvailable":function(translate, translators) {
 		// make sure translatorsAvailable succeded
 		if(!translators.length) {
-			me._translate.cookieSandbox.destroy();
 			Zotero.Browser.deleteHiddenBrowser(this._browser);
 			this.sendResponse(500);
 			return;
@@ -251,7 +249,6 @@ Zotero.Server.Connector.SavePage.prototype = {
 			jsonItems.push(jsonItem);
 		});
 		translate.setHandler("done", function(obj, item) {
-			me._translate.cookieSandbox.destroy();
 			Zotero.Browser.deleteHiddenBrowser(me._browser);
 			if(jsonItems.length || me.selectedItems === false) {
 				me.sendResponse(201, "application/json", JSON.stringify({"items":jsonItems}));
@@ -296,9 +293,12 @@ Zotero.Server.Connector.SaveItem.prototype = {
 			var collection = zp.getSelectedCollection();
 		} catch(e) {}
 		
+		var cookieSandbox = data["uri"] && data["cookie"] ? new Zotero.CookieSandbox(null, data["uri"],
+			data["cookie"]) : null;
+		
 		// save items
 		var itemSaver = new Zotero.Translate.ItemSaver(libraryID,
-			Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD, 1);
+			Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD, 1, undefined, cookieSandbox);
 		itemSaver.saveItems(data.items, function(returnValue, data) {
 			if(returnValue) {
 				try {
@@ -390,8 +390,6 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
 				// remove browser
 				Zotero.Browser.deleteHiddenBrowser(browser);
 				
-				// destroy cookieSandbox
-				cookieSandbox.destroy();
 				sendResponseCallback(201);
 			} catch(e) {
 				sendResponseCallback(500);
diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js
index 8e2ee7993..8716d4f48 100644
--- a/chrome/content/zotero/xpcom/translation/translate.js
+++ b/chrome/content/zotero/xpcom/translation/translate.js
@@ -1433,7 +1433,8 @@ Zotero.Translate.Web.prototype._getParameters = function() { return [this.docume
  */
 Zotero.Translate.Web.prototype._prepareTranslation = function() {
 	this._itemSaver = new Zotero.Translate.ItemSaver(this._libraryID,
-		Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_DOWNLOAD" : "ATTACHMENT_MODE_IGNORE")], 1);
+		Zotero.Translate.ItemSaver[(this._saveAttachments ? "ATTACHMENT_MODE_DOWNLOAD" : "ATTACHMENT_MODE_IGNORE")], 1,
+		this.document, this._cookieSandbox);
 	this.newItems = [];
 }
 
@@ -1548,7 +1549,7 @@ Zotero.Translate.Import.prototype.setString = function(string) {
 Zotero.Translate.Import.prototype.complete = function(returnValue, error) {
 	if(this._io) {
 		this._progress = null;
-		this._io.close();
+		this._io.close(false);
 	}
 	
 	// call super
@@ -1728,7 +1729,7 @@ Zotero.Translate.Export.prototype.setDisplayOptions = function(displayOptions) {
 Zotero.Translate.Export.prototype.complete = function(returnValue, error) {
 	if(this._io) {
 		this._progress = null;
-		this._io.close();
+		this._io.close(true);
 		if(this._io instanceof Zotero.Translate.IO.String) {
 			this.string = this._io.string;
 		}
@@ -2064,7 +2065,13 @@ Zotero.Translate.IO.String.prototype = {
 		}
 	},
 	
-	"close":function() {}
+	"close":function(serialize) {
+		// if we are writing in RDF data mode and no string is set, serialize current RDF to the
+		// string
+		if(serialize && Zotero.Translate.IO.rdfDataModes.indexOf(this._mode) !== -1 && this.string === "") {
+			this.string = this.RDF.serialize();
+		}
+	}
 }
 
 /****** RDF DATA MODE ******/
diff --git a/chrome/content/zotero/xpcom/translation/translate_item.js b/chrome/content/zotero/xpcom/translation/translate_item.js
index 2def6c55d..daf0b5a12 100644
--- a/chrome/content/zotero/xpcom/translation/translate_item.js
+++ b/chrome/content/zotero/xpcom/translation/translate_item.js
@@ -1,7 +1,7 @@
 /*
     ***** BEGIN LICENSE BLOCK *****
     
-    Copyright © 2009 Center for History and New Media
+    Copyright © 2012 Center for History and New Media
                      George Mason University, Fairfax, Virginia, USA
                      http://zotero.org
     
@@ -23,7 +23,8 @@
     ***** END LICENSE BLOCK *****
 */
 
-Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) {
+Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType, document,
+		cookieSandbox) {
 	// initialize constants
 	this.newItems = [];
 	this.newCollections = [];
@@ -65,6 +66,7 @@ Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) {
 	
 	// force tag types if requested
 	this._forceTagType = forceTagType;
+	this._cookieSandbox = cookieSandbox;
 };
 
 Zotero.Translate.ItemSaver.ATTACHMENT_MODE_IGNORE = 0;
@@ -346,7 +348,9 @@ Zotero.Translate.ItemSaver.prototype = {
 
 					var fileBaseName = Zotero.Attachments.getFileBaseNameFromItem(parentID);
 					try {
-						Zotero.Attachments.importFromURL(attachment.url, parentID, title, fileBaseName);
+						Zotero.debug('Importing attachment from URL');
+						Zotero.Attachments.importFromURL(attachment.url, parentID, title,
+							fileBaseName, null, mimeType, this._libraryID, null, this._cookieSandbox);
 					} catch(e) {
 						Zotero.debug("Translate: Error adding attachment "+attachment.url, 2);
 					}
diff --git a/resource/schema/repotime.txt b/resource/schema/repotime.txt
index e54aade7f..1a2ff2586 100644
--- a/resource/schema/repotime.txt
+++ b/resource/schema/repotime.txt
@@ -1 +1 @@
-2012-01-31 04:00:00
+2012-02-10 23:24:58
diff --git a/translators b/translators
index 3daf3bdf2..94bcbbcdc 160000
--- a/translators
+++ b/translators
@@ -1 +1 @@
-Subproject commit 3daf3bdf2ce13d7e1a329f5648f18937d8251dee
+Subproject commit 94bcbbcdc6c4db94391db64dd4b7270e12401e13