Browse Source

create mails with blob attachments (embedded or attached)

euromark 13 years ago
parent
commit
61de8749dc
2 changed files with 392 additions and 152 deletions
  1. 351 152
      Lib/EmailLib.php
  2. 41 0
      Test/Case/Lib/EmailLibTest.php

+ 351 - 152
Lib/EmailLib.php

@@ -12,7 +12,7 @@ if (!defined('BR')) {
  * - allow setting domain for CLI environment
  * - enable easier attachment adding
  * - extensive logging and error tracing
- * 
+ * - create mails with blob attachments (embedded or attached)
  * TODO: cleanup and more tests
  * 
  * @author Mark Scherer
@@ -95,7 +95,6 @@ class EmailLib extends CakeEmail {
 	}
 
 
-
 	public function layout($layout = false) {
 		if ($layout !== false) {
 			$this->_layout = $layout;
@@ -116,8 +115,50 @@ class EmailLib extends CakeEmail {
 	}
 	
 	/**
+	 * @param binary $content: blob data
+	 * @param string $filename to attach it
+	 * @param string $mimeType (leave it empty to get mimetype from $filename)
+	 * @param string $contentId (optional)
+	 * @return mixed ressource $EmailLib or string $contentId
+	 * 2011-11-02 ms
+	 */
+	public function addBlobAttachment($content, $name, $mimeType = null) {
+		$fileInfo = array();
+		$fileInfo['content'] = $content;
+		$fileInfo['mimetype'] = $mimeType;
+		$file = array($name=>$fileInfo);
+		$res = $this->addAttachments($file);
+		if ($contentId === null) {
+			return $fileInfo['contentId'];
+		}
+		return $res;
+	}
+
+	/**
+	 * @param binary $content: blob data
+	 * @param string $filename to attach it
+	 * @param string $mimeType (leave it empty to get mimetype from $filename)
+	 * @param string $contentId (optional)
+	 * @return mixed ressource $EmailLib or string $contentId
+	 * 2011-11-02 ms
+	 */
+	public function addEmbeddedBlobAttachment($content, $name, $mimeType = null, $contentId = null) {
+		$fileInfo = array();
+		$fileInfo['content'] = $content;
+		$fileInfo['mimetype'] = $mimeType;
+		$fileInfo['contentId'] = $contentId ? $contentId : str_replace('-', '', String::uuid()) . '@' . $this->_domain;
+		$file = array($name=>$fileInfo);
+		$res = $this->addAttachments($file);
+		if ($contentId === null) {
+			return $fileInfo['contentId'];
+		}
+		return $res;
+	}
+	
+	/**
 	 * @param string $file: absolute path
 	 * @param string $filename (optional)
+	 * @param string $contentId (optional)
 	 * @return mixed ressource $EmailLib or string $contentId
 	 * 2011-11-02 ms
 	 */
@@ -145,158 +186,170 @@ class EmailLib extends CakeEmail {
 		} else {
 	 		//TODO: improve
 	 		$ext = pathinfo($filename, PATHINFO_EXTENSION);
-	 		switch ($ext) { 
-				case "zip": $mime="application/zip"; break; 
-				case "ez":  $mime="application/andrew-inset"; break; 
-				case "hqx": $mime="application/mac-binhex40"; break; 
-				case "cpt": $mime="application/mac-compactpro"; break; 
-				case "doc": $mime="application/msword"; break; 
-				case "bin": $mime="application/octet-stream"; break; 
-				case "dms": $mime="application/octet-stream"; break; 
-				case "lha": $mime="application/octet-stream"; break; 
-				case "lzh": $mime="application/octet-stream"; break; 
-				case "exe": $mime="application/octet-stream"; break; 
-				case "class": $mime="application/octet-stream"; break; 
-				case "so":  $mime="application/octet-stream"; break; 
-				case "dll": $mime="application/octet-stream"; break; 
-				case "oda": $mime="application/oda"; break; 
-				case "pdf": $mime="application/pdf"; break; 
-				case "ai":  $mime="application/postscript"; break; 
-				case "eps": $mime="application/postscript"; break; 
-				case "ps":  $mime="application/postscript"; break; 
-				case "smi": $mime="application/smil"; break; 
-				case "smil": $mime="application/smil"; break; 
-				case "xls": $mime="application/vnd.ms-excel"; break; 
-				case "ppt": $mime="application/vnd.ms-powerpoint"; break; 
-				case "wbxml": $mime="application/vnd.wap.wbxml"; break; 
-				case "wmlc": $mime="application/vnd.wap.wmlc"; break; 
-				case "wmlsc": $mime="application/vnd.wap.wmlscriptc"; break; 
-				case "bcpio": $mime="application/x-bcpio"; break; 
-				case "vcd": $mime="application/x-cdlink"; break; 
-				case "pgn": $mime="application/x-chess-pgn"; break; 
-				case "cpio": $mime="application/x-cpio"; break; 
-				case "csh": $mime="application/x-csh"; break; 
-				case "dcr": $mime="application/x-director"; break; 
-				case "dir": $mime="application/x-director"; break; 
-				case "dxr": $mime="application/x-director"; break; 
-				case "dvi": $mime="application/x-dvi"; break; 
-				case "spl": $mime="application/x-futuresplash"; break; 
-				case "gtar": $mime="application/x-gtar"; break; 
-				case "hdf": $mime="application/x-hdf"; break; 
-				case "js":  $mime="application/x-javascript"; break; 
-				case "skp": $mime="application/x-koan"; break; 
-				case "skd": $mime="application/x-koan"; break; 
-				case "skt": $mime="application/x-koan"; break; 
-				case "skm": $mime="application/x-koan"; break; 
-				case "latex": $mime="application/x-latex"; break; 
-				case "nc":  $mime="application/x-netcdf"; break; 
-				case "cdf": $mime="application/x-netcdf"; break; 
-				case "sh":  $mime="application/x-sh"; break; 
-				case "shar": $mime="application/x-shar"; break; 
-				case "swf": $mime="application/x-shockwave-flash"; break; 
-				case "sit": $mime="application/x-stuffit"; break; 
-				case "sv4cpio": $mime="application/x-sv4cpio"; break; 
-				case "sv4crc": $mime="application/x-sv4crc"; break; 
-				case "tar": $mime="application/x-tar"; break; 
-				case "tcl": $mime="application/x-tcl"; break; 
-				case "tex": $mime="application/x-tex"; break; 
-				case "texinfo": $mime="application/x-texinfo"; break; 
-				case "texi": $mime="application/x-texinfo"; break; 
-				case "t":   $mime="application/x-troff"; break; 
-				case "tr":  $mime="application/x-troff"; break; 
-				case "roff": $mime="application/x-troff"; break; 
-				case "man": $mime="application/x-troff-man"; break; 
-				case "me":  $mime="application/x-troff-me"; break; 
-				case "ms":  $mime="application/x-troff-ms"; break; 
-				case "ustar": $mime="application/x-ustar"; break; 
-				case "src": $mime="application/x-wais-source"; break; 
-				case "xhtml": $mime="application/xhtml+xml"; break; 
-				case "xht": $mime="application/xhtml+xml"; break; 
-				case "zip": $mime="application/zip"; break; 
-				case "au":  $mime="audio/basic"; break; 
-				case "snd": $mime="audio/basic"; break; 
-				case "mid": $mime="audio/midi"; break; 
-				case "midi": $mime="audio/midi"; break; 
-				case "kar": $mime="audio/midi"; break; 
-				case "mpga": $mime="audio/mpeg"; break; 
-				case "mp2": $mime="audio/mpeg"; break; 
-				case "mp3": $mime="audio/mpeg"; break; 
-				case "aif": $mime="audio/x-aiff"; break; 
-				case "aiff": $mime="audio/x-aiff"; break; 
-				case "aifc": $mime="audio/x-aiff"; break; 
-				case "m3u": $mime="audio/x-mpegurl"; break; 
-				case "ram": $mime="audio/x-pn-realaudio"; break; 
-				case "rm":  $mime="audio/x-pn-realaudio"; break; 
-				case "rpm": $mime="audio/x-pn-realaudio-plugin"; break; 
-				case "ra":  $mime="audio/x-realaudio"; break; 
-				case "wav": $mime="audio/x-wav"; break; 
-				case "pdb": $mime="chemical/x-pdb"; break; 
-				case "xyz": $mime="chemical/x-xyz"; break; 
-				case "bmp": $mime="image/bmp"; break; 
-				case "gif": $mime="image/gif"; break; 
-				case "ief": $mime="image/ief"; break; 
-				case "jpeg": $mime="image/jpeg"; break; 
-				case "jpg": $mime="image/jpeg"; break; 
-				case "jpe": $mime="image/jpeg"; break; 
-				case "png": $mime="image/png"; break; 
-				case "tiff": $mime="image/tiff"; break; 
-				case "tif": $mime="image/tiff"; break; 
-				case "djvu": $mime="image/vnd.djvu"; break; 
-				case "djv": $mime="image/vnd.djvu"; break; 
-				case "wbmp": $mime="image/vnd.wap.wbmp"; break; 
-				case "ras": $mime="image/x-cmu-raster"; break; 
-				case "pnm": $mime="image/x-portable-anymap"; break; 
-				case "pbm": $mime="image/x-portable-bitmap"; break; 
-				case "pgm": $mime="image/x-portable-graymap"; break; 
-				case "ppm": $mime="image/x-portable-pixmap"; break; 
-				case "rgb": $mime="image/x-rgb"; break; 
-				case "xbm": $mime="image/x-xbitmap"; break; 
-				case "xpm": $mime="image/x-xpixmap"; break; 
-				case "xwd": $mime="image/x-xwindowdump"; break; 
-				case "igs": $mime="model/iges"; break; 
-				case "iges": $mime="model/iges"; break; 
-				case "msh": $mime="model/mesh"; break; 
-				case "mesh": $mime="model/mesh"; break; 
-				case "silo": $mime="model/mesh"; break; 
-				case "wrl": $mime="model/vrml"; break; 
-				case "vrml": $mime="model/vrml"; break; 
-				case "css": $mime="text/css"; break; 
-				case "html": $mime="text/html"; break; 
-				case "htm": $mime="text/html"; break; 
-				case "asc": $mime="text/plain"; break; 
-				case "txt": $mime="text/plain"; break; 
-				case "rtx": $mime="text/richtext"; break; 
-				case "rtf": $mime="text/rtf"; break; 
-				case "sgml": $mime="text/sgml"; break; 
-				case "sgm": $mime="text/sgml"; break; 
-				case "tsv": $mime="text/tab-separated-values"; break; 
-				case "wml": $mime="text/vnd.wap.wml"; break; 
-				case "wmls": $mime="text/vnd.wap.wmlscript"; break; 
-				case "etx": $mime="text/x-setext"; break; 
-				case "xml": $mime="text/xml"; break; 
-				case "xsl": $mime="text/xml"; break; 
-				case "mpeg": $mime="video/mpeg"; break; 
-				case "mpg": $mime="video/mpeg"; break; 
-				case "mpe": $mime="video/mpeg"; break; 
-				case "qt":  $mime="video/quicktime"; break; 
-				case "mov": $mime="video/quicktime"; break; 
-				case "mxu": $mime="video/vnd.mpegurl"; break; 
-				case "avi": $mime="video/x-msvideo"; break; 
-				case "movie": $mime="video/x-sgi-movie"; break; 
-				case "asf": $mime="video/x-ms-asf"; break; 
-				case "asx": $mime="video/x-ms-asf"; break; 
-				case "wm":  $mime="video/x-ms-wm"; break; 
-				case "wmv": $mime="video/x-ms-wmv"; break; 
-				case "wvx": $mime="video/x-ms-wvx"; break; 
-				case "ice": $mime="x-conference/x-cooltalk"; break; 
-			}
-			if (empty($mime)) {
-				$mime = 'application/octet-stream';
-			}
-			$mimetype = $mime;
+			$mimetype = $this->_getMimeByExtension($ext);
 		}
 		return $mimetype;
 	}
+	
+	/**
+	 * try to find mimetype by file extension
+	 * @param string $ext lowercase (jpg, png, pdf, ...)
+	 * @param string $defaultMimeType
+	 * @return string $mimeType (falls back to )
+	 * 2012-04-17 ms
+	 */
+	protected function _getMimeByExtension($ext, $default = 'application/octet-stream') {
+		switch ($ext) {
+			case "zip": $mime="application/zip"; break; 
+			case "ez":  $mime="application/andrew-inset"; break; 
+			case "hqx": $mime="application/mac-binhex40"; break; 
+			case "cpt": $mime="application/mac-compactpro"; break; 
+			case "doc": $mime="application/msword"; break; 
+			case "bin": $mime="application/octet-stream"; break; 
+			case "dms": $mime="application/octet-stream"; break; 
+			case "lha": $mime="application/octet-stream"; break; 
+			case "lzh": $mime="application/octet-stream"; break; 
+			case "exe": $mime="application/octet-stream"; break; 
+			case "class": $mime="application/octet-stream"; break; 
+			case "so":  $mime="application/octet-stream"; break; 
+			case "dll": $mime="application/octet-stream"; break; 
+			case "oda": $mime="application/oda"; break; 
+			case "pdf": $mime="application/pdf"; break; 
+			case "ai":  $mime="application/postscript"; break; 
+			case "eps": $mime="application/postscript"; break; 
+			case "ps":  $mime="application/postscript"; break; 
+			case "smi": $mime="application/smil"; break; 
+			case "smil": $mime="application/smil"; break; 
+			case "xls": $mime="application/vnd.ms-excel"; break; 
+			case "ppt": $mime="application/vnd.ms-powerpoint"; break; 
+			case "wbxml": $mime="application/vnd.wap.wbxml"; break; 
+			case "wmlc": $mime="application/vnd.wap.wmlc"; break; 
+			case "wmlsc": $mime="application/vnd.wap.wmlscriptc"; break; 
+			case "bcpio": $mime="application/x-bcpio"; break; 
+			case "vcd": $mime="application/x-cdlink"; break; 
+			case "pgn": $mime="application/x-chess-pgn"; break; 
+			case "cpio": $mime="application/x-cpio"; break; 
+			case "csh": $mime="application/x-csh"; break; 
+			case "dcr": $mime="application/x-director"; break; 
+			case "dir": $mime="application/x-director"; break; 
+			case "dxr": $mime="application/x-director"; break; 
+			case "dvi": $mime="application/x-dvi"; break; 
+			case "spl": $mime="application/x-futuresplash"; break; 
+			case "gtar": $mime="application/x-gtar"; break; 
+			case "hdf": $mime="application/x-hdf"; break; 
+			case "js":  $mime="application/x-javascript"; break; 
+			case "skp": $mime="application/x-koan"; break; 
+			case "skd": $mime="application/x-koan"; break; 
+			case "skt": $mime="application/x-koan"; break; 
+			case "skm": $mime="application/x-koan"; break; 
+			case "latex": $mime="application/x-latex"; break; 
+			case "nc":  $mime="application/x-netcdf"; break; 
+			case "cdf": $mime="application/x-netcdf"; break; 
+			case "sh":  $mime="application/x-sh"; break; 
+			case "shar": $mime="application/x-shar"; break; 
+			case "swf": $mime="application/x-shockwave-flash"; break; 
+			case "sit": $mime="application/x-stuffit"; break; 
+			case "sv4cpio": $mime="application/x-sv4cpio"; break; 
+			case "sv4crc": $mime="application/x-sv4crc"; break; 
+			case "tar": $mime="application/x-tar"; break; 
+			case "tcl": $mime="application/x-tcl"; break; 
+			case "tex": $mime="application/x-tex"; break; 
+			case "texinfo": $mime="application/x-texinfo"; break; 
+			case "texi": $mime="application/x-texinfo"; break; 
+			case "t":   $mime="application/x-troff"; break; 
+			case "tr":  $mime="application/x-troff"; break; 
+			case "roff": $mime="application/x-troff"; break; 
+			case "man": $mime="application/x-troff-man"; break; 
+			case "me":  $mime="application/x-troff-me"; break; 
+			case "ms":  $mime="application/x-troff-ms"; break; 
+			case "ustar": $mime="application/x-ustar"; break; 
+			case "src": $mime="application/x-wais-source"; break; 
+			case "xhtml": $mime="application/xhtml+xml"; break; 
+			case "xht": $mime="application/xhtml+xml"; break; 
+			case "zip": $mime="application/zip"; break; 
+			case "au":  $mime="audio/basic"; break; 
+			case "snd": $mime="audio/basic"; break; 
+			case "mid": $mime="audio/midi"; break; 
+			case "midi": $mime="audio/midi"; break; 
+			case "kar": $mime="audio/midi"; break; 
+			case "mpga": $mime="audio/mpeg"; break; 
+			case "mp2": $mime="audio/mpeg"; break; 
+			case "mp3": $mime="audio/mpeg"; break; 
+			case "aif": $mime="audio/x-aiff"; break; 
+			case "aiff": $mime="audio/x-aiff"; break; 
+			case "aifc": $mime="audio/x-aiff"; break; 
+			case "m3u": $mime="audio/x-mpegurl"; break; 
+			case "ram": $mime="audio/x-pn-realaudio"; break; 
+			case "rm":  $mime="audio/x-pn-realaudio"; break; 
+			case "rpm": $mime="audio/x-pn-realaudio-plugin"; break; 
+			case "ra":  $mime="audio/x-realaudio"; break; 
+			case "wav": $mime="audio/x-wav"; break; 
+			case "pdb": $mime="chemical/x-pdb"; break; 
+			case "xyz": $mime="chemical/x-xyz"; break; 
+			case "bmp": $mime="image/bmp"; break; 
+			case "gif": $mime="image/gif"; break; 
+			case "ief": $mime="image/ief"; break; 
+			case "jpeg": $mime="image/jpeg"; break; 
+			case "jpg": $mime="image/jpeg"; break; 
+			case "jpe": $mime="image/jpeg"; break; 
+			case "png": $mime="image/png"; break; 
+			case "tiff": $mime="image/tiff"; break; 
+			case "tif": $mime="image/tiff"; break; 
+			case "djvu": $mime="image/vnd.djvu"; break; 
+			case "djv": $mime="image/vnd.djvu"; break; 
+			case "wbmp": $mime="image/vnd.wap.wbmp"; break; 
+			case "ras": $mime="image/x-cmu-raster"; break; 
+			case "pnm": $mime="image/x-portable-anymap"; break; 
+			case "pbm": $mime="image/x-portable-bitmap"; break; 
+			case "pgm": $mime="image/x-portable-graymap"; break; 
+			case "ppm": $mime="image/x-portable-pixmap"; break; 
+			case "rgb": $mime="image/x-rgb"; break; 
+			case "xbm": $mime="image/x-xbitmap"; break; 
+			case "xpm": $mime="image/x-xpixmap"; break; 
+			case "xwd": $mime="image/x-xwindowdump"; break; 
+			case "igs": $mime="model/iges"; break; 
+			case "iges": $mime="model/iges"; break; 
+			case "msh": $mime="model/mesh"; break; 
+			case "mesh": $mime="model/mesh"; break; 
+			case "silo": $mime="model/mesh"; break; 
+			case "wrl": $mime="model/vrml"; break; 
+			case "vrml": $mime="model/vrml"; break; 
+			case "css": $mime="text/css"; break; 
+			case "html": $mime="text/html"; break; 
+			case "htm": $mime="text/html"; break; 
+			case "asc": $mime="text/plain"; break; 
+			case "txt": $mime="text/plain"; break; 
+			case "rtx": $mime="text/richtext"; break; 
+			case "rtf": $mime="text/rtf"; break; 
+			case "sgml": $mime="text/sgml"; break; 
+			case "sgm": $mime="text/sgml"; break; 
+			case "tsv": $mime="text/tab-separated-values"; break; 
+			case "wml": $mime="text/vnd.wap.wml"; break; 
+			case "wmls": $mime="text/vnd.wap.wmlscript"; break; 
+			case "etx": $mime="text/x-setext"; break; 
+			case "xml": $mime="text/xml"; break; 
+			case "xsl": $mime="text/xml"; break; 
+			case "mpeg": $mime="video/mpeg"; break; 
+			case "mpg": $mime="video/mpeg"; break; 
+			case "mpe": $mime="video/mpeg"; break; 
+			case "qt":  $mime="video/quicktime"; break; 
+			case "mov": $mime="video/quicktime"; break; 
+			case "mxu": $mime="video/vnd.mpegurl"; break; 
+			case "avi": $mime="video/x-msvideo"; break; 
+			case "movie": $mime="video/x-sgi-movie"; break; 
+			case "asf": $mime="video/x-ms-asf"; break; 
+			case "asx": $mime="video/x-ms-asf"; break; 
+			case "wm":  $mime="video/x-ms-wm"; break; 
+			case "wmv": $mime="video/x-ms-wmv"; break; 
+			case "wvx": $mime="video/x-ms-wvx"; break; 
+			case "ice": $mime="x-conference/x-cooltalk"; break; 
+		}
+		if (empty($mime)) {
+			$mime = $default;
+		}
+		return $mime;
+	}
+	
 
 	public function preset($type = null) {
 		# testing only:
@@ -341,9 +394,155 @@ class EmailLib extends CakeEmail {
 		return $this;
 	}
 	
+
 	/**
-	 * Get list of headers
+	 * Attach inline/embedded files to the message.
+	 * @override
+	 * CUSTOM FIX: blob data support
+	 *
+	 * @param string $boundary Boundary to use. If null, will default to $this->_boundary 
+	 * @return array An array of lines to add to the message
+	 */
+	protected function _attachInlineFiles($boundary = null) {
+		if ($boundary === null) {
+			$boundary = $this->_boundary;
+		}
+
+		$msg = array();
+		foreach ($this->_attachments as $filename => $fileInfo) {
+			if (empty($fileInfo['contentId'])) {
+				continue;
+			}
+			if (!empty($fileInfo['content'])) {
+				$data = $fileInfo['content'];
+				$data = chunk_split(base64_encode($data));
+			} elseif (!empty($fileInfo['file'])) {
+				$data = $this->_readFile($fileInfo['file']);
+			} else {
+				continue;
+			}
+
+			$msg[] = '--' . $boundary;
+			$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
+			$msg[] = 'Content-Transfer-Encoding: base64';
+			$msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
+			$msg[] = 'Content-Disposition: inline; filename="' . $filename . '"';
+			$msg[] = '';
+			$msg[] = $data;
+			$msg[] = '';
+		}
+		return $msg;
+	}
+
+	/**
+	 * Attach non-embedded files by adding file contents inside boundaries.
+	 * @override
+	 * CUSTOM FIX: blob data support
+	 * 
+	 * @param string $boundary Boundary to use. If null, will default to $this->_boundary 
+	 * @return array An array of lines to add to the message
+	 */
+	protected function _attachFiles($boundary = null) {
+		if ($boundary === null) {
+			$boundary = $this->_boundary;
+		}
+
+		$msg = array();
+		foreach ($this->_attachments as $filename => $fileInfo) {
+			if (!empty($fileInfo['contentId'])) {
+				continue;
+			}
+			if (!empty($fileInfo['content'])) {
+				$data = $fileInfo['content'];
+			} elseif (!empty($fileInfo['file'])) {
+				$data = $this->_readFile($fileInfo['file']);
+				$data = chunk_split(base64_encode($data));
+			} else {
+				continue;
+			}
+
+			$msg[] = '--' . $boundary;
+			$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
+			$msg[] = 'Content-Transfer-Encoding: base64';
+			$msg[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
+			$msg[] = '';
+			$msg[] = $data;
+			$msg[] = '';
+		}
+		return $msg;
+	}
+
+	/**
+	 * Add attachments to the email message
+	 * @override
+	 * CUSTOM FIX: blob data support
+	 * 
+	 * Attachments can be defined in a few forms depending on how much control you need:
+	 *
+	 * Attach a single file:
 	 *
+	 * {{{
+	 * $email->attachments('path/to/file');
+	 * }}}
+	 *
+	 * Attach a file with a different filename:
+	 *
+	 * {{{
+	 * $email->attachments(array('custom_name.txt' => 'path/to/file.txt'));
+	 * }}}
+	 *
+	 * Attach a file and specify additional properties:
+	 *
+	 * {{{
+	 * $email->attachments(array('custom_name.png' => array(
+	 *		'file' => 'path/to/file',
+	 *		'mimetype' => 'image/png',
+	 *		'contentId' => 'abc123'
+	 * ));
+	 * }}}
+	 *
+	 * The `contentId` key allows you to specify an inline attachment. In your email text, you
+	 * can use `<img src="cid:abc123" />` to display the image inline.
+	 *
+	 * @param mixed $attachments String with the filename or array with filenames
+	 * @return mixed Either the array of attachments when getting or $this when setting.
+	 * @throws SocketException
+	 */
+	public function attachments($attachments = null) {
+		if ($attachments === null) {
+			return $this->_attachments;
+		}
+		$attach = array();
+		foreach ((array)$attachments as $name => $fileInfo) {
+			if (!is_array($fileInfo)) {
+				$fileInfo = array('file' => $fileInfo);
+			}
+			if (empty($fileInfo['content'])) {
+				if (!isset($fileInfo['file'])) {
+					throw new SocketException(__d('cake_dev', 'File not specified.'));
+				}
+				$fileInfo['file'] = realpath($fileInfo['file']);
+				if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) {
+					throw new SocketException(__d('cake_dev', 'File not found: "%s"', $fileInfo['file']));
+				}
+				if (is_int($name)) {
+					$name = basename($fileInfo['file']);
+				}
+			}
+			if (!isset($fileInfo['mimetype'])) {
+				$fileInfo['mimetype'] = 'application/octet-stream';
+			}
+			$attach[$name] = $fileInfo;
+		}
+		$this->_attachments = $attach;
+		return $this;
+	}
+	
+	/**
+	 * Get list of headers
+	 * @override
+	 * CUSTOM FIX: message id correctly set in CLI and can be passed in via domain()
+	 * 
 	 * ### Includes:
 	 *
 	 * - `from`

+ 41 - 0
Test/Case/Lib/EmailLibTest.php

@@ -122,6 +122,47 @@ html-part
 		$this->assertTrue($res);
 	}
 	
+	/**
+	 * html email
+	 */
+	public function testAddEmbeddedBlobAttachment() {
+		$file = CakePlugin::path('Tools').'Test'.DS.'test_files'.DS.'img'.DS.'hotel.png';
+		$this->assertTrue(file_exists($file));
+		
+		Configure::write('debug', 0);
+		$this->Email = new EmailLib();
+		$this->Email->emailFormat('both');
+		$this->Email->to(Configure::read('Config.admin_email'));
+		$cid = $this->Email->addEmbeddedBlobAttachment(file_get_contents($file), 'my_hotel.png', 'png');
+		
+		$this->assertContains('@'.env('HTTP_HOST'), $cid);
+
+			
+		$html = '<head>
+	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+	<meta name="author" content="ohyeah" />
+
+	<title>Untitled 6</title>
+</head>
+<body>
+test_embedded_blob_default äöü <img src="cid:'.$cid.'" /> end
+html-part
+</body>
+</html>';
+		$text = trim(strip_tags($html));	
+		$this->Email->viewVars(compact('text', 'html'));
+		
+		$res = $this->Email->send();
+		Configure::write('debug', 2);
+		if ($error = $this->Email->getError()) {
+			$this->out($error);
+		}
+		$this->assertEquals('', $this->Email->getError());
+		$this->assertTrue($res);
+	}
+		
+	
+	
 	public function _testComplexeHtmlWithEmbeddedImages() {
 		$file = CakePlugin::path('Tools').'Test'.DS.'test_files'.DS.'img'.DS.'hotel.png';
 		$this->assertTrue(file_exists($file));