Changing Application Settings in OS X Based on (Network) Location

Filed under Mac, Scripts

MarcoPolo switching the location I Generally use my laptop in three different locations:

Changing application settings manually every time I arrive at a new location gets tiring really, really fast. The location settings you can create in OS X can specify network-related settings (proxies etc.) on a system level for all the different locations you enter, but then you of course have to manually switch the current location setting every time. I First used Quicksilver for that, but then found out about MarcoPolo, which is an excellent open source application that can change your network location (and initiate any other specified actions) automatically based on a bunch of rules using evidence sources (like Wi-Fi access point SSID or the first octets of a DHCP-assigned IP address) that you can set up. Setting up MarcoPolo and defining the rules was really straightforward, and its operation has been quite stable from the day I've started using it.

The problem I had next was that I used two applications that are cross-platform and thus not designed to take advantage of any OS-wide settings: Firefox and Subversion. So, these applications have their own settings management and I needed a way to make some of their settings (namely the proxy servers) change automatically along with everything else.

Update (jan 14, 2008): I Rewrote this script in Python a while back. It's now much nicer. Get the updated version here.

Update (aug 17, 2008): Refined the text in the post a bit according to my larger amount of experience with this. Changed link to point to the latest version of the script.

The way I did this was to create a Python script that swaps configuration files set up beforehand for every location for these two applications. Then I added actions in MarcoPolo to run this shell script every time I arrive at a new location. When the script runs, it checks the current location and then goes through all the applications specified in its settings and changes their configuration files to ones I have set up for the location in question. It also notifies the user of what it is doing with Growl notifications.

You can download the shell script below. I tried to write it so that it'll be pretty straightforward for others to take advantage of as well. The directions to use it are in the script itself.

The script and supporting files in a zip archive

This is what the script itself looks like:

# ----------------------------------------------------------
# A Script that switches application configuration files
# based on the current system network location (I myself
# use this for changing proxy server configurations)
# Copyright (c) 2007-2008 Ali Rantakari
# http://hasseg.org
# ----------------------------------------------------------
# - OS X Leopard (10.5)
# - Growl (and the growlnotify script (distributed with
#          growl))
# ----------------------------------------------------------
# - Go through the 'settings' area below and change the
#   values to your liking
# - Create configuration files for each location and each
#   application you would like to change configs for, using
#   the following naming pattern:
#      <appname>.<config_file_name>.<location_name>
#      ( for example: firefox.user.js.Work )
#   These files must be located in the same folder as this
#   script. If you wish to add a common, location-agnostic
#   prefix for an app's configuration file, create a file
#   with the following naming pattern, and it will be
#   prepended to the configuration file before it is set:
#      <appname>.<config_file_name>
#   You can also create a file called "defaultconfig-prefix",
#   which will be prepended to all configuration files as
#   they are set, regardless of application and location.
# - Call the script to switch the configuration files.
#   I Suggest using some automated system for this - I
#   myself use MarcoPolo. Since this is a Python script and
#   MarcoPolo doesn't want to run it (using the "ShellScript"
#   action,) make it call the included switchProxiesCaller
#   bash script (which will then call this one) each time
#   the network location changes.
# ----------------------------------------------------------

import string, sys, os, shutil

def getMyBaseName():
	return os.path.basename(sys.argv[0])

def getMyDirName():
	return os.path.dirname(sys.argv[0])

# ----------------------------------------------------------------

# The applications you want to switch configuration files for.
# A dictionary where each key is the app name and corresponding
# values are dictionaries with the following two keys:
#   file : the app's config file name
#   dir  : the directory where the config file is located
#          (without trailing slash)
# examples:
#   apps['firefox'] = {'file':'user.js', 'dir':'/Users/me/Library/Application Support/Firefox/Profiles/wn7lxarp.default'}
#   apps['subversion'] = {'file':'servers', 'dir':'/Users/me/.subversion'}
#   apps['bash'] = {'file':'.bash_profile', 'dir':'/Users/me'}
apps = {}

# locations of CLI apps used
# - system: (these should be here)
osascript = "/usr/bin/osascript"
# - custom: (these you need to get yourself)
growlnotify = "/usr/local/bin/growlnotify"


# whether or not to use growl for notifications
# (if this is False, notifications will be
#  printed to stdout)
growl_use = True

# the name of this application 
# (to send to growlnotify)
growl_appname = "Config switch script"

# the file to use as an icon in growl
# notifications
growl_icon = getMyDirName()+"/icon.png"

# - - - - - - - - - - - - - - - - - - - - - -
# settings end here.
# ----------------------------------------------------------------

# BEGIN: functions -----------------------------------------

# displays a notification/message to the user
def showMessage(title, msg):
	title = title.replace('"', '\'')
	msg = msg.replace('"', '\'')
	if (growl_use):
		if (os.path.exists(growlnotify)):
			os.system(growlnotify+' -a "'+growl_appname+'" -t "'+title+'" -m "'+msg+'" --image "'+growl_icon+'"')
			print " "
			print "** Error:"
			print "   growlnotify not found in path: \""+growlnotify+"\""
		print " "
		print "** "+title+":"
		print "   "+msg

# returns the name of the current OS X network location
def getCurrLocation():
	cmd = osascript+"""<<END
	tell application "System Events"
		tell network preferences
			return the name of current location
		end tell
	end tell
	fin,fout = os.popen2(cmd)
	return fout.read().rstrip('\n')

# concatenates two files (first & second) into a
# third file (output)
def concatenateFiles(first, second, output):
	file = open(first, 'r')
	data = file.read()
	file = open(second, 'r')
	data += file.read()
	outputfile = open(output, 'w')

def getFileContents(filepath):
	file = open(filepath, 'r')
	data = file.read()
	return data

# --end--: functions - - - - - - - - - - - - - - - - - - - -

currloc = getCurrLocation()

if (currloc != None) and (currloc != ""):
	for appkey in apps:
		app = apps[appkey]
		origcfg = app['dir']+"/"+app['file']
		thiscfg = getMyDirName()+"/"+appkey+"."+app['file']+"."+currloc
		if os.path.exists(thiscfg):
			newcontents = ""
			if os.path.exists(getMyDirName()+"/defaultconfig-prefix"):
				newcontents = newcontents + getFileContents(getMyDirName()+"/defaultconfig-prefix")
			if os.path.exists(getMyDirName()+"/"+appkey+"."+app['file']):
				newcontents = newcontents + getFileContents(getMyDirName()+"/"+appkey+"."+app['file'])
			newcontents = newcontents + getFileContents(thiscfg)
				outputfile = open(origcfg, 'w')
				showMessage("Settings for "+appkey+" OK", 'When you run '+appkey+' the next time, settings for "'+currloc+'" will be used.')
			except (IOError, os.error), why:
				showMessage("Error", 'Can not write new '+appkey+' config file: "'+str(why)+'"')
			showMessage("Error", 'Can not find '+appkey+' configuration for location "'+currloc+'"')
	showMessage("Error", "Can not determine current network location")


google.com/accounts/o8… November 11, 2009 at 8:42 AM

Thank you. This is great! Like all your other scripts, this one is well documented and makes it thus easy to adapt (and learn from it). Great!!

Matthieu March 23, 2011 at 10:42 AM

If your experience configuration files being switched to the wrong location, try adding a 2 to 5 sec delay before calling switchConfigsCaller, in your Marco Polo preferences.

It gives either MP or the OS enough time to effectively make the switch to the current location, before switchConfigs asks for it.

Thanks for the script, a real productivity booster !