Garrett St. John

I am a web developer and partner at Bold. This is where I share my thoughts, discoveries and other random bits.

Sending Email Attachments in iOS

April 15, 2011

The bottom line is it’s not possible. At least natively.

I discovered this unfortunate circumstance this morning as I was trying to reply to an email with a screenshot from my iPad. I could have sent it from Photos, but that would have required me to write a new email and the recipients weren’t in my address book. I would also prefer to keep the screenshot associated with the original email for context. No such luck, though.

What about sending non-photo attachments (like a document)? Also, not possible. There is an app called GoodReader that (among other things) will allow you to send files as email attachments, but no such functionality is built into iOS. Do I really want another app to enable such a simple process? And for $4.99?

I love my iPad for 101 reasons, but how can we really call the iPad a “go between” device or “laptop killer” if it can’t natively do something as simple as send an email attachment. I think this discovery just reinforces for me even further that the iPad is a consumption device and isn’t suited for creation/productivity.

Hollywood fears Netflix

March 8, 2011

Link: Fearing iTunes-Like Domination, Hollywood Plots Netflix’s Downfall

Hollywood is scared of Netflix. They saw what iTunes did to CD sales and they want to stop the same from happening to them.

Why is the Entertainment Industry So Out of Touch?

Hollywood is the land of progressive thinking, but when the consumer evolves how they want to receive their media, it’s all freaking out and plug-pulling. How did this mindset work out for the music industry?

If it didn’t cost my wife and I $25 to get into the theater, I would see more movies (let’s not even discuss how many movies are total duds once you get in). “We had to raise the prices because our films are being pirated!” True, and I grabbed a torrent or two back in the day, but Netflix has provided a way for me to legally watch a movie with a price tag I can actually bear (same goes for Rdio and my now dwindling MP3 collection).  I’ll even accept the fact that a lot of the instant streaming content is older films and Netflix throttles shipping discs. Why can’t Hollywood see what their consumers want and not only jump on board, but help expedite the process?

Companies that have had their hand in the cookie jar for a long time need to keep innovating and reinventing themselves to stay relevant or the consumer will eventually just leave them behind. Wake up, Hollywood!

A Great Method for Modeling Git Branches

February 16, 2011

Link: A successful Git branching model

This is probably one of the best tutorials I’ve seen on how to run a successful Git repository. Bravo!

Modeling Our Business Before Taking the Plunge

February 9, 2011

Link: The Magic Spreadsheet or Let’s Talk About Money

Noah Stokes shares on his blog about how we modeled Bold on “paper” before jumping in together. Planning is a very important exercise in business that I feel like a lot of creative-types are more than willing to overlook. Hopefully you find it to be just as helpful for your business.

Extracting Attachments From Emails With PHP

February 8, 2011

Yesterday I wrote about how to read emails with PHP, but I want to dig a bit deeper and discuss another part of the My Slow Low project that needed tackling: Extracting attachments from emails with PHP. For the purposes of this post, I will be specifically discussing file attachments, not HTML inline attached files.

This project was executed with CodeIgniter 2.0 so there are a few libraries and helper functions used that will not be available in other frameworks or with straight PHP, however, their functionality can be pretty easily replicated.

function email_pull() {
	// load the Email_reader library from previous post
	$this->load->library('email_reader');

	// load the meals_model to store meal information
	$this->load->model('meals_model');

	// this method is run on a cronjob and should process all emails in the inbox
	while (1) {
		// get an email
		$email = $this->email_reader->get();

		// if there are no emails, jump out
		if (count($email) <= 0) {
			break;
		}

		$attachments = array();
		// check for attachments
		if (isset($email['structure']->parts) && count($email['structure']->parts)) {
			// loop through all attachments
			for ($i = 0; $i < count($email['structure']->parts); $i++) {
				// set up an empty attachment
				$attachments[$i] = array(
					'is_attachment' => FALSE,
					'filename'      => '',
					'name'          => '',
					'attachment'    => ''
				);

				// if this attachment has idfparameters, then proceed
				if ($email['structure']->parts[$i]->ifdparameters) {
					foreach ($email['structure']->parts[$i]->dparameters as $object) {
						// if this attachment is a file, mark the attachment and filename
						if (strtolower($object->attribute) == 'filename') {
							$attachments[$i]['is_attachment'] = TRUE;
							$attachments[$i]['filename']      = $object->value;
						}
					}
				}

				// if this attachment has ifparameters, then proceed as above
				if ($email['structure']->parts[$i]->ifparameters) {
					foreach ($email['structure']->parts[$i]->parameters as $object) {
						if (strtolower($object->attribute) == 'name') {
							$attachments[$i]['is_attachment'] = TRUE;
							$attachments[$i]['name']          = $object->value;
						}
					}
				}

				// if we found a valid attachment for this 'part' of the email, process the attachment
				if ($attachments[$i]['is_attachment']) {
					// get the content of the attachment
					$attachments[$i]['attachment'] = imap_fetchbody($this->email_reader->conn, $email['index'], $i+1);

					// check if this is base64 encoding
					if ($email['structure']->parts[$i]->encoding == 3) { // 3 = BASE64
						$attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
					}
					// otherwise, check if this is "quoted-printable" format
					elseif ($email['structure']->parts[$i]->encoding == 4) { // 4 = QUOTED-PRINTABLE
						$attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
					}
				}
			}
		}

		// for My Slow Low, check if I found an image attachment
		$found_img = FALSE;
		foreach ($attachments as $a) {
			if ($a['is_attachment'] == 1) {
				// get information on the file
				$finfo = pathinfo($a['filename']);

				// check if the file is a jpg, png, or gif
				if (preg_match('/(jpg|gif|png)/i', $finfo['extension'], $n)) {
					$found_img = TRUE;
					// process the image (save, resize, crop, etc.)
					$fname = $this->_process_img($a['attachment'], $n[1]);

					break;
				}
			}
		}

		// if there was no image, move the email to the Rejected folder on the server
		if ( ! $found_img) {
			$this->email_reader->move($email['index'], 'INBOX.Rejected');
			continue;
		}

		// get content from the email that I want to store
		$addr   = $email['header']->from[0]->mailbox."@".$email['header']->from[0]->host;
		$sender = $email['header']->from[0]->mailbox;
		$text   = ( ! empty($email['header']->subject) ? $email['header']->subject : '');

		// move the email to Processed folder on the server
		$this->email_reader->move($email['index'], 'INBOX.Processed');

		// add the data to the database
		$this->meals_model->add(array(
			'username'    => $sender,
			'email'       => $addr,
			'photo'       => $fname,
			'description' => ($text == '' ? NULL : $text)
		));

		// don't slam the server
		sleep(1);
	}

	// close the connection to the IMAP server
	$this->email_reader->close();
}

I tried to comment the code above as best as possible to convey what it is I am doing with the information. This code is a method within a Controller, but I’ve only included the attachment extraction method of the class for this post.

For more information and to credit those I’ve learned from, please check out David Walsh’s post on Retriev[ing] Your Gmail Emails with PHP and IMAP as well as Chris Hope’s post on Extracting attachments from an email message using PHP IMAP functions.

Reading Emails with PHP

February 7, 2011

Last week I got to do a fun little side project which we called My Slow Low. The site is a simple photo collection of slow/low carb meals for those that are out of ideas on what to eat, but want to stick to their diet. While conceptualizing how we would build the site, the idea of emailing in photos came up. We didn’t want the hassle of account management and were trying to go for a more “mobile capable” option. This was my first time coding for IMAP with PHP and I figured some others could use a jump start from what I’ve learned.

PHP already has a nice IMAP extension which needs to be installed and enabled before going further. The core functionality is all there, but the specifics on how to use it aren’t necessarily all that clear.

Here’s a PHP class I put together to do some basic operations on an IMAP Inbox. It’s a bit tailored to this project, but could be easily revised to fit other needs or extended to be more full featured.

<?php

class Email_reader {

	// imap server connection
	public $conn;

	// inbox storage and inbox message count
	private $inbox;
	private $msg_cnt;

	// email login credentials
	private $server = 'yourserver.com';
	private $user   = 'email@yourserver.com';
	private $pass   = 'yourpassword';
	private $port   = 143; // adjust according to server settings

	// connect to the server and get the inbox emails
	function __construct() {
		$this->connect();
		$this->inbox();
	}

	// close the server connection
	function close() {
		$this->inbox = array();
		$this->msg_cnt = 0;

		imap_close($this->conn);
	}

	// open the server connection
	// the imap_open function parameters will need to be changed for the particular server
	// these are laid out to connect to a Dreamhost IMAP server
	function connect() {
		$this->conn = imap_open('{'.$this->server.'/notls}', $this->user, $this->pass);
	}

	// move the message to a new folder
	function move($msg_index, $folder='INBOX.Processed') {
		// move on server
		imap_mail_move($this->conn, $msg_index, $folder);
		imap_expunge($this->conn);

		// re-read the inbox
		$this->inbox();
	}

	// get a specific message (1 = first email, 2 = second email, etc.)
	function get($msg_index=NULL) {
		if (count($this->inbox) <= 0) {
			return array();
		}
		elseif ( ! is_null($msg_index) && isset($this->inbox[$msg_index])) {
			return $this->inbox[$msg_index];
		}

		return $this->inbox[0];
	}

