Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1

This commit is contained in:
Matt A. Tobin 2019-11-03 00:17:46 -04:00 • committed by Roy Tam
commit e400f4130a
1564 changed files with 510348 additions and 0 deletions

View file

@ -0,0 +1,620 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Cache for all of the feeds currently being downloaded, indexed by URL,
// so the load event listener can access the Feed objects after it finishes
// downloading the feed.
var FeedCache =
{
mFeeds: {},
putFeed: function (aFeed)
{
this.mFeeds[this.normalizeHost(aFeed.url)] = aFeed;
},
getFeed: function (aUrl)
{
let index = this.normalizeHost(aUrl);
if (index in this.mFeeds)
return this.mFeeds[index];
return null;
},
removeFeed: function (aUrl)
{
let index = this.normalizeHost(aUrl);
if (index in this.mFeeds)
delete this.mFeeds[index];
},
normalizeHost: function (aUrl)
{
try
{
let normalizedUrl = Services.io.newURI(aUrl, null, null);
normalizedUrl.host = normalizedUrl.host.toLowerCase();
return normalizedUrl.spec
}
catch (ex)
{
return aUrl;
}
}
};
function Feed(aResource, aRSSServer)
{
this.resource = aResource.QueryInterface(Ci.nsIRDFResource);
this.server = aRSSServer;
}
Feed.prototype =
{
description: null,
author: null,
request: null,
server: null,
downloadCallback: null,
resource: null,
items: new Array(),
itemsStored: 0,
mFolder: null,
mInvalidFeed: false,
mFeedType: null,
mLastModified: null,
get folder()
{
return this.mFolder;
},
set folder (aFolder)
{
this.mFolder = aFolder;
},
get name()
{
// Used for the feed's title in Subcribe dialog and opml export.
let name = this.title || this.description || this.url;
return name.replace(/[\n\r\t]+/g, " ").replace(/[\x00-\x1F]+/g, "");
},
get folderName()
{
if (this.mFolderName)
return this.mFolderName;
// Get a unique sanitized name. Use title or description as a base;
// these are mandatory by spec. Length of 80 is plenty.
let folderName = (this.title || this.description || "").substr(0,80);
let defaultName = FeedUtils.strings.GetStringFromName("ImportFeedsNew");
return this.mFolderName = FeedUtils.getSanitizedFolderName(this.server.rootMsgFolder,
folderName,
defaultName,
true);
},
download: function(aParseItems, aCallback)
{
// May be null.
this.downloadCallback = aCallback;
// Whether or not to parse items when downloading and parsing the feed.
// Defaults to true, but setting to false is useful for obtaining
// just the title of the feed when the user subscribes to it.
this.parseItems = aParseItems == null ? true : aParseItems ? true : false;
// Before we do anything, make sure the url is an http url. This is just
// a sanity check so we don't try opening mailto urls, imap urls, etc. that
// the user may have tried to subscribe to as an rss feed.
if (!FeedUtils.isValidScheme(this.url))
{
// Simulate an invalid feed error.
FeedUtils.log.info("Feed.download: invalid protocol for - " + this.url);
this.onParseError(this);
return;
}
// Before we try to download the feed, make sure we aren't already
// processing the feed by looking up the url in our feed cache.
if (FeedCache.getFeed(this.url))
{
if (this.downloadCallback)
this.downloadCallback.downloaded(this, FeedUtils.kNewsBlogFeedIsBusy);
// Return, the feed is already in use.
return;
}
if (Services.io.offline) {
// If offline and don't want to go online, just add the feed subscription;
// it can be verified later (the folder name will be the url if not adding
// to an existing folder). Only for subscribe actions; passive biff and
// active get new messages are handled prior to getting here.
let win = Services.wm.getMostRecentWindow("mail:3pane");
if (!win.MailOfflineMgr.getNewMail()) {
this.storeNextItem();
return;
}
}
this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Ci.nsIXMLHttpRequest);
// Must set onProgress before calling open.
this.request.onprogress = this.onProgress;
this.request.open("GET", this.url, true);
this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
Ci.nsIRequest.INHIBIT_CACHING;
// Some servers, if sent If-Modified-Since, will send 304 if subsequently
// not sent If-Modified-Since, as in the case of an unsubscribe and new
// subscribe. Send start of century date to force a download; some servers
// will 304 on older dates (such as epoch 1970).
let lastModified = this.lastModified || "Sat, 01 Jan 2000 00:00:00 GMT";
this.request.setRequestHeader("If-Modified-Since", lastModified);
// Only order what you're going to eat...
this.request.responseType = "document";
this.request.overrideMimeType("text/xml");
this.request.setRequestHeader("Accept", FeedUtils.REQUEST_ACCEPT);
this.request.timeout = FeedUtils.REQUEST_TIMEOUT;
this.request.onload = this.onDownloaded;
this.request.onerror = this.onDownloadError;
this.request.ontimeout = this.onDownloadError;
FeedCache.putFeed(this);
this.request.send(null);
},
onDownloaded: function(aEvent)
{
let request = aEvent.target;
let isHttp = request.channel.originalURI.scheme.startsWith("http");
let url = request.channel.originalURI.spec;
if (isHttp && (request.status < 200 || request.status >= 300))
{
Feed.prototype.onDownloadError(aEvent);
return;
}
FeedUtils.log.debug("Feed.onDownloaded: got a download - " + url);
let feed = FeedCache.getFeed(url);
if (!feed)
throw new Error("Feed.onDownloaded: error - couldn't retrieve feed " +
"from cache");
// If the server sends a Last-Modified header, store the property on the
// feed so we can use it when making future requests, to avoid downloading
// and parsing feeds that have not changed. Don't update if merely checking
// the url, as for subscribe move/copy, as a subsequent refresh may get a 304.
// Save the response and persist it only upon successful completion of the
// refresh cycle (i.e. not if the request is cancelled).
let lastModifiedHeader = request.getResponseHeader("Last-Modified");
feed.mLastModified = (lastModifiedHeader && feed.parseItems) ?
lastModifiedHeader : null;
// The download callback is called asynchronously when parse() is done.
feed.parse();
},
onProgress: function(aEvent)
{
let request = aEvent.target;
let url = request.channel.originalURI.spec;
let feed = FeedCache.getFeed(url);
if (feed.downloadCallback)
feed.downloadCallback.onProgress(feed, aEvent.loaded, aEvent.total,
aEvent.lengthComputable);
},
onDownloadError: function(aEvent)
{
let request = aEvent.target;
let url = request.channel.originalURI.spec;
let feed = FeedCache.getFeed(url);
if (feed.downloadCallback)
{
// Generic network or 'not found' error initially.
let error = FeedUtils.kNewsBlogRequestFailure;
if (request.status == 304) {
// If the http status code is 304, the feed has not been modified
// since we last downloaded it and does not need to be parsed.
error = FeedUtils.kNewsBlogNoNewItems;
}
else {
let [errType, errName] = FeedUtils.createTCPErrorFromFailedXHR(request);
FeedUtils.log.info("Feed.onDownloaded: request errType:errName:statusCode - " +
errType + ":" + errName + ":" + request.status);
if (errType == "SecurityCertificate")
// This is the code for nsINSSErrorsService.ERROR_CLASS_BAD_CERT
// overrideable security certificate errors.
error = FeedUtils.kNewsBlogBadCertError;
if (request.status == 401 || request.status == 403)
// Unauthorized or Forbidden.
error = FeedUtils.kNewsBlogNoAuthError;
}
feed.downloadCallback.downloaded(feed, error);
}
FeedCache.removeFeed(url);
},
onParseError: function(aFeed)
{
if (!aFeed)
return;
aFeed.mInvalidFeed = true;
if (aFeed.downloadCallback)
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogInvalidFeed);
FeedCache.removeFeed(aFeed.url);
},
onUrlChange: function(aFeed, aOldUrl)
{
if (!aFeed)
return;
// Simulate a cancel after a url update; next cycle will check the new url.
aFeed.mInvalidFeed = true;
if (aFeed.downloadCallback)
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogCancel);
FeedCache.removeFeed(aOldUrl);
},
get url()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let url = ds.GetTarget(this.resource, FeedUtils.DC_IDENTIFIER, true);
if (url)
url = url.QueryInterface(Ci.nsIRDFLiteral).Value;
else
url = this.resource.ValueUTF8;
return url;
},
get title()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
if (title)
title = title.QueryInterface(Ci.nsIRDFLiteral).Value;
return title;
},
set title (aNewTitle)
{
if (!aNewTitle)
return;
let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewTitle = FeedUtils.rdf.GetLiteral(aNewTitle);
let old_title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
if (old_title)
ds.Change(this.resource, FeedUtils.DC_TITLE, old_title, aNewTitle);
else
ds.Assert(this.resource, FeedUtils.DC_TITLE, aNewTitle, true);
},
get lastModified()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let lastModified = ds.GetTarget(this.resource,
FeedUtils.DC_LASTMODIFIED,
true);
if (lastModified)
lastModified = lastModified.QueryInterface(Ci.nsIRDFLiteral).Value;
return lastModified;
},
set lastModified(aLastModified)
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
aLastModified = FeedUtils.rdf.GetLiteral(aLastModified);
let old_lastmodified = ds.GetTarget(this.resource,
FeedUtils.DC_LASTMODIFIED,
true);
if (old_lastmodified)
ds.Change(this.resource, FeedUtils.DC_LASTMODIFIED,
old_lastmodified, aLastModified);
else
ds.Assert(this.resource, FeedUtils.DC_LASTMODIFIED, aLastModified, true);
},
get quickMode ()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let quickMode = ds.GetTarget(this.resource, FeedUtils.FZ_QUICKMODE, true);
if (quickMode)
{
quickMode = quickMode.QueryInterface(Ci.nsIRDFLiteral);
quickMode = quickMode.Value == "true";
}
return quickMode;
},
set quickMode (aNewQuickMode)
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewQuickMode = FeedUtils.rdf.GetLiteral(aNewQuickMode);
let old_quickMode = ds.GetTarget(this.resource,
FeedUtils.FZ_QUICKMODE,
true);
if (old_quickMode)
ds.Change(this.resource, FeedUtils.FZ_QUICKMODE,
old_quickMode, aNewQuickMode);
else
ds.Assert(this.resource, FeedUtils.FZ_QUICKMODE,
aNewQuickMode, true);
},
get options ()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let options = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
if (options)
return JSON.parse(options.QueryInterface(Ci.nsIRDFLiteral).Value);
return null;
},
set options (aOptions)
{
let newOptions = aOptions ? FeedUtils.newOptions(aOptions) :
FeedUtils._optionsDefault;
let ds = FeedUtils.getSubscriptionsDS(this.server);
newOptions = FeedUtils.rdf.GetLiteral(JSON.stringify(newOptions));
let oldOptions = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
if (oldOptions)
ds.Change(this.resource, FeedUtils.FZ_OPTIONS, oldOptions, newOptions);
else
ds.Assert(this.resource, FeedUtils.FZ_OPTIONS, newOptions, true);
},
categoryPrefs: function ()
{
let categoryPrefsAcct = FeedUtils.getOptionsAcct(this.server).category;
if (!this.options)
return categoryPrefsAcct;
return this.options.category;
},
get link ()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
if (link)
link = link.QueryInterface(Ci.nsIRDFLiteral).Value;
return link;
},
set link (aNewLink)
{
if (!aNewLink)
return;
let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewLink = FeedUtils.rdf.GetLiteral(aNewLink);
let old_link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
if (old_link)
ds.Change(this.resource, FeedUtils.RSS_LINK, old_link, aNewLink);
else
ds.Assert(this.resource, FeedUtils.RSS_LINK, aNewLink, true);
},
parse: function()
{
// Create a feed parser which will parse the feed.
let parser = new FeedParser();
this.itemsToStore = parser.parseFeed(this, this.request.responseXML);
parser = null;
if (this.mInvalidFeed)
{
this.request = null;
this.mInvalidFeed = false;
return;
}
// storeNextItem() will iterate through the parsed items, storing each one.
this.itemsToStoreIndex = 0;
this.itemsStored = 0;
this.storeNextItem();
},
invalidateItems: function ()
{
let ds = FeedUtils.getItemsDS(this.server);
FeedUtils.log.debug("Feed.invalidateItems: for url - " + this.url);
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
let item;
while (items.hasMoreElements())
{
item = items.getNext();
item = item.QueryInterface(Ci.nsIRDFResource);
FeedUtils.log.trace("Feed.invalidateItems: item - " + item.Value);
let valid = ds.GetTarget(item, FeedUtils.FZ_VALID, true);
if (valid)
ds.Unassert(item, FeedUtils.FZ_VALID, valid, true);
}
},
removeInvalidItems: function(aDeleteFeed)
{
let ds = FeedUtils.getItemsDS(this.server);
FeedUtils.log.debug("Feed.removeInvalidItems: for url - " + this.url);
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
let item;
let currentTime = new Date().getTime();
while (items.hasMoreElements())
{
item = items.getNext();
item = item.QueryInterface(Ci.nsIRDFResource);
if (ds.HasAssertion(item, FeedUtils.FZ_VALID,
FeedUtils.RDF_LITERAL_TRUE, true))
continue;
let lastSeenTime = ds.GetTarget(item, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, true);
if (lastSeenTime)
lastSeenTime = parseInt(lastSeenTime.QueryInterface(Ci.nsIRDFLiteral).Value)
else
lastSeenTime = 0;
if ((currentTime - lastSeenTime) < FeedUtils.INVALID_ITEM_PURGE_DELAY &&
!aDeleteFeed)
// Don't immediately purge items in active feeds; do so for deleted feeds.
continue;
FeedUtils.log.trace("Feed.removeInvalidItems: item - " + item.Value);
ds.Unassert(item, FeedUtils.FZ_FEED, this.resource, true);
if (ds.hasArcOut(item, FeedUtils.FZ_FEED))
FeedUtils.log.debug("Feed.removeInvalidItems: " + item.Value +
" is from more than one feed; only the reference to" +
" this feed removed");
else
FeedUtils.removeAssertions(ds, item);
}
},
createFolder: function()
{
if (this.folder)
return;
try {
this.folder = this.server.rootMsgFolder
.QueryInterface(Ci.nsIMsgLocalMailFolder)
.createLocalSubfolder(this.folderName);
}
catch (ex) {
// An error creating.
FeedUtils.log.info("Feed.createFolder: error creating folder - '"+
this.folderName+"' in parent folder "+
this.server.rootMsgFolder.filePath.path + " -- "+ex);
// But its remnants are still there, clean up.
let xfolder = this.server.rootMsgFolder.getChildNamed(this.folderName);
this.server.rootMsgFolder.propagateDelete(xfolder, true, null);
}
},
// Gets the next item from itemsToStore and forces that item to be stored
// to the folder. If more items are left to be stored, fires a timer for
// the next one, otherwise triggers a download done notification to the UI.
storeNextItem: function()
{
if (FeedUtils.CANCEL_REQUESTED)
{
FeedUtils.CANCEL_REQUESTED = false;
this.cleanupParsingState(this, FeedUtils.kNewsBlogCancel);
return;
}
if (!this.itemsToStore || !this.itemsToStore.length)
{
let code = FeedUtils.kNewsBlogSuccess;
this.createFolder();
if (!this.folder)
code = FeedUtils.kNewsBlogFileError;
this.cleanupParsingState(this, code);
return;
}
let item = this.itemsToStore[this.itemsToStoreIndex];
if (item.store())
this.itemsStored++;
if (!this.folder)
{
this.cleanupParsingState(this, FeedUtils.kNewsBlogFileError);
return;
}
this.itemsToStoreIndex++;
// If the listener is tracking progress for each item, report it here.
if (item.feed.downloadCallback && item.feed.downloadCallback.onFeedItemStored)
item.feed.downloadCallback.onFeedItemStored(item.feed,
this.itemsToStoreIndex,
this.itemsToStore.length);
// Eventually we'll report individual progress here.
if (this.itemsToStoreIndex < this.itemsToStore.length)
{
if (!this.storeItemsTimer)
this.storeItemsTimer = Cc["@mozilla.org/timer;1"].
createInstance(Ci.nsITimer);
this.storeItemsTimer.initWithCallback(this, 50, Ci.nsITimer.TYPE_ONE_SHOT);
}
else
{
// We have just finished downloading one or more feed items into the
// destination folder; if the folder is still listed as having new
// messages in it, then we should set the biff state on the folder so the
// right RDF UI changes happen in the folder pane to indicate new mail.
if (item.feed.folder.hasNewMessages)
{
item.feed.folder.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NewMail;
// Run the bayesian spam filter, if enabled.
item.feed.folder.callFilterPlugins(null);
}
this.cleanupParsingState(this, FeedUtils.kNewsBlogSuccess);
}
},
cleanupParsingState: function(aFeed, aCode)
{
// Now that we are done parsing the feed, remove the feed from the cache.
FeedCache.removeFeed(aFeed.url);
if (aFeed.parseItems)
{
// Do this only if we're in parse/store mode.
aFeed.removeInvalidItems(false);
if (aCode == FeedUtils.kNewsBlogSuccess && aFeed.mLastModified)
aFeed.lastModified = aFeed.mLastModified;
// Flush any feed item changes to disk.
let ds = FeedUtils.getItemsDS(aFeed.server);
ds.Flush();
FeedUtils.log.debug("Feed.cleanupParsingState: items stored - " + this.itemsStored);
}
// Force the xml http request to go away. This helps reduce some nasty
// assertions on shut down.
this.request = null;
this.itemsToStore = "";
this.itemsToStoreIndex = 0;
this.itemsStored = 0;
this.storeItemsTimer = null;
if (aFeed.downloadCallback)
aFeed.downloadCallback.downloaded(aFeed, aCode);
},
// nsITimerCallback
notify: function(aTimer)
{
this.storeNextItem();
}
};

