Continuing my series on Mission Control Desktop, this post covers some enhancements I made for configuring email accounts in Thunderbird. The mail.*
branch is one of the largest group of preferences you can manipulate using autoconfig. Setting up the right combination of mail.account
, mail.server
, mail.identity
and mail.smtpserver
is just a little tricky.
With the exception of the special “local folders” account, an email account in Thunderbird has four components: an account, a server, one or more identities, and an smtp server. There are a few preference strings gluing each of these elements together. Each account you create is differentiated from the others by a unique label, defaulting to account<num>, with incrementing numbers. You can also use your own, more meaningful labels, such as mail.account.work-email
and mail.account.gmail
.
Element | Branch | |
---|---|---|
Account | mail.account.accountN |
Glues POP/IMAP, SMTP servers and identities together |
Server | mail.server.serverN |
Settings for the IMAP or POP server |
Identity | mail.identity.idN |
Name, email address, drafts & stationery folders |
SMTP Server | mail.smtpserver.smtpN |
Settings for the SMTP server |
Glue settings | ||
Accounts list | mail.accountmanager.accounts |
Comma-separated list of mail.account labels |
Default account | mail.accountmanager.defaultaccount |
The label of the default mail account |
SMTP servers | mail.smtpservers |
Comma-separated list of mail.smtpserver server labels |
Default SMTP | mail.smtp.defaultserver |
The label of the default SMTP server |
Here is a quick example:
// Identity defaultPref("mail.identity.id1.fullName", "Dean Brundage"); defaultPref("mail.identity.id1.draft_folder", "imap://dean.brundage@example.com@mail.example.com/Drafts"); defaultPref("mail.identity.id1.smtpServer", "smtp1"); // IMAP server settings defaultPref("mail.server.server1.type", "imap"); defaultPref("mail.server.server1.hostname", "mail.example.com"); // etc // SMTP server settings defaultPref("mail.smtpserver.smtp1.auth_method", 1); defaultPref("mail.smtpserver.smtp1.hostname", "smtp.example.com"); // etc // Glue it all together defaultPref("mail.account.account1.identities", "id1"); defaultPref("mail.account.account1.server", "server1"); defaultPref("mail.accountmanager.accounts", "account1"); defaultPref("mail.accountmanager.defaultaccount", "account1"); defaultPref("mail.smtp.defaultserver", "smtp1"); defaultPref("mail.smtpservers", "smtp1"); |
As you add more email accounts the code can get unmanageable when you try to remember to twiddle the right branches so all the accounts, their identities and smtp servers show up. I would like to present an alternative.
This code builds upon the PreferenceFactory
prototype covered earlier. I put together an object prototype for an account, containing a server, one or more identities, and an smtp server. There is also a singleton for the account manager. Now you can instantiate an object and call preference setters on it. The code is more readable and less error prone.
// Create an email account var workAccount = AccountManager.newAccount({ isDefault: true, label: "work", type: "imap" }); // Lock Preferences on the IMAP server workAccount.server.setPrefs({ hostname: "mail.example.com", type: "imap" }, "lock" ); // Default Preferences workAccount.server.setPrefs({ check_new_mail: true, name: "Corporate eMail" }); // Lock Preferences on the SMTP server workAccount.smtpServer.setPrefs({ auth_method: 1, /* User/pass */ username: "dean.brundage@example.com" }, "lock" ); // Default Preferences workAccount.smtpServer.setPrefs({ description: "Corporate SMTP server", hostname: "mail.example.com"}); // And now a personal account var myGmailAccount = AccountManager.newAccount({ label: "gmail", type: "imap" }); // Go about setting preferences on myGmailAccount as before |
Download the source
That block of code replaces the tedious series of defaultPref
and lockPref
required to set up an email account. It produces preference settings like these:
defaultPref("mail.accountmanager.accounts", "work-account1,gmail-account2"); defaultPref("mail.accountmanager.defaultaccount", "work-account1"); defaultPref("mail.account.work-account1.identities", "work-id1"); defaultPref("mail.account.work-account1.server", "work-server1"); lockPref("mail.server.work-server1.type", "imap"); defaultPref("mail.server.gmail-server2.type", "imap"); // etc defaultPref("mail.smtp.defaultserver", "work-smtp1"); defaultPref("mail.smtpservers", "work-smtp1"); |
Download the source
/** By Dean Brundage Originally published here: http://blog.deanandadie.net/2010/06/easy-thunderbird-account-management-using-mcd/ */ /* "Inheritance" helper http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/ copy all of parent's prototype functions to descendant */ function copyPrototype(parent, descendant) { var sConstructor = parent.toString(); var aMatch = sConstructor.match( /\s*function (.*)\(/ ); if( aMatch != null ) { descendant.prototype[aMatch[1]] = parent; } for (var m in parent.prototype) { descendant.prototype[m] = parent.prototype[m]; } }; /* "Base" for the Mail.xxxx objects This requires the PreferenceFactory prototype covered in a previous post: http://blog.deanandadie.net/2010/05/manufacturing-user-preferences-for-mcd/ */ function Mail() { this.PreferenceFactory(); // super this.addPrefBranch("mail"); return this; } copyPrototype( PreferenceFactory, Mail ); Mail.prototype.joinIds = function(collection,separator) { if( ! separator ) separator = ","; if( collection ) { ret = collection[0].id for( i = 1; i < collection.length; i++ ) ret = ret + separator + collection[i].id; } return ret; }; /* A thunderbird email "account" consists of Mail.Server -- A mail server (IMAP or POP) Mail.SMTPServer -- A SMTP server One or more Mail.Identities -- Email address and name A thunderbird local folders "account" has only one Mail.Server, nothing else Yup, local folders are "servers" */ /* Object to manage accounts I recommend managing mail accounts with the AccountManager unless you know what you are doing. var myAccount = AccountManager.newAccount(); myAccount.doStuff(); */ var AccountManager = new Mail(); AccountManager.accounts = []; AccountManager.addPrefBranch("accountmanager"); /* Create a new account Alerts Thunderbird to the presence of the new account See Mail.Account for valid options */ AccountManager.newAccount = function(opts) { if( ! opts ) opts = { }; acct = new Mail.Account(opts); if( opts.isDefault ) { acct.useSMTPServer( new Mail.SMTPServer(opts) ); this.setDefaultAccount(acct,opts.lockLevel); } else { acct.useSMTPServer( Mail.SMTPServer.defaultServer ); } this.accounts.push(acct); this.setPref( "accounts", this.joinIds(this.accounts) ); return acct; } AccountManager.setDefaultAccount = function(account,lockLevel) { this.setPref("defaultaccount", account.id, lockLevel ); /* It's possible to create an account before setting the default SMTP server. Clean them up if this new account is the default (Expect Mail.Account to inform Mail.SMTPServer of the new default) */ for( i = 0; i < this.accounts.length; i++ ) this.accounts[i].useSMTPServer(Mail.SMTPServer.defaultServer); } AccountManager.setLocalFolders = function(folders,lockLevel) { this.setPref("localfoldersserver", folders.id, lockLevel); } /* Creates a new generic mail account (identity, server, smtp) Arguments opts: A hash of options. Valid options are: "isDefault": true | false (default false) "label": string - A unique label for this account, server & identity "lockLevel": "default" | "lock" | "pref" (default "default") "type": imap | pop | localFolder This prototype creates an object representing an email account It exposes some objects: myAccount.directoryServer // The (optional) LDAP2Server object myAccount.identities // An array of identites myAccount.server // The IMAP/POP/LocalFolder Mail.Server object myAccount.smtpServer // The Mail.SMTPServer object (The LDAP2Server object is detailed in a separate post on http://blog.deanandadie.net/) */ Mail.Account = function(opts) { if( ! opts ) opts = { }; if( ! opts.type ) throw("What's my type?"); this.Mail(); // "super" this.id = "account" + ++Mail.Account.count; if( opts.label ) { this.id = opts.label + "-" + this.id; } // Our preference branch is mail.account.accountN this.addPrefBranch( [ "account", this.id ] ); this.identities = []; this.server = new Mail.Server(opts); // Need this either way if( opts.type.match(/^^imap$|^pop$/i) ) { this.server.setPref("type", opts.type, opts.lockLevel); // Important that addIdentity be before the SMTP server is set up this.addIdentity( new Mail.Identity(opts), opts.lockLevel ); } else if( opts.type.match(/^localFolders?$/i) ) { this.folders = this.server; AccountManager.setLocalFolders(this.folders); this.folders.setPref("type", "none", opts.lockLevel ); } else { throw("unrecognized Mail.Account type: " + opts.type); } this.useServer(this.server, opts.lockLevel); } copyPrototype( Mail, Mail.Account ); Mail.Account.count = 0; // Fake class variable // Expects a Mail.Identity object and, optionally, the preference locking level Mail.Account.prototype.addIdentity = function(identity,lockLevel) { if( identity ) { if( ! this.identities.contains(identity) ) { this.identities.push(identity); this.setPref("identities", this.joinIds(this.identities), lockLevel ); } } } /* Helper to set the SMTP server Expects a Mail.SMTPServer object & optionally, an options hash Valid options: lockLevel: lock | default | pref force: true | false */ Mail.Account.prototype.useDirectory = function(directory, opts) { if( ! opts ) opts = { }; if( directory ) { if( opts.force || typeof(this.directoryServer) == "undefined" ) { this.directoryServer = directory; for( i = 0; i < this.identities.length; i++ ) this.identities[i].useDirectory( this.directoryServer, opts ); } } } /* Helper to set the SMTP server Expects a Mail.SMTPServer object & optionally, an options hash Valid options: lockLevel: lock | default | pref force: true | false */ Mail.Account.prototype.useSMTPServer = function(smtp,opts) { if( ! opts ) opts = { }; if( smtp ) { if( opts.force || typeof(this.smtpServer) == "undefined" ) { this.smtpServer = smtp; for( i = 0; i < this.identities.length; i++ ) this.identities[i].useSMTPServer( this.smtpServer, opts ); } } } // Expects a Mail.Server object and, optionally, the preference locking level Mail.Account.prototype.useServer = function(server,lockLevel) { if( server ) this.setPref("server", server.id, lockLevel ); } /* Creates a new generic mail server Arguments opts: A hash of options. Valid options are: label: string - A unique label for this account, server & identity */ Mail.Server = function(opts) { if( ! opts ) opts = { }; this.Mail(); this.id = "server" + ++Mail.Server.count; if( opts.label ) { this.id = opts.label + "-" + this.id; } // Our preference branch is mail.server.serverN this.addPrefBranch( [ "server", this.id ] ); } copyPrototype( Mail, Mail.Server ); Mail.Server.count = 0; // Fake class variable /* Creates a new mail identity. (name, email address, etc) Arguments opts: A hash of options. Valid options are: "label": string - A unique label for this account, server & identity */ Mail.Identity = function(opts) { if( ! opts ) opts = { }; this.Mail(); this.id = "id" + ++Mail.Identity.count; if( opts.label ) { this.id = opts.label + "-" + this.id; } // Our preference branch is mail.identity.idN this.addPrefBranch( [ "identity", this.id ] ); this.hasSMTPServer = false; }; copyPrototype(Mail, Mail.Identity); Mail.Identity.count = 0; // Fake class variable /* Expects a LDAP2Server object and a hash of options Valid options: lockLevel: lock | default | pref */ Mail.Identity.prototype.useDirectory = function(ldap2Server,opts) { if( ! opts ) opts = { }; if( ldap2Server ) { this.setPref("directoryServer", "ldap_2.servers." + ldap2Server.id, opts.lockLevel); this.setPref("overrideGlobal_Pref", true, opts.lockLevel); } }; /* Expects a SMTPServer object and a hash of options Valid options: force: true | false lockLevel: lock | default | pref */ Mail.Identity.prototype.useSMTPServer = function(smtpServer,opts) { if( ! opts ) opts = { } if( smtpServer ) { if( opts.force || ! this.hasSMTPServer ) { this.setPref("smtpServer", smtpServer.id, opts.lockLevel); this.hasSMTPServer = true; } } }; /* Creates a new smtp server Artuments: opts: A hash of options. Valid options are: isDefault: true | false (default) label: A unique label for this smtp server Throws an error if there is already a default smtp server */ Mail.SMTPServer = function(opts) { if( ! opts ) opts = { }; this.Mail(); this.id = "smtp" + ++Mail.SMTPServer.count; if( opts.label ) { this.id = opts.label + "-" + this.id; } // Our perference branch is mail.smtpserver.smtpN this.addPrefBranch( [ "smtpserver", this.id ] ); if( opts.isDefault && Mail.SMTPServer.defaultServer ) throw("Default smtp server already configured to " + Mail.SMTPServer.defaultServer); if( opts.isDefault ) this.setDefault(); // Register this SMTP server if( ! Mail.SMTPServer.servers.contains(this) ) Mail.SMTPServer.servers.push(this); // Update mail.smtpservers to include the new one defaultPref("mail.smtpservers", this.joinIds(Mail.SMTPServer.servers) ); return this; }; copyPrototype(Mail, Mail.SMTPServer); // Some "class" variables Mail.SMTPServer.count = 0; Mail.SMTPServer.defaultServer = undefined; Mail.SMTPServer.servers = []; Mail.SMTPServer.prototype.setDefault = function() { Mail.SMTPServer.defaultServer = this; defaultPref("mail.smtp.defaultserver", Mail.SMTPServer.defaultServer.id ); } |