iOS mail signature woes

This morning I decided to update my company email signatures and synchronise them between the MacBook Pro (Mavericks), the iPhone and the iPad.

A couple of hours later I finally cracked it!

The main issue I had was preventing iOS from automatically converting numbers and addresses into links. This is a particular problem when it gets it wrong and doesn’t provide any obvious means of editing/cancelling the links.

First I used an online HTML editor to make my signature:

Online_HTML_Editor__Real_time_online_with_preview

Then I copied the rendered output to the mail app on Mavericks:

Signatures_and_Inbox

Then I tried emailing myself using the new signature. On Mavericks it looked perfect, but on the iPhone it misinterpreted some of the  information:

2014-01-21_10_22_14-3

In this case just the company number was displayed as a telephone number link. But on one of my other email accounts half of the address was converted to a link while the other half remained as text.

I don’t think it’s possible to suppress this automatic conversion, and I can live with it because usually it works quite well.

My iPhone and iPad problems started when I tried to copy the signature to the clipboard and paste it into the signature box on the settings app:
2014-01-21_10_30_19-3

So, the copy and paste has worked, but it’s not what I want. I’ve lost the font type and size, and nearly everything is blue.

I then tried using the online HTML editor on the iPhone but the rendered output had automatic link conversions on the telephone numbers. Not really a problem but it was still doing things I didn’t want.

Then I decided to try a cunning plan: I sent myself an email with the signature broken into multiple lines, so that no one line appeared to contain a useable telephone number or address:

2014-01-21_10_41_02-2

Then I copied the message to the iPhone’s email signature editor but it lost the font!

Finally, as I was considering giving up, I went back to the MacBook and pasted my signature onto Evernote and split it into multiple lines again. Back on the iPhone I copied the mangled signature  from Evernote to the signature editor:

2014-01-21_10_55_02-2

Progress! Next I edited the signature to remove the line breaks:

2014-01-21_10_58_05-2

And finally an email:

2014-01-21_10_58_44-2

Ok, so the telephone numbers have been converted into links but I suspect I can never prevent that, and since these are telephone numbers then I’m fine with it. But I’ve got my font type and size, and the company number is just a number (not a link), and I’ve roughly got what I wanted.

Then I repeated the last few steps again on the iPad.

There are plenty of iOS apps for making signatures but I really wanted to have the in-built signature working. And now I have it. But there must be a better way, either now or in the future. And once Apple and Google start talking to each other I might even see emails as they arrive without having to restart the mail app on Mavericks.

OS X, Python 3.3, LiClipse

Yesterday I tried to get Eclipse and PyDev installed on my MacBook Pro (running Mavericks). After a couple of hours of struggling I gave up and discovered LiClipse which is a highly recommended system for working with Python and many other languages from the Eclipse framework.

I used a clean OS X Mavericks virtual machine to get my installation nailed down, especially important since I wanted to work with Python 3.x.

Downloads

Download and install:

  1. Python 3.x.
  2. Java 7.
  3. LiClipse: click the Google Drive link, select the latest version (currently 0.9.6), click and download the OS X file (currently liclipse_0.9.6_macosx.cocoa.x86_64.dmg), then install using all default options.

Configuration

Eject all download package (DMG) folders on the desktop.

Use Finder and go to Applications, open the liclipse folder, then drag the LiClipse app to the dock.

Run LiClipse. If a software update prompt appears for Java SE 6 then install it.

Accept all defaults when starting for the first time and restart the app.
 
Select File / New project, then select PyDev/PyDev Project:
ad45e741fc7fadfa78252bb606d55228

Hit Next, then set a project name and click the link to configure an interpreter:

489ed184ec9e6e8b074feccf177fcb78

Select the advanced configuration option:

ff2c2bbde406a0db49eae0d0d91f25e7

Then select python3.3:

32ca7a2249fdaa5c7ab1905a39b37dbb

On the next screen make sure all folders are selected:

378876332975796581d1a1bba04b66ee

Back on the new project screen make any other changes – I opted not to configure the Python path – then click Finish:

04b122222be512f58fe0f7fa062a8801

The Open Associated Perspective prompt may appear – I chose to associate the project with the PyDev perspective:

0a8180c02287ff3b076f068ffd8b3004

These Python and perspective settings will be used the next time LiClipse is started for Python projects.

Quick Python test

Following on from the above new I added a new Python file, entered a couple of lines of code, and ran it:

5b8f1d79fa162facf4e384b1394d0a99

132a82e84ba8b7066edba59927094d74

4a997671f4a90eee79e6c84d6342f354

638b29041e54c6d81427455cf5407130

feb22a04d91362315bae7493317c90ec

Debugging

If the debugger is used, for example by stepping through the code, there’ll be an option to switch to another perspective – this should be used for debugging:

a8a26c331c032792e467e96787890a55

This presents more window areas for examining variables, viewing the editor and console output, etc:

a9b4613df5986bd7bd9915660c715f15

After the debugging session is complete the Debug perspective will still be active. To switch back to the original perspective use the menus:
  • Window | Open Perspective | Other, then select PyDev.

Automated Dynamic DNS updating on Raspberry Pi

I use a Raspberry Pi to host SVN and Git repositories but don’t have a static IP with my BT Infinity account. To support remote connectivity I use a dynamic DNS service from Dyn.

The only feature missing was the ability to detect when the IP address had changed, and then to automatically update it.