View file

@ -0,0 +1,490 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function FeedItem()
{
this.mDate = FeedUtils.getValidRFC5322Date();
this.mUnicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
this.mParserUtils = Cc["@mozilla.org/parserutils;1"].
getService(Ci.nsIParserUtils);
}
FeedItem.prototype =
{
// Only for IETF Atom.
xmlContentBase: null,
id: null,
feed: null,
description: null,
content: null,
enclosures: [],
title: null,
author: "anonymous",
inReplyTo: "",
keywords: [],
mURL: null,
characterSet: "UTF-8",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
ENCLOSURE_HEADER_BOUNDARY_PREFIX: "------------", // 12 dashes
MESSAGE_TEMPLATE: '\n' +
'<html>\n' +
' <head>\n' +
' <title>%TITLE%</title>\n' +
' <base href="%BASE%">\n' +
' </head>\n' +
' <body id="msgFeedSummaryBody" selected="false">\n' +
' %CONTENT%\n' +
' </body>\n' +
'</html>\n',
get url()
{
return this.mURL;
},
set url(aVal)
{
try
{
this.mURL = Services.io.newURI(aVal, null, null).spec;
}
catch(ex)
{
// The url as published or constructed can be a non url. It's used as a
// feeditem identifier in feeditems.rdf, as a messageId, and as an href
// and for the content-base header. Save as is; ensure not null.
this.mURL = aVal ? aVal : "";
}
},
get date()
{
return this.mDate;
},
set date (aVal)
{
this.mDate = aVal;
},
get identity ()
{
return this.feed.name + ": " + this.title + " (" + this.id + ")"
},
normalizeMessageID: function(messageID)
{
// Escape occurrences of message ID meta characters <, >, and @.
messageID.replace(/</g, "%3C");
messageID.replace(/>/g, "%3E");
messageID.replace(/@/g, "%40");
messageID = "<" + messageID.trim() + "@" + "localhost.localdomain" + ">";
FeedUtils.log.trace("FeedItem.normalizeMessageID: messageID - " + messageID);
return messageID;
},
get itemUniqueURI()
{
return this.createURN(this.id);
},
get contentBase()
{
if(this.xmlContentBase)
return this.xmlContentBase
else
return this.mURL;
},
store: function()
{
// this.title and this.content contain HTML.
// this.mUrl and this.contentBase contain plain text.
let stored = false;
let resource = this.findStoredResource();
if (!this.feed.folder)
return stored;
if (resource == null)
{
resource = FeedUtils.rdf.GetResource(this.itemUniqueURI);
if (!this.content)
{
FeedUtils.log.trace("FeedItem.store: " + this.identity +
" no content; storing description or title");
this.content = this.description || this.title;
}
let content = this.MESSAGE_TEMPLATE;
content = content.replace(/%TITLE%/, this.title);
content = content.replace(/%BASE%/, this.htmlEscape(this.contentBase));
content = content.replace(/%CONTENT%/, this.content);
this.content = content;
this.writeToFolder();
this.markStored(resource);
stored = true;
}
this.markValid(resource);
return stored;
},
findStoredResource: function()
{
// Checks to see if the item has already been stored in its feed's
// message folder.
FeedUtils.log.trace("FeedItem.findStoredResource: checking if stored - " +
this.identity);
let server = this.feed.server;
let folder = this.feed.folder;
if (!folder)
{
FeedUtils.log.debug("FeedItem.findStoredResource: folder '" +
this.feed.folderName +
"' doesn't exist; creating as child of " +
server.rootMsgFolder.prettyName + "\n");
this.feed.createFolder();
return null;
}
let ds = FeedUtils.getItemsDS(server);
let itemURI = this.itemUniqueURI;
let itemResource = FeedUtils.rdf.GetResource(itemURI);
let downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
if (!downloaded ||
downloaded.QueryInterface(Ci.nsIRDFLiteral).Value == "false")
{
FeedUtils.log.trace("FeedItem.findStoredResource: not stored");
return null;
}
FeedUtils.log.trace("FeedItem.findStoredResource: already stored");
return itemResource;
},
markValid: function(resource)
{
let ds = FeedUtils.getItemsDS(this.feed.server);
let newTimeStamp = FeedUtils.rdf.GetLiteral(new Date().getTime());
let currentTimeStamp = ds.GetTarget(resource,
FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
true);
if (currentTimeStamp)
ds.Change(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
currentTimeStamp, newTimeStamp);
else
ds.Assert(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
newTimeStamp, true);
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true);
if (ds.hasArcOut(resource, FeedUtils.FZ_VALID))
{
let currentValue = ds.GetTarget(resource, FeedUtils.FZ_VALID, true);
ds.Change(resource, FeedUtils.FZ_VALID,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
}
else
ds.Assert(resource, FeedUtils.FZ_VALID, FeedUtils.RDF_LITERAL_TRUE, true);
},
markStored: function(resource)
{
let ds = FeedUtils.getItemsDS(this.feed.server);
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true);
let currentValue;
if (ds.hasArcOut(resource, FeedUtils.FZ_STORED))
{
currentValue = ds.GetTarget(resource, FeedUtils.FZ_STORED, true);
ds.Change(resource, FeedUtils.FZ_STORED,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
}
else
ds.Assert(resource, FeedUtils.FZ_STORED,
FeedUtils.RDF_LITERAL_TRUE, true);
},
mimeEncodeSubject: function(aSubject, aCharset)
{
// This routine sometimes throws exceptions for mis-encoded data so
// wrap it with a try catch for now.
let newSubject;
try
{
newSubject = mailServices.mimeConverter.encodeMimePartIIStr_UTF8(aSubject,
false,
aCharset, 9, 72);
}
catch (ex)
{
newSubject = aSubject;
}
return newSubject;
},
writeToFolder: function()
{
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" writing to message folder " + this.feed.name);
// Convert the title to UTF-16 before performing our HTML entity
// replacement reg expressions.
let title = this.title;
// The subject may contain HTML entities. Convert these to their unencoded
// state. i.e. &amp; becomes '&'.
title = this.mParserUtils.convertToPlainText(
title,
Ci.nsIDocumentEncoder.OutputSelectionOnly |
Ci.nsIDocumentEncoder.OutputAbsoluteLinks,
0);
// Compress white space in the subject to make it look better. Trim
// leading/trailing spaces to prevent mbox header folding issue at just
// the right subject length.
title = title.replace(/[\t\r\n]+/g, " ").trim();
this.title = this.mimeEncodeSubject(title, this.characterSet);
// If the date looks like it's in W3C-DTF format, convert it into
// an IETF standard date. Otherwise assume it's in IETF format.
if (this.mDate.search(/^\d\d\d\d/) != -1)
this.mDate = new Date(this.mDate).toUTCString();
// If there is an inreplyto value, create the headers.
let inreplytoHdrsStr = this.inReplyTo ?
("References: " + this.inReplyTo + "\n" +
"In-Reply-To: " + this.inReplyTo + "\n") : "";
// If there are keywords (categories), create the headers. In the case of
// a longer than RFC5322 recommended line length, create multiple folded
// lines (easier to parse than multiple Keywords headers).
let keywordsStr = "";
if (this.keywords.length)
{
let HEADER = "Keywords: ";
let MAXLEN = 78;
keywordsStr = HEADER;
let keyword;
let keywords = [].concat(this.keywords);
let lines = [];
while (keywords.length)
{
keyword = keywords.shift();
if (keywordsStr.length + keyword.length > MAXLEN)
{
lines.push(keywordsStr)
keywordsStr = " ".repeat(HEADER.length);
}
keywordsStr += keyword + ",";
}
keywordsStr = keywordsStr.replace(/,$/,"\n");
lines.push(keywordsStr)
keywordsStr = lines.join("\n");
}
// Escape occurrences of "From " at the beginning of lines of
// content per the mbox standard, since "From " denotes a new
// message, and add a line break so we know the last line has one.
this.content = this.content.replace(/([\r\n]+)(>*From )/g, "$1>$2");
this.content += "\n";
// The opening line of the message, mandated by standards to start
// with "From ". It's useful to construct this separately because
// we not only need to write it into the message, we also need to
// use it to calculate the offset of the X-Mozilla-Status lines from
// the front of the message for the statusOffset property of the
// DB header object.
let openingLine = 'From - ' + this.mDate + '\n';
let source =
openingLine +
'X-Mozilla-Status: 0000\n' +
'X-Mozilla-Status2: 00000000\n' +
'X-Mozilla-Keys: ' + " ".repeat(80) + '\n' +
'Received: by localhost; ' + FeedUtils.getValidRFC5322Date() + '\n' +
'Date: ' + this.mDate + '\n' +
'Message-Id: ' + this.normalizeMessageID(this.id) + '\n' +
'From: ' + this.author + '\n' +
'MIME-Version: 1.0\n' +
'Subject: ' + this.title + '\n' +
inreplytoHdrsStr +
keywordsStr +
'Content-Transfer-Encoding: 8bit\n' +
'Content-Base: ' + this.mURL + '\n';
if (this.enclosures.length)
{
let boundaryID = source.length;
source += 'Content-Type: multipart/mixed; boundary="' +
this.ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' +
'This is a multi-part message in MIME format.\n' +
this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' +
'Content-Type: text/html; charset=' + this.characterSet + '\n' +
'Content-Transfer-Encoding: 8bit\n' +
this.content;
this.enclosures.forEach(function(enclosure) {
source += enclosure.convertToAttachment(boundaryID);
});
source += this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '--' + '\n\n\n';
}
else
source += 'Content-Type: text/html; charset=' + this.characterSet + '\n' +
this.content;
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" is " + source.length + " characters long");
// Get the folder and database storing the feed's messages and headers.
let folder = this.feed.folder.QueryInterface(Ci.nsIMsgLocalMailFolder);
let msgFolder = folder.QueryInterface(Ci.nsIMsgFolder);
msgFolder.gettingNewMessages = true;
// Source is a unicode string, we want to save a char * string in
// the original charset. So convert back.
this.mUnicodeConverter.charset = this.characterSet;
let msgDBHdr = folder.addMessage(this.mUnicodeConverter.ConvertFromUnicode(source));
msgDBHdr.OrFlags(Ci.nsMsgMessageFlags.FeedMsg);
msgFolder.gettingNewMessages = false;
this.tagItem(msgDBHdr, this.keywords);
},
/**
* Autotag messages.
*
* @param nsIMsgDBHdr aMsgDBHdr - message to tag
* @param array aKeywords - keywords (tags)
*/
tagItem: function(aMsgDBHdr, aKeywords)
{
let categoryPrefs = this.feed.categoryPrefs();
if (!aKeywords.length || !categoryPrefs.enabled)
return;
let msgArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
msgArray.appendElement(aMsgDBHdr, false);
let prefix = categoryPrefs.prefixEnabled ? categoryPrefs.prefix : "";
let rtl = Services.prefs.getIntPref("bidi.direction") == 2;
let keys = [];
for (let keyword of aKeywords)
{
keyword = rtl ? keyword + prefix : prefix + keyword;
let keyForTag = MailServices.tags.getKeyForTag(keyword);
if (!keyForTag)
{
// Add the tag if it doesn't exist.
MailServices.tags.addTag(keyword, "", FeedUtils.AUTOTAG);
keyForTag = MailServices.tags.getKeyForTag(keyword);
}
// Add the tag key to the keys array.
keys.push(keyForTag);
}
if (keys.length)
// Add the keys to the message.
aMsgDBHdr.folder.addKeywordsToMessages(msgArray, keys.join(" "));
},
htmlEscape: function(s)
{
s = s.replace(/&/g, "&amp;");
s = s.replace(/>/g, "&gt;");
s = s.replace(/</g, "&lt;");
s = s.replace(/'/g, "&#39;");
s = s.replace(/"/g, "&quot;");
return s;
},
createURN: function(aName)
{
// Returns name as a URN in the 'feeditem' namespace. The returned URN is
// (or is intended to be) RFC2141 compliant.
// The builtin encodeURI provides nearly the exact encoding functionality
// required by the RFC. The exceptions are that NULL characters should not
// appear, and that #, /, ?, &, and ~ should be escaped.
// NULL characters are removed before encoding.
let name = aName.replace(/\0/g, "");
let encoded = encodeURI(name);
encoded = encoded.replace(/\#/g, "%23");
encoded = encoded.replace(/\//g, "%2f");
encoded = encoded.replace(/\?/g, "%3f");
encoded = encoded.replace(/\&/g, "%26");
encoded = encoded.replace(/\~/g, "%7e");
return FeedUtils.FZ_ITEM_NS + encoded;
}
};
// A feed enclosure is to RSS what an attachment is for e-mail. We make
// enclosures look like attachments in the UI.
function FeedEnclosure(aURL, aContentType, aLength, aTitle)
{
this.mURL = aURL;
// Store a reasonable mimetype if content-type is not present.
this.mContentType = aContentType || "application/unknown";
this.mLength = aLength;
this.mTitle = aTitle;
// Generate a fileName from the URL.
if (this.mURL)
{
try
{
this.mFileName = Services.io.newURI(this.mURL, null, null).
QueryInterface(Ci.nsIURL).
fileName;
}
catch(ex)
{
this.mFileName = this.mURL;
}
}
}
FeedEnclosure.prototype =
{
mURL: "",
mContentType: "",
mLength: 0,
mFileName: "",
mTitle: "",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
// Returns a string that looks like an e-mail attachment which represents
// the enclosure.
convertToAttachment: function(aBoundaryID)
{
return '\n' +
this.ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '\n' +
'Content-Type: ' + this.mContentType +
'; name="' + (this.mTitle || this.mFileName) +
(this.mLength ? '"; size=' + this.mLength : '"') + '\n' +
'X-Mozilla-External-Attachment-URL: ' + this.mURL + '\n' +
'Content-Disposition: attachment; filename="' + this.mFileName + '"\n\n' +
FeedUtils.strings.GetStringFromName("externalAttachmentMsg") + '\n';
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource:///modules/FeedUtils.jsm");
var gServer, autotagEnable, autotagUsePrefix, autotagPrefix;
function onInit(aPageId, aServerId)
{
var accountName = document.getElementById("server.prettyName");
var title = document.getElementById("am-newsblog-title");
var defaultTitle = title.getAttribute("defaultTitle");
var titleValue;
if (accountName.value)
titleValue = defaultTitle + " - <" + accountName.value + ">";
else
titleValue = defaultTitle;
title.setAttribute("title", titleValue);
document.title = titleValue;
onCheckItem("server.biffMinutes", ["server.doBiff"]);
autotagEnable = document.getElementById("autotagEnable");
autotagUsePrefix = document.getElementById("autotagUsePrefix");
autotagPrefix = document.getElementById("autotagPrefix");
let categoryPrefsAcct = FeedUtils.getOptionsAcct(gServer).category;
autotagEnable.checked = categoryPrefsAcct.enabled;
autotagUsePrefix.disabled = !autotagEnable.checked;
autotagUsePrefix.checked = categoryPrefsAcct.prefixEnabled;
autotagPrefix.disabled = autotagUsePrefix.disabled || !autotagUsePrefix.checked;
autotagPrefix.value = categoryPrefsAcct.prefix;
}
function onPreInit(account, accountValues)
{
gServer = account.incomingServer;
}
function setCategoryPrefs(aNode)
{
let options = FeedUtils.getOptionsAcct(gServer);
switch (aNode.id) {
case "autotagEnable":
options.category.enabled = aNode.checked;
autotagUsePrefix.disabled = !aNode.checked;
autotagPrefix.disabled = !aNode.checked || !autotagUsePrefix.checked;
break;
case "autotagUsePrefix":
options.category.prefixEnabled = aNode.checked;
autotagPrefix.disabled = aNode.disabled || !aNode.checked;
break;
case "autotagPrefix":
options.category.prefix = aNode.value;
break;
}
FeedUtils.setOptionsAcct(gServer, options)
}

View file

@ -0,0 +1,155 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
<?xml-stylesheet href="chrome://messenger-newsblog/skin/feed-subscriptions.css" type="text/css"?>
<!DOCTYPE page [
<!ENTITY % newsblogDTD SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd" >
%newsblogDTD;
<!ENTITY % feedDTD SYSTEM "chrome://messenger-newsblog/locale/feed-subscriptions.dtd" >
%feedDTD;
<!ENTITY % accountNoIdentDTD SYSTEM "chrome://messenger/locale/am-serverwithnoidentities.dtd" >
%accountNoIdentDTD;
<!ENTITY % accountServerTopDTD SYSTEM "chrome://messenger/locale/am-server-top.dtd">
%accountServerTopDTD;
]>
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
class="color-dialog"
title="&accountTitle.label;"
onload="parent.onPanelLoaded('am-newsblog.xul');">
<script type="application/javascript"
src="chrome://messenger/content/AccountManager.js"/>
<script type="application/javascript"
src="chrome://messenger-newsblog/content/am-newsblog.js"/>
<script type="application/javascript"
src="chrome://messenger-newsblog/content/newsblogOverlay.js"/>
<script type="application/javascript"
src="chrome://messenger/content/amUtils.js"/>
<script type="application/javascript"
src="chrome://messenger/content/am-prefs.js"/>
<vbox flex="1" style="overflow: auto;">
<dialogheader id="am-newsblog-title" defaultTitle="&accountTitle.label;"/>
<description class="secDesc">&accountSettingsDesc.label;</description>
<hbox align="center">
<label value="&accountName.label;"
accesskey="&accountName.accesskey;"
control="server.prettyName"/>
<textbox id="server.prettyName"
wsm_persist="true"
size="30"
prefstring="mail.server.%serverkey%.name"/>
</hbox>
<separator class="thin"/>
<groupbox>
<caption label="&serverSettings.label;"/>
<checkbox id="server.loginAtStartUp"
wsm_persist="true"
label="&loginAtStartup.label;"
accesskey="&loginAtStartup.accesskey;"
prefattribute="value"
prefstring="mail.server.%serverkey%.login_at_startup"/>
<hbox align="center">
<checkbox id="server.doBiff"
wsm_persist="true"
label="&biffStart.label;"
accesskey="&biffStart.accesskey;"
oncommand="onCheckItem('server.biffMinutes', [this.id]);"
prefattribute="value"
prefstring="mail.server.%serverkey%.check_new_mail"/>
<textbox id="server.biffMinutes"
wsm_persist="true"
type="number"
size="3"
min="1"
increment="1"
preftype="int"
prefstring="mail.server.%serverkey%.check_time"
aria-labelledby="server.doBiff server.biffMinutes biffEnd"/>
<label id="biffEnd"
value="&biffEnd.label;"
control="server.biffMinutes"/>
</hbox>
<checkbox id="server.quickMode"
wsm_persist="true"
genericattr="true"
label="&useQuickMode.label;"
accesskey="&useQuickMode.accesskey;"
preftype="bool"
prefattribute="value"
prefstring="mail.server.%serverkey%.quickMode"/>
<checkbox id="autotagEnable"
accesskey="&autotagEnable.accesskey;"
label="&autotagEnable.label;"
oncommand="setCategoryPrefs(this)"/>
<hbox>
<checkbox id="autotagUsePrefix"
class="indent"
accesskey="&autotagUsePrefix.accesskey;"
label="&autotagUsePrefix.label;"
oncommand="setCategoryPrefs(this)"/>
<textbox id="autotagPrefix"
placeholder="&autoTagPrefix.placeholder;"
clickSelectsAll="true"
onchange="setCategoryPrefs(this)"/>
</hbox>
</groupbox>
<separator class="thin"/>
<groupbox>
<caption label="&messageStorage.label;"/>
<checkbox id="server.emptyTrashOnExit"
wsm_persist="true"
label="&emptyTrashOnExit.label;"
accesskey="&emptyTrashOnExit.accesskey;"
prefattribute="value"
prefstring="mail.server.%serverkey%.empty_trash_on_exit"/>
<separator class="thin"/>
<vbox>
<label value="&localPath.label;" control="server.localPath"/>
<hbox align="center">
<textbox readonly="true"
wsm_persist="true"
flex="1"
id="server.localPath"
datatype="nsIFile"
prefstring="mail.server.%serverkey%.directory"
class="uri-element"/>
<button id="browseForLocalFolder"
label="&browseFolder.label;"
filepickertitle="&localFolderPicker.label;"
accesskey="&browseFolder.accesskey;"
oncommand="BrowseForLocalFolders();"/>
</hbox>
</vbox>
</groupbox>
<separator class="thin"/>
<hbox align="center">
<spacer flex="1"/>
<button label="&manageSubscriptions.label;"
accesskey="&manageSubscriptions.accesskey;"
oncommand="openSubscriptionsDialog(gServer.rootFolder);"/>
</hbox>
</vbox>
</page>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
<?xml version="1.0"?>
<!-- -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://messenger/skin/folderPane.css" type="text/css"?>
<?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
<?xml-stylesheet href="chrome://messenger-newsblog/skin/feed-subscriptions.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % feedDTD SYSTEM "chrome://messenger-newsblog/locale/feed-subscriptions.dtd">
%feedDTD;
<!ENTITY % certDTD SYSTEM "chrome://pippki/locale/certManager.dtd">
%certDTD;
]>
<window id="subscriptionsDialog"
flex="1"
title="&feedSubscriptions.label;"
windowtype="Mail:News-BlogSubscriptions"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:nc="http://home.netscape.com/NC-rdf#"
persist="width height screenX screenY sizemode"
onload="FeedSubscriptions.onLoad();"
onclose="return FeedSubscriptions.onClose();"
onkeypress="FeedSubscriptions.onKeyPress(event);"
onmousedown="FeedSubscriptions.onMouseDown(event);">
<script type="application/javascript"
src="chrome://messenger/content/specialTabs.js"/>
<script type="application/javascript"
src="chrome://messenger-newsblog/content/feed-subscriptions.js"/>
<keyset id="extensionsKeys">
<key id="key_close"
key="&cmd.close.commandKey;"
modifiers="accel"
oncommand="window.close();"/>
<key id="key_close2"
keycode="VK_ESCAPE"
oncommand="window.close();"/>
</keyset>
<stringbundle id="bundle_newsblog"
src="chrome://messenger-newsblog/locale/newsblog.properties"/>
<stringbundle id="bundle_brand"
src="chrome://branding/locale/brand.properties"/>
<vbox flex="1" id="contentPane">
<hbox align="right">
<label id="learnMore"
class="text-link"
crop="end"
value="&learnMore.label;"
href="https://support.mozilla.org/kb/how-subscribe-news-feeds-and-blogs"/>
</hbox>
<tree id="rssSubscriptionsList"
treelines="true"
flex="1"
hidecolumnpicker="true"
onselect="FeedSubscriptions.onSelect();"
seltype="single">
<treecols>
<treecol id="folderNameCol"
flex="2"
primary="true"
hideheader="true"/>
</treecols>
<treechildren id="subscriptionChildren"
ondragstart="FeedSubscriptions.onDragStart(event);"
ondragover="FeedSubscriptions.onDragOver(event);"/>
</tree>
<hbox id="rssFeedInfoBox">
<vbox flex="1">
<grid flex="1">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row>
<hbox align="right" valign="middle">
<label id="nameLabel"
accesskey="&feedTitle.accesskey;"
control="nameValue"
value="&feedTitle.label;"/>
</hbox>
<textbox id="nameValue"
clickSelectsAll="true"/>
</row>
<row>
<hbox align="right" valign="middle">
<label id="locationLabel"
accesskey="&feedLocation.accesskey;"
control="locationValue"
value="&feedLocation.label;"/>
</hbox>
<hbox>
<textbox id="locationValue"
flex="1"
class="uri-element"
placeholder="&feedLocation.placeholder;"
clickSelectsAll="true"
onfocus="FeedSubscriptions.setSummaryFocus();"
onblur="FeedSubscriptions.setSummaryFocus();"/>
<hbox align="center">
<label id="locationValidate"
collapsed="true"
class="text-link"
crop="end"
value="&locationValidate.label;"
onclick="FeedSubscriptions.checkValidation(event);"/>
</hbox>
</hbox>
</row>
<row>
<hbox align="right" valign="middle">
<label id="feedFolderLabel"
value="&feedFolder.label;"
accesskey="&feedFolder.accesskey;"
control="selectFolder"/>
</hbox>
<hbox>
<menulist id="selectFolder"
flex="1"
class="folderMenuItem"
hidden="true">
<menupopup id="selectFolderPopup"
class="menulist-menupopup"
type="folder"
mode="feeds"
showFileHereLabel="true"
showAccountsFileHere="true"
oncommand="FeedSubscriptions.setNewFolder(event)"/>
</menulist>
<textbox id="selectFolderValue"
flex="1"
readonly="true"
onkeypress="FeedSubscriptions.onClickSelectFolderValue(event)"
onclick="FeedSubscriptions.onClickSelectFolderValue(event)"/>
</hbox>
</row>
</rows>
</grid>
<checkbox id="quickMode"
accesskey="&quickMode.accesskey;"
label="&quickMode.label;"
oncommand="FeedSubscriptions.setSummary(this.checked)"/>
<checkbox id="autotagEnable"
accesskey="&autotagEnable.accesskey;"
label="&autotagEnable.label;"
oncommand="FeedSubscriptions.setCategoryPrefs(this)"/>
<hbox>
<checkbox id="autotagUsePrefix"
class="indent"
accesskey="&autotagUsePrefix.accesskey;"
label="&autotagUsePrefix.label;"
oncommand="FeedSubscriptions.setCategoryPrefs(this)"/>
<textbox id="autotagPrefix"
placeholder="&autoTagPrefix.placeholder;"
clickSelectsAll="true"/>
</hbox>
<separator class="thin"/>
</vbox>
</hbox>
<hbox id="statusContainerBox"
align="center"
valign="middle">
<vbox flex="1">
<description id="statusText"/>
</vbox>
<spacer flex="1"/>
<label id="validationText"
collapsed="true"
class="text-link"
crop="end"
value="&validateText.label;"
onclick="FeedSubscriptions.checkValidation(event);"/>
<button id="addCertException"
collapsed="true"
label="&certmgr.addException.label;"
accesskey="&certmgr.addException.accesskey;"
oncommand="FeedSubscriptions.addCertExceptionDialog();"/>
<progressmeter id="progressMeter"
collapsed="true"
mode="determined"
value="0"/>
</hbox>
<hbox align="end">
<hbox class="actionButtons" flex="1">
<button id="addFeed"
label="&button.addFeed.label;"
accesskey="&button.addFeed.accesskey;"
oncommand="FeedSubscriptions.addFeed();"/>
<button id="editFeed"
disabled="true"
label="&button.updateFeed.label;"
accesskey="&button.updateFeed.accesskey;"
oncommand="FeedSubscriptions.editFeed();"/>
<button id="removeFeed"
disabled="true"
label="&button.removeFeed.label;"
accesskey="&button.removeFeed.accesskey;"
oncommand="FeedSubscriptions.removeFeed(true);"/>
<button id="importOPML"
label="&button.importOPML.label;"
accesskey="&button.importOPML.accesskey;"
oncommand="FeedSubscriptions.importOPML();"/>
<button id="exportOPML"
label="&button.exportOPML.label;"
accesskey="&button.exportOPML.accesskey;"
tooltiptext="&button.exportOPML.tooltip;"
oncommand="FeedSubscriptions.exportOPML(event);"/>
<spacer flex="1"/>
<button id="close"
label="&button.close.label;"
icon="close"
oncommand="if (FeedSubscriptions.onClose()) window.close();"/>
</hbox>
</hbox>
</vbox>
</window>

View file

@ -0,0 +1,45 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource:///modules/FeedUtils.jsm");
/* Feed account standalone wizard functions */
var FeedAccountWizard = {
accountName: "",
accountSetupPageInit: function() {
this.accountSetupPageValidate();
},
accountSetupPageValidate: function() {
this.accountName = document.getElementById("prettyName").value.trim();
document.documentElement.canAdvance = this.accountName;
},
accountSetupPageUnload: function() {
return;
},
donePageInit: function() {
document.getElementById("account.name.text").value = this.accountName;
},
onCancel: function() {
return true;
},
onFinish: function() {
let account = FeedUtils.createRssAccount(this.accountName);
if ("gFolderTreeView" in window.opener.top)
// Opened from 3pane File->New or Appmenu New Message, or
// Account Central link.
window.opener.top.gFolderTreeView.selectFolder(account.incomingServer.rootMsgFolder);
else if ("selectServer" in window.opener)
// Opened from Account Settings.
window.opener.selectServer(account.incomingServer);
window.close();
}
}

View file

@ -0,0 +1,79 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountWizard.css" type="text/css"?>
<!DOCTYPE wizard [
<!ENTITY % accountDTD SYSTEM "chrome://messenger/locale/AccountWizard.dtd">
%accountDTD;
<!ENTITY % newsblogDTD SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd" >
%newsblogDTD;
<!ENTITY % imDTD SYSTEM "chrome://messenger/locale/imAccountWizard.dtd" >
%imDTD;
]>
<wizard id="FeedAccountWizard"
title="&feedWindowTitle.label;"
onwizardcancel="return FeedAccountWizard.onCancel();"
onwizardfinish="return FeedAccountWizard.onFinish();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://messenger-newsblog/content/feedAccountWizard.js"/>
<!-- Account setup page : User gets a choice to enter a name for the account -->
<!-- Defaults : Feed account name -> default string -->
<wizardpage id="accountsetuppage"
pageid="accountsetuppage"
label="&accnameTitle.label;"
onpageshow="return FeedAccountWizard.accountSetupPageInit();"
onpageadvanced="return FeedAccountWizard.accountSetupPageUnload();">
<vbox flex="1">
<description>&accnameDesc.label;</description>
<separator class="thin"/>
<hbox align="center">
<label class="label"
value="&accnameLabel.label;"
accesskey="&accnameLabel.accesskey;"
control="prettyName"/>
<textbox id="prettyName"
flex="1"
value="&feeds.accountName;"
oninput="FeedAccountWizard.accountSetupPageValidate();"/>
</hbox>
</vbox>
</wizardpage>
<!-- Done page : Summarizes information collected to create a feed account -->
<wizardpage id="done"
pageid="done"
label="&accountSummaryTitle.label;"
onpageshow="return FeedAccountWizard.donePageInit();">
<vbox flex="1">
<description>&accountSummaryInfo.label;</description>
<separator class="thin"/>
<grid>
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row id="account.name"
align="center">
<label id="account.name.label"
class="label"
flex="1"
value="&accnameLabel.label;"/>
<label id="account.name.text"
class="label"/>
</row>
</rows>
</grid>
<separator/>
<spacer flex="1"/>
</vbox>
</wizardpage>
</wizard>

View file

@ -0,0 +1,363 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource:///modules/gloda/mimemsg.js");
Components.utils.import("resource:///modules/mailServices.js");
Components.utils.import("resource://gre/modules/Services.jsm");
// This global is for SeaMonkey compatibility.
var gShowFeedSummary;
var FeedMessageHandler = {
gShowSummary: true,
gToggle: false,
kSelectOverrideWebPage: 0,
kSelectOverrideSummary: 1,
kSelectFeedDefault: 2,
kOpenWebPage: 0,
kOpenSummary: 1,
kOpenToggleInMessagePane: 2,
kOpenLoadInBrowser: 3,
/**
* How to load message on threadpane select.
*/
get onSelectPref() {
return Services.prefs.getIntPref("rss.show.summary");
},
set onSelectPref(val) {
Services.prefs.setIntPref("rss.show.summary", val);
ReloadMessage();
},
/**
* Load web page on threadpane select.
*/
get loadWebPageOnSelectPref() {
return Services.prefs.getIntPref("rss.message.loadWebPageOnSelect") ? true : false;
},
/**
* How to load message on open (enter/dbl click in threadpane, contextmenu).
*/
get onOpenPref() {
return Services.prefs.getIntPref("rss.show.content-base");
},
set onOpenPref(val) {
Services.prefs.setIntPref("rss.show.content-base", val);
},
/**
* Determine if a message is a feed message. Prior to Tb15, a message had to
* be in an rss acount type folder. In Tb15 and later, a flag is set on the
* message itself upon initial store; the message can be moved to any folder.
*
* @param nsIMsgDBHdr aMsgHdr - the message.
*
* @return true if message is a feed, false if not.
*/
isFeedMessage: function(aMsgHdr) {
return (aMsgHdr instanceof Components.interfaces.nsIMsgDBHdr) &&
((aMsgHdr.flags & Components.interfaces.nsMsgMessageFlags.FeedMsg) ||
(aMsgHdr.folder && aMsgHdr.folder.server.type == "rss"));
},
/**
* Determine whether to show a feed message summary or load a web page in the
* message pane.
*
* @param nsIMsgDBHdr aMsgHdr - the message.
* @param bool aToggle - true if in toggle mode, false otherwise.
*
* @return true if summary is to be displayed, false if web page.
*/
shouldShowSummary: function(aMsgHdr, aToggle) {
// Not a feed message, always show summary (the message).
if (!this.isFeedMessage(aMsgHdr))
return true;
// Notified of a summary reload when toggling, reset toggle and return.
if (!aToggle && this.gToggle)
return !(this.gToggle = false);
let showSummary = true;
this.gToggle = aToggle;
// Thunderbird 2 rss messages with 'Show article summary' not selected,
// ie message body constructed to show web page in an iframe, can't show
// a summary - notify user.
let browser = getBrowser();
let contentDoc = browser ? browser.contentDocument : null;
let rssIframe = contentDoc ? contentDoc.getElementById("_mailrssiframe") : null;
if (rssIframe) {
if (this.gToggle || this.onSelectPref == this.kSelectOverrideSummary)
this.gToggle = false;
return false;
}
if (aToggle)
// Toggle mode, flip value.
return gShowFeedSummary = this.gShowSummary = !this.gShowSummary;
let wintype = document.documentElement.getAttribute("windowtype");
let tabMail = document.getElementById("tabmail");
let messageTab = tabMail && tabMail.currentTabInfo.mode.type == "message";
let messageWindow = wintype == "mail:messageWindow";
switch (this.onSelectPref) {
case this.kSelectOverrideWebPage:
showSummary = false;
break;
case this.kSelectOverrideSummary:
showSummary = true
break;
case this.kSelectFeedDefault:
// Get quickmode per feed folder pref from feeds.rdf. If the feed
// message is not in a feed account folder (hence the folder is not in
// the feeds database), or FZ_QUICKMODE property is not found (possible
// in pre renovation urls), err on the side of showing the summary.
// For the former, toggle or global override is necessary; for the
// latter, a show summary checkbox toggle in Subscribe dialog will set
// one on the path to bliss.
let folder = aMsgHdr.folder, targetRes;
try {
targetRes = FeedUtils.getParentTargetForChildResource(
folder.URI, FeedUtils.FZ_QUICKMODE, folder.server);
}
catch (ex) {
// Not in a feed account folder or other error.
FeedUtils.log.info("FeedMessageHandler.shouldShowSummary: could not " +
"get summary pref for this folder");
}
showSummary = targetRes && targetRes.QueryInterface(Ci.nsIRDFLiteral).
Value == "false" ? false : true;
break;
}
gShowFeedSummary = this.gShowSummary = showSummary;
if (messageWindow || messageTab) {
// Message opened in either standalone window or tab, due to either
// message open pref (we are here only if the pref is 0 or 1) or
// contextmenu open.
switch (this.onOpenPref) {
case this.kOpenToggleInMessagePane:
// Opened by contextmenu, use the value derived above.
// XXX: allow a toggle via crtl?
break;
case this.kOpenWebPage:
showSummary = false;
break;
case this.kOpenSummary:
showSummary = true;
break;
}
}
// Auto load web page in browser on select, per pref; shouldShowSummary() is
// always called first to 1)test if feed, 2)get summary pref, so do it here.
if (this.loadWebPageOnSelectPref)
setTimeout(FeedMessageHandler.loadWebPage, 20, aMsgHdr, {browser:true});
return showSummary;
},
/**
* Load a web page for feed messages. Use MsgHdrToMimeMessage() to get
* the content-base url from the message headers. We cannot rely on
* currentHeaderData; it has not yet been streamed at our entry point in
* displayMessageChanged(), and in the case of a collapsed message pane it
* is not streamed.
*
* @param nsIMsgDBHdr aMessageHdr - the message.
* @param {obj} aWhere - name value=true pair, where name is in:
* 'messagepane', 'browser', 'tab', 'window'.
*/
loadWebPage: function(aMessageHdr, aWhere) {
MsgHdrToMimeMessage(aMessageHdr, null, function(aMsgHdr, aMimeMsg) {
if (aMimeMsg && aMimeMsg.headers["content-base"] &&
aMimeMsg.headers["content-base"][0]) {
let url = aMimeMsg.headers["content-base"], uri;
try {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
url = converter.ConvertToUnicode(url);
uri = Services.io.newURI(url, null, null);
url = uri.spec;
}
catch (ex) {
FeedUtils.log.info("FeedMessageHandler.loadWebPage: " +
"invalid Content-Base header url - " + url);
return;
}
if (aWhere.browser)
Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Components.interfaces.nsIExternalProtocolService)
.loadURI(uri);
else if (aWhere.messagepane) {
let loadFlag = getBrowser().webNavigation.LOAD_FLAGS_NONE;
getBrowser().webNavigation.loadURI(url, loadFlag, null, null, null);
}
else if (aWhere.tab)
openContentTab(url, "tab", "^");
else if (aWhere.window)
openContentTab(url, "window", "^");
}
else
FeedUtils.log.info("FeedMessageHandler.loadWebPage: could not get " +
"Content-Base header url for this message");
});
},
/**
* Display summary or load web page for feed messages. Caller should already
* know if the message is a feed message.
*
* @param nsIMsgDBHdr aMsgHdr - the message.
* @param bool aShowSummary - true if summary is to be displayed, false if
* web page.
*/
setContent: function(aMsgHdr, aShowSummary) {
if (aShowSummary) {
// Only here if toggling to summary in 3pane.
if (this.gToggle && gDBView && GetNumSelectedMessages() == 1)
ReloadMessage();
}
else {
let browser = getBrowser();
if (browser && browser.contentDocument && browser.contentDocument.body)
browser.contentDocument.body.hidden = true;
// If in a non rss folder, hide possible remote content bar on a web
// page load, as it doesn't apply.
if ("msgNotificationBar" in window)
gMessageNotificationBar.clearMsgNotifications();
this.loadWebPage(aMsgHdr, {messagepane:true});
this.gToggle = false;
}
}
}
function openSubscriptionsDialog(aFolder)
{
// Check for an existing feed subscriptions window and focus it.
let subscriptionsWindow =
Services.wm.getMostRecentWindow("Mail:News-BlogSubscriptions");
if (subscriptionsWindow)
{
if (aFolder)
{
subscriptionsWindow.FeedSubscriptions.selectFolder(aFolder);
subscriptionsWindow.FeedSubscriptions.mView.treeBox.ensureRowIsVisible(
subscriptionsWindow.FeedSubscriptions.mView.selection.currentIndex);
}
subscriptionsWindow.focus();
}
else
{
window.openDialog("chrome://messenger-newsblog/content/feed-subscriptions.xul",
"", "centerscreen,chrome,dialog=no,resizable",
{ folder: aFolder});
}
}
// Special case attempts to reply/forward/edit as new RSS articles. For
// messages stored prior to Tb15, we are here only if the message's folder's
// account server is rss and feed messages moved to other types will have their
// summaries loaded, as viewing web pages only happened in an rss account.
// The user may choose whether to load a summary or web page link by ensuring
// the current feed message is being viewed as either a summary or web page.
function openComposeWindowForRSSArticle(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow)
{
// Ensure right content is handled for web pages in window/tab.
let tabmail = document.getElementById("tabmail");
let is3pane = tabmail && tabmail.selectedTab && tabmail.selectedTab.mode ?
tabmail.selectedTab.mode.type == "folder" : false;
let showingwebpage = ("FeedMessageHandler" in window) && !is3pane &&
FeedMessageHandler.onOpenPref == FeedMessageHandler.kOpenWebPage;
if (gShowFeedSummary && !showingwebpage)
{
// The user is viewing the summary.
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow);
}
else
{
// Set up the compose message and get the feed message's web page link.
let Cc = Components.classes;
let Ci = Components.interfaces;
let msgHdr = aMsgHdr;
let type = aType;
let msgComposeType = Ci.nsIMsgCompType;
let subject = msgHdr.mime2DecodedSubject;
let fwdPrefix = Services.prefs.getCharPref("mail.forward_subject_prefix");
fwdPrefix = fwdPrefix ? fwdPrefix + ": " : "";
let params = Cc["@mozilla.org/messengercompose/composeparams;1"]
.createInstance(Ci.nsIMsgComposeParams);
let composeFields = Cc["@mozilla.org/messengercompose/composefields;1"]
.createInstance(Ci.nsIMsgCompFields);
if (type == msgComposeType.Reply ||
type == msgComposeType.ReplyAll ||
type == msgComposeType.ReplyToSender ||
type == msgComposeType.ReplyToGroup ||
type == msgComposeType.ReplyToSenderAndGroup)
{
subject = "Re: " + subject;
}
else if (type == msgComposeType.ForwardInline ||
type == msgComposeType.ForwardAsAttachment)
{
subject = fwdPrefix + subject;
}
params.composeFields = composeFields;
params.composeFields.subject = subject;
params.composeFields.characterSet = msgHdr.Charset;
params.composeFields.body = "";
params.bodyIsLink = false;
params.identity = aIdentity;
try
{
// The feed's web page url is stored in the Content-Base header.
MsgHdrToMimeMessage(msgHdr, null, function(aMsgHdr, aMimeMsg) {
if (aMimeMsg && aMimeMsg.headers["content-base"] &&
aMimeMsg.headers["content-base"][0])
{
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let url = converter.ConvertToUnicode(aMimeMsg.headers["content-base"]);
params.composeFields.body = url;
params.bodyIsLink = true;
MailServices.compose.OpenComposeWindowWithParams(null, params);
}
else
// No content-base url, use the summary.
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow);
}, false, {saneBodySize: true});
}
catch (ex)
{
// Error getting header, use the summary.
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow);
}
}
}

