Making an SMS Chatbot #1 - Exposing a REST API on an iPhone 3GS for sending and receiving SMS
This is a fun little project I started while toying around with a spare iPhone. All mentioned code can be found on my GitHub.
For this project, I will be using a jailbroken iPhone 3GS running iOS 6.1.6, the latest it can handle. The nice thing about these older devices is that they can be easily restored to any compatible iOS version, without any trouble. Jailbreaking then allows us to get really creative with the software running on the machine. I used the redsn0w jailbreak for the new-bootrom iPhone 3GS, with the p0sixspwn package to make it an untethered jailbreak.
The most important part of a web API is, quite obviously, the web part. For this purpose, I used the ios-webstack.tk Cydia repository which provides a "one-tap solution" for installing a complete WebStack on a jailbroken iOS device. The repository actually contains multiple network service packages, such as an SMTP server, and a fully-fledged WordPress installation. The part we're more interested in, however, is the ios-lighttpd-php-mysql package, which installs, amongst other things, an HTTP daemon and a convenient preferences panel in the settings app to enable or disable the service.
I configured httpd to execute CGI scripts, and set a filetype association for .sh and .py scripts to their respective interpreters. That way, when the server gets a request for a python file, it will instead execute it and return the console output as a raw HTTP response. This will allow us to make quick and dirty scripts for running on the very resource-limited hardware that we have.
For sending out SMS messages, I could not find any built-in or third-party options that were dedicated to operating the messaging app from the command line. That said, I did find a quite obscure and undocumented use for the biteSMS app that will allows me to send messages from a shell script. The app itself is riddled with ads, but luckily we won't be using its GUI. Once installed, we can call it from a shell prompt to send a text message!
/Applications/biteSMS.app/biteSMS -send -carrier 1234567890
Receiving Incoming Transmissions
Now comes the fun part. Ideally, we would like a way of triggering an action whenever an SMS is received. In the best-case scenario, we would be calling a shell script directly. Worst-case, we would end up catching some UI notification, which would be somewhat impractical, be we could work with that.
I looked into Activator triggers for incoming messages, sadly the triggering action was not as reliable as I would have liked. The iPhone ended up missing many text messages, or only triggering when multiple unread messages had started piling up. In the process however, I did find ways of triggering UI system functions, like capturing a screen shot, from the command line, which is nice, but not technically useful for the task at hand.
How the iPhone stores messages
While digging around, I stumbled upon the internal SMS database, which is simply an sqlite3 database. The .db file itself is located in /var/mobile/Library/SMS/sms.db. In there, we find a "chat" table containing the conversation metadata, and a "message" table, which contains the actual text messages. It is then a matter of writing an SQL query to grab that data.
SELECT m.rowid, c.chat_identifier,m.is_from_me,m.date,m.text FROM message m INNER JOIN chat_message_join cmj ON cmj.message_id=m.rowid INNER JOIN chat c ON cmj.chat_id=c.rowid WHERE cmj.message_id=:id
To trigger an action when a message is received, I wrote a little background script which polls the database every second, checking whether a new entry has been created by looking at the highest id assigned. When a change is detected, the script makes an HTTP request to my computer, which runs the backend software.
The iPhone, like OS X, uses the plist format with launchd to control system services. The syntax is somewhat more esoteric than the more common systemd, but it gets the job done anyway. The service files are located under /Library/LaunchDaemons, and can be enabled using launchctl.
- To prevent the iPhone from turning off its Wi-Fi connection after going to sleep, I used the Insomnia Cydia tweak.
- Instead of going through the process of installing a recent python version, which would have included proper JSON serialization support, I formatted the HTTP response manually by concatenating various parts of the SMS data. Using proper JSON formatting would definitely be better, especially in the long run, but I did not really want to go through the trouble of installing multiple python versions on the iPhone.
I now have a way of sending and receiving SMS by sending a few JSON-formatted requests. That should definitely come in handy later...