So with a bit lot of Googling I’ve put together a Python 3 script which is run hourly via crontabThe script uses Dyn’s IP address utility to find the current IP address. This is compared to the last known IP address which I store in a text file. If the address has changed then Dyn’s Perform Update utility is used to sent the new IP address. Any changes or errors are emailed back to me.

Here’s the script:


#!/usr/bin/env python3

import re
import urllib.request
import os
import sys
import smtplib

# -----------------------------------------------------------------------------
# variable: lastIPAddressFileName
#
# The name of a file used to store the last committed IP address.
# -----------------------------------------------------------------------------
lastIPAddressFileName = '/var/tmp/last_router_ip'

# -----------------------------------------------------------------------------
# function: mailInfo
#
# Emails a message to one or more recipients. This should just be used for
# errors and successful changes of IP. (I.e. not used for regular no-change
# messages).
# -----------------------------------------------------------------------------
def mailInfo(info):
	#print('Sending email [{0}]'.format(info))
	from email.mime.text import MIMEText

	msg = MIMEText(
	"Hello from the Raspberry Pi DNS IP update utility.\n"
	"I have a new message for you:\n\n{0}".format(info))

	msg['Subject'] = 'CDS.RPi DSN IP Utility'
	msg['From'] = 'CDS RPi'
	msg['To'] = 'jon@carpediemsystems.co.uk'

	s = smtplib.SMTP('smtp.gmail.com', 587)
	s.ehlo()
	s.starttls()
	s.ehlo()
	s.login('jon@carpediemsystems.co.uk', '******')
	s.sendmail('jon@carpediemsystems.co.uk', ['jon@carpediemsystems.co.uk'], msg.as_string())
	s.quit()

# -----------------------------------------------------------------------------
# function: findRouterIP
#
# Returns the IP of the router by scraping the results of the DYN DNS check
# IP web site. None is returned if the address cannot be found.
# -----------------------------------------------------------------------------
def findRouterIP():

	ipAddress = None
	webAddress = 'http://checkip.dyndns.org/'
	siteData = urllib.request.urlopen(url = webAddress).read()
	siteAsText = str(siteData)
	ipAddresses = re.findall('\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', siteAsText)

	if(len(ipAddresses) == 1):
		ipAddress = ipAddresses[0]

	return ipAddress

# -----------------------------------------------------------------------------
# function: dynDnsPerformUpdate
#
# Updates the IP address of a DynDNS host using Dyn's Perform Update request.
# The resulting web page is scraped and the function will return True if one
# of the valid results codes is seen (meaning the IP address was either updated
# or was not changed).
# -----------------------------------------------------------------------------
def dynDnsPerformUpdate(username, password, hostname, ipAddress):

	url = 'http://{0}:{1}@members.dyndns.org/nic/update?hostname={2}&myip={3}&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG'.format(username, password, hostname, ipAddress)
	fancyOpener = urllib.request.FancyURLopener()
	siteData = fancyOpener.open(url).read()
	siteAsText = str(siteData)

	acceptableResults = ['good', 'nochg']
	isOk = len([r for r in acceptableResults if r in siteAsText]) > 0
	return isOk

# -----------------------------------------------------------------------------
# function: checkForNewIPAddress
#
# Finds the current router IP address and compares it to the last address
# saved to the lastIPAddressFileName file (if it exists).
#
# Returns a tuple:
#	Current IP address (string)
#	Is this a new and valid IP address (boolean)
# -----------------------------------------------------------------------------
def checkForNewIPAddress():
	lastIPAddress = None

	if(os.path.exists(lastIPAddressFileName) == True):
		with open(lastIPAddressFileName, 'r') as f:
			lastIPAddress = f.read()

	currentIPAddress = findRouterIP()
	isNewIPAddress = ((currentIPAddress != None) and (currentIPAddress != lastIPAddress))

	return currentIPAddress, isNewIPAddress

# -----------------------------------------------------------------------------
# function: saveNewIPAddress
#
# Sends the new IP address to Dyn DNS. This is also saved to the
# lastIPAddressFileName file if the web operation succeeded.
#
# Returns True if the operation succeeded.
# -----------------------------------------------------------------------------
def saveNewIPAddress(newIPAddress):
	username = '******'
	password = '******'
	host = '******.dnsalias.net'
	didUpdate = dynDnsPerformUpdate(username = username, password = password, hostname = host, ipAddress = newIPAddress)

	if(didUpdate == True):
		with open(lastIPAddressFileName, 'w') as f:
			f.write(newIPAddress)

	return didUpdate

# -----------------------------------------------------------------------------
# Script main
# -----------------------------------------------------------------------------

try:
	currentIPAddress, isNewIPAddress = checkForNewIPAddress()

	if(currentIPAddress == None):
		mailInfo('ERROR: failed to get current IP address')
	elif(isNewIPAddress == False):
		print('IP address is unchanged - nothing to do')
	else:
		saveNewIPAddress(currentIPAddress)
		mailInfo('IP address updated to {0}'.format(currentIPAddress))
except:
   mailInfo("ERROR: An unexpected error was detected in the script:", sys.exc_info()[0])

Note: the scripting presentation in this post was done by using WordPress.com’s sourcecode widget, as described here.

The script was saved to:

 /bin/DNS_IP_Util.py

and made executable with:

sudo chmod +x DNS_IP_Util.py

It was scheduled to run hourly by editing root’s cron file. The command to run the editor is:

sudo crontab -e

The cron file was edited to include the following two lines:

# DNS update, every day, hourly
0 * * * * /bin/DNS_IP_Util.py &