Under the Covers

In our modern world of software, we take for granted how much is done for us by “black box” code and often tend to look right past how things are actually done behind the scenes. While libraries that conveniently handle sending email, authenticating users, encrypting data, etc. are helpful and speed up the engineering process, they also can leave a void in the knowledge of engineers.

I have always been a fan of looking under the covers and figuring out how things actually work. Note that this doesn't mean re-inventing the wheel – just that you should understand how things you use work. I, personally, dissect everything, often to the distress of my wife as she sees me pulling out my tools next to the brand new electronic item we just brought home. I typically don’t dissect it to modify or re-implement it, but to understand how it works. This knowledge and ability came in handy not too long ago when my television decided to turn itself on and increase to max volume at 3 a.m. The next morning I popped the cover, disconnected the faulty power/volume touch pad and bingo – fixed.

For software engineers, this knowledge often helps with debugging and understanding what is actually transpiring behind the scenes. I was recently looking through some of my old code and ran across some code I wrote in 2002 to better understand how SMTP worked. The goal wasn't to write a new library or find a way of sending email through Java code – it was simply to understand how it worked. To date, I can’t remember ever using this in any real applications, though it was hugely helpful in understanding the internal workings of SMTP.

The entire application is available for download here, but the meat of the code is below:

 
/**
	 * Sends an email. Returns true if the mail send was successful, otherwise it returns false.
	 * @return boolean
	 */
	public boolean send() {

		logEnter("send()");

		StringBuffer msg = new StringBuffer();
		int result = -1;
		boolean recipientsAdded = false;

		// Although I could not find any direct mention of a date format,
		// all examples in RFC-821 are in the 2 Nov 81 22:33:44 format.
		SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yy HH:mm:ss");

		//Make sure we have all
		//the required information
		if (!validateInfo()) {
			return false;
		}

		//Get a connection to the SMTP server
		if (!connect()) {
			status = "Unable to get a connection to the specified SMTP server.";
			return false;
		}

		//Get acquainted. You should always start a polite conversation with "HEL[L]O".
		msg.append("HELO " + this.helloDomain);
		result = sendData(msg);
		msg.delete(0, msg.length());

		if (result != 250 && result != 251) {
			status = "Unable to get a connection to the specified SMTP server.";
			return false;
		}

		//add the sender
		msg.append("MAIL FROM:<" + this.fromAddress + ">");
		result = sendData(msg);
		msg.delete(0, msg.length());

		if (result != 250) {
			status = "Invalid from email address.";
			return false;
		}

		//add the recipients
		StringBuffer sbSendTo = new StringBuffer();

		for (int i = 0;i < recipients.size();i++) {

			Recipient r = (Recipient) recipients.get(i);

			String head = "";

			switch (r.getType()) {

				case Recipient.TO:
					head = "TO";
					break;

				case Recipient.CC:
					head = "CC";
					break;

				case Recipient.BCC:
					head = "BCC";
					break;
			}

			if (!head.equals("BCC")) {
				sbSendTo.append(head + ": " + r.getName() + "<" + r.getAddress() + ">" + lineBreak);
			}

			msg.append("RCPT TO:<" + r.getAddress() + ">");
			result = sendData(msg);
			msg.delete(0, msg.length());

			if (result == 250) {
				recipientsAdded = true;
			}
		}

		//Make sure some recipients were added
		if (!recipientsAdded) {
			status = "No valid recipients.";
			return false;
		}

		//Get ready to send the body of the message
		msg.append("DATA");
		result = sendData(msg);
		msg.delete(0, msg.length());

		// Build the body of the message. This gets into the RFC spec for multi-part mime.
		// I could explain it here but it would be easier to just read the RFC yourself.
		// A basic version is that we'll break up content by mime boundaries so that the
		// mail client reading the message can display things in the appropriate way.

		msg.append("MIME-Version: 1.0" + lineBreak);
		msg.append("Content-Type: multipart/mixed;boundary=\"--NextMimePart\"" + lineBreak);
		msg.append("DATE:" + sdf.format(new java.util.Date()) + lineBreak);

		msg.append("FROM:");

		if (this.fromName.length() > 0) {
			msg.append(this.fromName);
		}

		msg.append("<" + fromAddress + ">" + lineBreak);
		msg.append(sbSendTo.toString());

		msg.append("SUBJECT:" + this.subject + lineBreak + lineBreak);
		msg.append("----NextMimePart" + lineBreak);
		msg.append("Content-Type: multipart/alternative; boundary=\"--NextMimePartHTML\"" + lineBreak);
		msg.append(lineBreak);

		//add the Plain Text Body
		if (this.ptext != null && this.ptext.length() > 0) {
			msg.append("----NextMimePartHTML" + lineBreak);
			msg.append("Content-Type: text/plain; charset=\"us-ascii\"" + lineBreak);
			msg.append("Content-Transfer-Encoding: 7bit" + lineBreak);
			msg.append(lineBreak);
			msg.append(this.ptext.replace(
							     lineBreak + "." + lineBreak,
							     lineBreak + ".." + lineBreak) + lineBreak);
		}

		//add the HTML Body
		if (this.htext != null && this.htext.length() > 0) {
			msg.append("----NextMimePartHTML" + lineBreak);
			msg.append("Content-Type: text/html; charset=\"us-ascii\"" + lineBreak);
			msg.append("Content-Transfer-Encoding: quoted -printable" + lineBreak);
			msg.append(lineBreak);

			msg.append(this.htext.replace(
								 lineBreak + "." + lineBreak,
								 lineBreak + ".." + lineBreak) + lineBreak);
		}

		//add any attachments
		if (attachments != null && attachments.size() > 0) {

			Base64 base = new Base64();

			msg.append("----NextMimePartHTML--" + lineBreak);
			msg.append(lineBreak);

			for (int i = 0;i < attachments.size();i++) {

				Attachment a = (Attachment) attachments.get(i);

				msg.append("----NextMimePart" + lineBreak);
				//msg.append("Content-Type: application/octet-stream";
				msg.append("Content-Type: application/octet-stream; Name=\"" + a.getFile() + "\"" + lineBreak);
				msg.append("Content-Transfer-Encoding: Base64" + lineBreak);
				msg.append("Content-Disposition: attachment; FileName=\"" + a.getFile() + "\"" + lineBreak);
				msg.append(lineBreak);
				msg.append(base.encodeFromFile(a) + lineBreak);
			}

		}

		//Signifies the end of the email
		msg.append(lineBreak + "." + lineBreak);
		result = sendData(msg);
		msg.delete(0, msg.length());

		if (result == 250) {
			status = "Mail sent successfully.";
		} else {
			status = "Unable to deliver mail. Check SMTP logs.";
			return false;
		}

		//disconnect from the SMTP server
		disconnect();

		logInfo("### SEND ####");
		logExit("send()");
		return true;
	}

Topics: