Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions AutoFromPicker-10.9.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# AutoFromPicker plugin for Mail.app
# (C) Copyright 2012 Matthew Somerville
# Copyright 2003-2011, James R. Eagan (code at my last name dot me)
# Nagging and whining and intermittent Maintaining by Stefan Magdalinski
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# and GNU Lesser General Public License along with this program.
# If not, see <http://www.gnu.org/licenses/>.
# Set up

from AppKit import *
from Foundation import *
import objc
import PyObjCTools.AppHelper

from config import *

ModuleBundle = objc.currentBundle()
MVMailBundle = objc.lookUpClass('MVMailBundle')
DocumentEditor = objc.lookUpClass('DocumentEditor')

# Register this module
class AutoFromPicker(MVMailBundle):
@classmethod
def initialize(cls):
cls.registerBundle()
NSLog(u"AutoFromPicker plugin registered with Mail")

# This is a helper function that swaps an internal Mail function with a
# function in this file. Called as a decorator on the new function you want to
# use instead, which will be passed the original function as its first argument
def swizzle(*args):
cls, SEL = args
def decorator(func):
oldIMP = cls.instanceMethodForSelector_(SEL)
def wrapper(self, *args, **kwargs):
return func(self, oldIMP, *args, **kwargs)
newMethod = objc.selector(wrapper, selector = oldIMP.selector, signature = oldIMP.signature)
objc.classAddMethod(cls, SEL, newMethod)
return wrapper
return decorator

# This sets the sender for the message to the address provided. It should also
# update the visible look, but this doesn't always work in Lion.
def updateSenderAndNotify(backend, to):
backend.setSender_(to)
# Below does not exist any more :( And don't know what argument to pass to _mailAccountDidChange_
try:
backend.delegate().headersEditor().mailAccountsDidChange()
except:
pass

class DocumentEditor(objc.Category(DocumentEditor)):
# Main checking function, called in the three cases. Checks To against provided data,
# and alerts or changes account.
def checkRecipients(self, alert=False):
backend = self.backEnd()
try:
from_line = backend.message().mutableHeaders().addressListForKey_(u"from")[0]
except:
from_line = backend.message().mutableHeaders().headersForKey_(u"from")
to_line = backend.allRecipients()
if to_line:
for to in to_line:
# The below logging breaks if there's a non-ASCII character in the From/To headers.
#NSLog('%s %s' % (from_line, to))
for account, strings in ACCOUNTS.items():
for string in strings:
if string.lower() in to.lower() and from_line.lower() != account.lower():
if alert:
self.fromAlertSheet()
return False
else:
NSLog('[AFP] switching account to %s' % account)
updateSenderAndNotify(backend, account)
return True
return True

# Function that displays a modal dialogue box
def fromAlertSheet(self):
alert = NSAlert.alloc().init()
alert.addButtonWithTitle_('Send')
alert.addButtonWithTitle_('Cancel')
alert.setMessageText_('Message Has Incorrect From')
alert.setInformativeText_("Your mail is to work addresses, but is not from your work address. Do you wish to continue?")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window(), self, self.fromAlertSheetDidEnd, 0)

# What to do when one of the dialogue box buttons are clicked
def fromAlertSheetDidEnd(self, panel, returnCode, contextInfo):
if returnCode == NSAlertFirstButtonReturn:
self.send_(contextInfo)
else:
NSLog('User cancelled sending message.')
fromAlertSheetDidEnd = PyObjCTools.AppHelper.endSheetMethod(fromAlertSheetDidEnd)

# If desired, replace the function that runs when you hit Reply with one that
# checks the To: list and then calls the original function
if SET_ON_REPLY:
@swizzle(DocumentEditor, 'finishLoadingEditor')
def finishLoadingEditor(self, original):
NSLog('[AFP] finishLoadingEditor')
self.checkRecipients()
original(self)

# If desired, replace the function called when you hit Send with one that
# checks the recipients, possibly shows a dialogue box, or otherwise just
# sends.
if ALERT_ON_SEND:
@swizzle(DocumentEditor, 'send:')
def send(self, original, sender):
NSLog('[AFP] send:')
if self.checkRecipients(True):
original(self, sender)

13 changes: 0 additions & 13 deletions AutoFromPicker.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
ModuleBundle = objc.currentBundle()
MVMailBundle = objc.lookUpClass('MVMailBundle')
MailDocumentEditor = objc.lookUpClass('MailDocumentEditor')
HeadersEditor = objc.lookUpClass('HeadersEditor')

# Register this module
class AutoFromPicker(MVMailBundle):
Expand Down Expand Up @@ -113,18 +112,6 @@ def finishLoadingEditor(self, original):
self.checkRecipients()
original(self)

