Check Your Email Via Text Message

I sometimes find myself in a situation where I have a cellular signal but no data signal on my phone. I don’t know how common this is for others, but I often have this problem when I’m in rural areas. In this situation, it would be nice to be able to send a quick text and receive the subject and sender of the first few messages in my email inbox. I couldn’t find any other service that does this, so I decided to build one using Python, Flask and Twilio.

An example of the text response, with the sender and subject for five messages

This type of service might be helpful for people in lower income countries where smartphone usage is less common and data networks are more spotty. Google rolled out a SMS method for checking your email in Nigeria, Ghana and Kenya in 2012, but it seems to have since ended. Whether or not you could profitably provide this service probably depends on the SMS messaging rates for the specific country.

Anyways, on to the details.

The Round Trip

The entire information round trip is as follows:

Phone SMS <-> Twilio <-> App Engine Server <-> Gmail Server

  1. My phone sends an SMS to a Twilio number
  2. Twilio performs a POST request to my application running on Google App Engine
  3. My application opens an IMAP connection with Google’s servers
  4. Google sends my app the inbox content
  5. My app parses this content and sends the response to Twilio
  6. Twilio sends an SMS to my phone with the final inbox content

Although this may look complicated, all we really have to deal with is the stuff that runs on the App Engine Server, as Twilio and Google handle the rest.

Receiving the POST Request

You can configure Twilio to perform a GET or POST request to any URL when they recieve a text from your phone. I chose to use a POST request, as it might be a little more secure. I won’t go through the details of this setup, but the Twilio docs walk you through how to associate the below URL with your account’s phone number.

The request comes with a lot of metadata including the sender’s number, the time of day and the content of the text. Because this application responds with private email data, I added a few somewhat hacky security measures. Note that these measures aren’t entirely secure, and some other approach would be needed for a production application.

First, I set the url for the post request to be a long random string numbers and letters. This will prevent someone from doing a brute force attack on your site until they find the correct url. In this case, I store that random url in a config.py file, and import those configurations with the url = "/" + app.config.get('SECRET_URL’) line below. The secret url will be something like: /4dd927161ef69f8e89b44d555259dd565a8e6b592d111c147b743347.

Second, I ensure that the sender of the request has my phone number in the body with the from_number = request.values.get("From", None) line, and then compare that with the callers config variable, where I store any authorized phone numbers.

The following is the majority of the application code in main.py:

app.config.from_object('config')
callers = app.config.get('CALLERS')
email_address = app.config.get('EMAIL')
password = app.config.get('PASSWORD')
url = "/" + app.config.get('SECRET_URL')

# url is long random string to prevent a brute force attack
@app.route(url, methods=["GET", "POST"]) 
def mail_response():
    """Respond to incoming text with email content, if authorized"""
    from_number = request.values.get("From", None)
    if from_number in callers:
        resp = twilio.twiml.Response()
        content = getmail(email_address, password)
        resp.message(content)
        return str(resp)
    else:
        return 'Sorry, Nothing at this URL.', 404

Opening the Connection with Gmail

After I have verified that the request is legitimate, I then open up an IMAP connection with Gmail. The function below takes your Gmail address and password as arguments and logs in:

def getmail(email_address, password):
    mail = imaplib.IMAP4_SSL('imap.gmail.com')  
    mail.login(email_address, password)
    mail.list()
    mail.select('inbox')

Note that the IMAP connection method is an older one, and is considered to be less secure by Google now. In order to use it, you need to enable less secure apps on your Gmail account (I have only used this on a test account so far). This method requires the use of websockets, which are only available on paid apps. Google does offer a free trial for this service, which I used for this example.

Future versions of this app will probably need to use OAuth2.0 authentication, and some type of initial web interface for signup, as this form of authentication can’t be done via SMS message alone. This will make the request process easier and more secure, as you could just use Gmail’s RESTful API.

Parsing and Responding

I won’t go into the details of this parsing, as the comments in the code do a fairly good job of explaining. One note: if you want to return more than 5 emails, just change the 5 in this for loop to the desired number: for i in range( latest_email_id, latest_email_id - 5, -1 ).

After the emails have been parsed, I just add the subject and sender content to the response in main.py, then return that response to Twilio:

content = getmail(email_address, password)
resp.message(content)
return str(resp) 

Twilio takes care of the rest by sending this message back the original phone via SMS. This whole process usually takes around 5 seconds total. The end result will be an SMS message with a body that looks something like this:

[john@gmail.com] tps reports 
[doe@gmail.com] meeting 
[matt@yahoo.com] hello friend
[bob@gmail.com] new news
[ben@gmail.com] meeting Wed