	// read the inbox
	function inbox() {
		$this->msg_cnt = imap_num_msg($this->conn);

		$in = array();
		for($i = 1; $i <= $this->msg_cnt; $i++) {
			$in[] = array(
				'index'     => $i,
				'header'    => imap_headerinfo($this->conn, $i),
				'body'      => imap_body($this->conn, $i),
				'structure' => imap_fetchstructure($this->conn, $i)
			);
		}

		$this->inbox = $in;
	}

}

?>

A fair amount of this is self-explanatory or commented inline, but I will go over the inbox() method because it is the core functionality. The IMAP inbox is much like an array with a numbered key starting at 1. In the inbox() method, I store that index so that the email can be moved, deleted, or read again later.

Next, the header is stored with the function imap_headerinfo(). This pulls down an object from the server containing information like the Subject, From: address, To: address, and text encoding type.

Using imap_body(), the body text of the email is retrieved. What’s returned isn’t overly clean as it’s just the raw body with boundaries included (see: multipart messages). If the received email is in HTML, there will be a plain text and HTML version included. It’s certainly possible to parse through this data like any email client does, but it’s definitely a little bit messy.

Lastly, ‘structure’ is retrieved with the imap_fetchstructure() function. This is very important if you are trying to access attachments as I was with My Slow Low. In my next post, I’ll go further into the details of saving an attachment from an email and share some more about how I implemented the email processor for My Slow Low.

Update: I’ve posted the follow-up to this article on extracting email attachments with PHP.

Bettween

February 4, 2011

Link: Bettween | Easily Track and Share Twitter Conversations

“Dang it, I know I talked to @sam_h about that a year ago, but what were the details?” This question led me to Bettween. Easily retrieve conversations you’ve had on Twitter, or filter down to conversations between you and another user. Remember that it was in the last 3 months? You can filter conversations that way too.

Slow / Low Carb Dieting

February 1, 2011

Link: My Slow Low

The resident ideas guy at Bold, Noah Stokes, has been doing the Four Hour Body “slow carbs” diet for about a month now. Today he admitted that the choices seem limited on what to eat and was interested in what others are eating. A few hours later, we present you with My Slow Low.

Advanced Regular Expressions

Link: Advanced Regular Expression Tips and Techniques

For those of you that don’t geek out about Regular Expressions like I do, here’s a nice article from Nettuts on advanced Regex techniques.

Becoming an Early Riser – Week 1

January 31, 2011

With the first week of being an early riser under my belt, I thought it appropriate to write about how it went. All-in-all things went well and I really enjoyed my mornings. There were, however, some things I struggled with throughout the week.

Fighting the Urge to Stay Up Late

Fighting the urge to stay up late was really tough. Something in my body kicks on about 9PM and no matter how tired I am throughout the day, I have a second boost of energy. It was very difficult for me on most days to force myself to get into bed and just read. Once I did committed, my body gave in and sleepiness followed within 15 minutes.

Feeling Like I Have Less “Me” Time

Because that alone time at night has become such a part of my life, I found myself feeling like I didn’t have enough time in the day to do the things I wanted to do. In reality, I was awake the same amount of time (or even more) as before my shift in schedule. I started skipping a few TV shows I have always watched in the past (likely a good thing) and shifted a few other tasks to the morning. I’m looking to incorporate exercise as well which should help me to feel like I’m accomplishing more as well as helping my overall energy level.

One habit I did fall into was working longer hours. No good. There were a few days I started working at 6AM and worked until 6PM. My goal was never to work longer, only to be more effective in those hours which I work. I’ve either got to fill my morning hours with non-work related activities or knock off work earlier. Currently, I’m leaning towards the latter.

Early Morning Darkness

In the winter, it’s dark in the morning and I feel like it makes getting up way more difficult. Stumbling into the bathroom to wake up with a shower is a struggle. This is where Steve Pavlina’s tips for waking up really come into play. Practice makes perfect and it’s getting easier each day.

Early Mornings on the Weekend

This last weekend was the first under my new schedule and it was fairly difficult. I made some plans early in the morning on Saturday which made it easier to get up, but Sunday was a struggle. I knew in my mind there was no reason to be awake. If I was up, it was going to be to watch TV because I had no desire to do chores or side projects. I ended up sleeping in until about 9:30AM which was a decent compromise. I was still tired at about 10PM last night which didn’t screw up my sleep cycle too badly.

I think going forward I need to come up with a solid plan for my weekend mornings or I’ll fall into the same issue again. Even though it feels like the natural thing to do at that hour, I don’t want to work on the weekends.

The Plan

All-in-all I’ve really liked this new schedule. I plan to stick to it and hope that this first week wasn’t possible just because of the excitement behind proving I can do something new. I’ll continue to practice getting up so that I get better at ignoring the voice in my head telling me to stay in bed. With a few refinements, I really think this is something I could find good success in by changing permanently.