# If desired, replace the function that runs when you change the recipients to one that
# calls the original function, then checks the To: list to possibly update sender
if SET_ON_RECIPIENT_UPDATE:
@swizzle(HeadersEditor, 'recipientsDidChange:')
def recipientsDidChange(self, original, sender):
NSLog('[AFP] recipientsDidChange')
original(self, sender)
try:
objc.getInstanceVariable(self, '_documentEditor').checkRecipients()
except:
objc.getInstanceVariable(self, 'documentEditor').checkRecipients()

# If desired, replace the function called when you hit Send with one that
# checks the recipients, possibly shows a dialogue box, or otherwise just
# sends.
Expand Down
57 changes: 10 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@ Mail.app-AutoFromPicker-plugin

Automatically keep work and personal OS X Mail.app accounts separate.

Here, it does three things, with a bug in one of them:
Here, it does two things:

1. When you hit reply to a mail, it scans the recipients and sets the From
account accordingly.

2. When you alter the recipient lines whilst writing an email, it updates the
From account. [1]

3. When you hit send, it does a final check of the recipients and brings up a
dialogue box (though given 1 and 2 it probably won't have to do that, as
it'll already be set correctly).

[1] It does do this, but it does not refresh the display of the dropdown - this
is the bug, I can't work out how to tell it to redraw. I think pre-Lion had a
call that was removed in Lion; I imagine there must be a way, but as none of
this is documented, I'm not sure how to find out.
2. When you hit send, it does a final check of the recipients and brings up a
dialogue box (though given 1 it probably won't have to do that, as it'll
already be set correctly).

Install
-------
Expand All @@ -33,45 +25,16 @@ under the manual install instructions.
and domains/emails you're interested in. Make sure the account matches what
appears in Mail's From drop down exactly. Also pick which features you want
to be on (True) or off (False).
3. Run make.sh in this directory. This will build the app and move it into
Mail's Bundles directory.
4. Start Mail.
3. If on 10.9, generate yourself a self-signed certificate.
4. Run make.sh "Your Certificate Name" in this directory. This will build the
app and move it into Mail's Bundles directory.
5. Start Mail.

Da-da! If it doesn't work, run Console and see what error it's outputting when
it goes wrong, and let me know.
=======
Automatically keep work and personal OS X Mail.app accounts separate

Based on Attachment Scanner Plugin by James Eagan http://eaganj.free.fr/code/mail-plugin/

and the code was written by Matthew Somerville...

Installation
If you haven't installed Mail bundles before, you'll have to run the two defaults write commands as given on http://www.cs.unc.edu/~welch/MailFollowup/ install instructions.

Quit Mail.
Unzip the zip file.
Edit config.py.EXAMPLE to have your account details and domains/emails you're interested in, make sure the account matches what appears in Mail's From drop down exactly, and save as config.py
Edit the True/False depending upon which functionality you want.
Open up Terminal in the directory containing what you've just unzipped.
Run "python setup.py py2app" to create the app from the source code, it'll print some gumph.
Move the newly created dist/AutoFromPicker.mailbundle to ~/Library/Mail/Bundles/
Start Mail.
If it gives a disabled plugin error, then I missed out some Mail version UUIDs from the list you have to provide for every version of Mail you want your plugin to run under. Find yours by running:

defaults read /System/Library/Frameworks/Message.framework/Resources/Info PluginCompatibilityUUID
defaults read /Applications/Mail.app/Contents/Info PluginCompatibilityUUID
and adding those two strings to the list in setup.py before step 4 below (which you'll see has many strings for many different versions).

Da-da! If it doesn't work, run Console and see what error it's outputting when it goes wrong, and let me know.
Based on Attachment Scanner Plugin by James Eagan http://eaganj.free.fr/code/mail-plugin/

Here, it does three things, with a bug in one of them:
and the code was written by Matthew Somerville.

When you hit reply to a mail, it scans the recipients and sets the From account accordingly.
When you alter the recipient lines whilst writing an email, it updates the From account. [1]
When you hit send, it does a final check of the recipients and brings up a dialogue box (though given 1 and 2 it probably won't have to do that, as it'll already be set correctly).
[1] It does do this, but it does not refresh the display of the dropdown - this is the bug, I can't work out how to tell it to redraw. I think pre-Lion had a call that was removed in Lion; I imagine there must be a way, but as none of this is documented, I'm not sure how to find out.
>>>>>>> better README.md
=======
Automatically keep work and personal OS X Mail.app accounts separate
>>>>>>> Revert "better README.md"
5 changes: 1 addition & 4 deletions config.py.EXAMPLE
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
# When you hit reply, should it change the account
SET_ON_REPLY = True

# When you change To/CC/etc when writing a reply, should it change the account (no visible notification in Lion BUG)
SET_ON_RECIPIENT_UPDATE = True

# When you click send, should it check and pop up a modal dialogue box
ALERT_ON_SEND = True

Expand All @@ -32,4 +29,4 @@ ALERT_ON_SEND = True
ACCOUNTS = {
# 'Matthew Somerville <[email protected]>': [ 'dracos.co.uk', '[email protected]' ],
# 'Matthew Somerville <[email protected]>': [ 'mysociety.org', 'theyworkforyou.com', '[email protected]' ],
}
}
19 changes: 16 additions & 3 deletions make.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
#!/bin/bash

OS_VERSION=`sw_vers -productVersion | grep -o 10\..`

CERT=$1
if [ "$OS_VERSION" == "10.9" ]; then
: ${CERT:?"You must supply a certificate name"}
fi

rm -rf build dist
python setup.py py2app
rm -rf ~/Library/Mail/Bundles/AutoFromPicker.mailbundle
mv dist/AutoFromPicker.mailbundle ~/Library/Mail/Bundles/
/usr/bin/python setup.py py2app
rm -rf ~/Library/Mail/Bundles/AutoFromPicker*.mailbundle

if [ "$OS_VERSION" == "10.9" ]; then
find dist -print | egrep .so$ | xargs codesign -fv -s "$CERT" > /dev/null
find dist -print | egrep -i python$ | xargs codesign -fv -s "$CERT" > /dev/null
fi

mv dist/AutoFromPicker*.mailbundle ~/Library/Mail/Bundles/
39 changes: 32 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@
from distutils.core import setup
import py2app

# If running gives a 'disabled plugin' error, then I missed out some Mail version UUIDs from the list; you have to provide for every version of Mail you want your plugin to run under. Find yours by running:
# "defaults read /System/Library/Frameworks/Message.framework/Resources/Info PluginCompatibilityUUID"
# "defaults read /Applications/Mail.app/Contents/Info PluginCompatibilityUUID"
# and adding those two strings to the list below
import platform

VERSION='0.3'
COPYRIGHT='Copyright 2012, Matthew Somerville'
# If running gives a 'disabled plugin' error, then I missed out some Mail
# version UUIDs from the list; you have to provide for every version of Mail
# you want your plugin to run under. Find yours by running, before 10.9:
# defaults read /System/Library/Frameworks/Message.framework/Resources/Info PluginCompatibilityUUID
# defaults read /Applications/Mail.app/Contents/Info PluginCompatibilityUUID
# OR in 10.9:
# defaults find UUID | grep MailCompatibility
# defaults find UUID | grep MessageCompatibility
# and adding those two strings to the list below.

VERSION='0.4'
COPYRIGHT='Copyright 2013, Matthew Somerville'

plist = dict(NSPrincipalClass='AutoFromPicker',
CFBundleGetInfoString=\
Expand All @@ -51,12 +58,30 @@
'6E7970A3-E5F1-4C41-A904-B18D3D8FAA7D', # Mail 5.1
'EF59EC5E-EFCD-4EA7-B617-6C5708397D24', # Message 5.1
'4C286C70-7F18-4839-B903-6F2D58FA4C71', # Mail 5.2 (upto 10.7.4)
'3335F782-01E2-4DF1-9E61-F81314124212', # Messages 5.3
'758F235A-2FD0-4660-9B52-102CD0EA897F', # Mail 5.3 (10.7.5)
'1146A009-E373-4DB6-AB4D-47E59A7E50FD', # Messages 6.0 ( 10.8.0 )
'608CE00F-4576-4CAD-B362-F3CCB7DE8D67', # Mail 6.0 ( 10.8.0 )
'2183B2CD-BEDF-4AA6-AC18-A1BBED2E3354', # Messages 6.? ( 10.8.4 )
'19B53E95-0964-4AAB-88F9-6D2F8B7B6037', # Mail 6.? ( 10.8.4 )
'0941BB9F-231F-452D-A26F-47A43863C991', # Mail 7.0 ( 10.9.0 )
'FBE5B158-5602-4A6D-9CC5-8461B9B7054E', # Mail 7.0 (1822)
'1CD40D64-945D-4D50-B12D-9CD865533506', # Mail 7.1 (1827)
'88ED2D4C-D384-4BF5-8E94-B533455E6AAF', # Mail 7.2 (1874)
'F4C26776-22B3-4A0A-96E1-EA8E4482E0B5', # Mail 7.3 (1878.2)
'D1EFE124-86FF-4751-BF00-80B2C0D6F2E4', # Mail 7.3 (1878.6)

]

)

OS_VERSION = platform.mac_ver()[0]

if OS_VERSION >= '10.9':
plugin = ['AutoFromPicker-10.9.py']
else:
plugin = ['AutoFromPicker.py']
setup(
plugin = ['AutoFromPicker.py'],
plugin = plugin,
options = dict(py2app=dict(extension='.mailbundle', plist=plist))
)