View file

@ -0,0 +1,16 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
newsblog.jar:
% content messenger-newsblog %content/messenger-newsblog/
content/messenger-newsblog/newsblogOverlay.js (content/newsblogOverlay.js)
content/messenger-newsblog/Feed.js (content/Feed.js)
content/messenger-newsblog/FeedItem.js (content/FeedItem.js)
content/messenger-newsblog/feed-parser.js (content/feed-parser.js)
* content/messenger-newsblog/feed-subscriptions.js (content/feed-subscriptions.js)
content/messenger-newsblog/feed-subscriptions.xul (content/feed-subscriptions.xul)
content/messenger-newsblog/am-newsblog.js (content/am-newsblog.js)
content/messenger-newsblog/am-newsblog.xul (content/am-newsblog.xul)
content/messenger-newsblog/feedAccountWizard.js (content/feedAccountWizard.js)
content/messenger-newsblog/feedAccountWizard.xul (content/feedAccountWizard.xul)

View file

@ -0,0 +1,99 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource:///modules/FeedUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var nsNewsBlogFeedDownloader =
{
downloadFeed: function(aFolder, aUrlListener, aIsBiff, aMsgWindow)
{
FeedUtils.downloadFeed(aFolder, aUrlListener, aIsBiff, aMsgWindow);
},
subscribeToFeed: function(aUrl, aFolder, aMsgWindow)
{
FeedUtils.subscribeToFeed(aUrl, aFolder, aMsgWindow);
},
updateSubscriptionsDS: function(aFolder, aOrigFolder, aAction)
{
FeedUtils.updateSubscriptionsDS(aFolder, aOrigFolder, aAction);
},
QueryInterface: function(aIID)
{
if (aIID.equals(Ci.nsINewsBlogFeedDownloader) ||
aIID.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
var nsNewsBlogAcctMgrExtension =
{
name: "newsblog",
chromePackageName: "messenger-newsblog",
showPanel: function (server)
{
return false;
},
QueryInterface: function(aIID)
{
if (aIID.equals(Ci.nsIMsgAccountManagerExtension) ||
aIID.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
function FeedDownloader() {}
FeedDownloader.prototype =
{
classID: Components.ID("{5c124537-adca-4456-b2b5-641ab687d1f6}"),
_xpcom_factory:
{
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
if (!aIID.equals(Ci.nsINewsBlogFeedDownloader) &&
!aIID.equals(Ci.nsISupports))
throw Cr.NS_ERROR_INVALID_ARG;
// return the singleton
return nsNewsBlogFeedDownloader.QueryInterface(aIID);
}
} // factory
}; // feed downloader
function AcctMgrExtension() {}
AcctMgrExtension.prototype =
{
classID: Components.ID("{E109C05F-D304-4ca5-8C44-6DE1BFAF1F74}"),
_xpcom_factory:
{
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
if (!aIID.equals(Ci.nsIMsgAccountManagerExtension) &&
!aIID.equals(Ci.nsISupports))
throw Cr.NS_ERROR_INVALID_ARG;
// return the singleton
return nsNewsBlogAcctMgrExtension.QueryInterface(aIID);
}
} // factory
}; // account manager extension
var components = [FeedDownloader, AcctMgrExtension];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View file

@ -0,0 +1,5 @@
component {5c124537-adca-4456-b2b5-641ab687d1f6} newsblog.js
contract @mozilla.org/newsblog-feed-downloader;1 {5c124537-adca-4456-b2b5-641ab687d1f6}
component {E109C05F-D304-4ca5-8C44-6DE1BFAF1F74} newsblog.js
contract @mozilla.org/accountmanager/extension;1?name=newsblog {E109C05F-D304-4ca5-8C44-6DE1BFAF1F74}
category mailnews-accountmanager-extensions newsblog @mozilla.org/accountmanager/extension;1?name=newsblog

View file

@ -0,0 +1,18 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_COMPONENTS += [
'js/newsblog.js',
'js/newsblog.manifest',
]
EXTRA_JS_MODULES += [
'content/FeedUtils.jsm',
]
JAR_MANIFESTS += ['jar.mn']
FINAL_TARGET_FILES.isp += [
'rss.rdf',
]

View file

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE RDF SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd">
<RDF:RDF
xmlns:NC="http://home.netscape.com/NC-rdf#"
xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<RDF:Description about="NC:ispinfo">
<NC:providers>
<NC:nsIMsgAccount about="newsblog">
<!-- server info -->
<NC:incomingServer>
<NC:nsIMsgIncomingServer>
<NC:hostName>Feeds</NC:hostName>
<NC:type>rss</NC:type>
<NC:biffMinutes>100</NC:biffMinutes>
<NC:username>nobody</NC:username>
</NC:nsIMsgIncomingServer>
</NC:incomingServer>
<!-- identity defaults -->
<NC:identity>
<NC:nsIMsgIdentity>
</NC:nsIMsgIdentity>
</NC:identity>
<NC:wizardAutoGenerateUniqueHostname>true</NC:wizardAutoGenerateUniqueHostname>
<NC:wizardHideIncoming>true</NC:wizardHideIncoming>
<NC:wizardAccountName>&feeds.accountName;</NC:wizardAccountName>
<NC:wizardSkipPanels>identitypage,incomingpage,outgoingpage</NC:wizardSkipPanels>
<NC:wizardShortName>&feeds.wizardShortName;</NC:wizardShortName>
<NC:wizardLongName>&feeds.wizardLongName;</NC:wizardLongName>
<NC:wizardLongNameAccesskey>&feeds.wizardLongName.accesskey;</NC:wizardLongNameAccesskey>
<NC:wizardShow>true</NC:wizardShow>
<NC:emailProviderName>RSS</NC:emailProviderName>
<NC:showServerDetailsOnWizardSummary>false</NC:showServerDetailsOnWizardSummary>
</NC:nsIMsgAccount>
</NC:providers>
</RDF:Description>
</RDF:RDF>