| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 | <?php/** * All helpder functions for this plugin can be found here *//** * Sends out a full HTML mail * * @param array $options In the format: *     to => STR|ARR of recipients in RFC-2822 format (http://www.faqs.org/rfcs/rfc2822.html) *     from => STR of senden in RFC-2822 format (http://www.faqs.org/rfcs/rfc2822.html) *     subject => STR with the subject of the message *     body => STR with the message body *     plaintext_message STR with the plaintext version of the message *     html_message => STR with the HTML version of the message *     cc => NULL|STR|ARR of CC recipients in RFC-2822 format (http://www.faqs.org/rfcs/rfc2822.html) *     bcc => NULL|STR|ARR of BCC recipients in RFC-2822 format (http://www.faqs.org/rfcs/rfc2822.html) *     date => NULL|UNIX timestamp with the date the message was created *     attachments => NULL|ARR of array(array('mimetype', 'filename', 'content')) * * @return bool */function html_email_handler_send_email(array $options = null) {	static $limit_subject;		$site = elgg_get_site_entity();		// make site email	$site_from = html_email_handler_make_rfc822_address($site);		// get sendmail options	$sendmail_options = html_email_handler_get_sendmail_options();		if (!isset($limit_subject)) {		$limit_subject = false;				if (elgg_get_plugin_setting("limit_subject", "html_email_handler") == "yes") {			$limit_subject = true;		}	}		// set default options	$default_options = array(		"to" => array(),		"from" => $site_from,		"subject" => "",		"html_message" => "",		"plaintext_message" => "",		"cc" => array(),		"bcc" => array(),		"date" => null,	);		// merge options	$options = array_merge($default_options, $options);		// redo to/from for notifications	$notification = elgg_extract('notification', $options);	if (!empty($notification) && ($notification instanceof \Elgg\Notifications\Notification)) {		$recipient = $notification->getRecipient();		$sender = $notification->getSender();				$options['to'] = html_email_handler_make_rfc822_address($recipient);		if (!isset($options['recipient'])) {			$options['recipient'] = $recipient;		}				if (!($sender instanceof \ElggUser) && $sender->email) {			$options['from'] = html_email_handler_make_rfc822_address($sender);		} else {			$options['from'] = $site_from;		}	}		// check options	if (!empty($options["to"]) && !is_array($options["to"])) {		$options["to"] = array($options["to"]);	}	if (!empty($options["cc"]) && !is_array($options["cc"])) {		$options["cc"] = array($options["cc"]);	}	if (!empty($options["bcc"]) && !is_array($options["bcc"])) {		$options["bcc"] = array($options["bcc"]);	}		if (empty($options['html_message']) && empty($options['plaintext_message'])) {		$options['html_message'] = html_email_handler_make_html_body($options);		$options['plaintext_message'] = $options['body'];	}		// can we send a message	if (empty($options["to"]) || (empty($options["html_message"]) && empty($options["plaintext_message"]))) {		return false;	}		// start preparing	// Facyla : better without spaces and special chars	//$boundary = uniqid($site->name);	$boundary = uniqid(elgg_get_friendly_title($site->name));		$headers = $options['headers'];		// start building headers	if (!empty($options["from"])) {		$headers['From'] = $options['from'];	} else {		$headers['From'] = $site_from;	}		// check CC mail	if (!empty($options["cc"])) {		$headers['Cc'] = implode(', ', $options['cc']);	}		// check BCC mail	if (!empty($options["bcc"])) {		$headers['Bcc'] = implode(', ', $options['bcc']);	}		// add a date header	if (!empty($options["date"])) {		$headers['Date'] = date('r', $options['date']);	}		$headers['X-Mailer'] = ' PHP/' . phpversion();	$headers['MIME-Version'] = '1.0';		// Facyla : try to add attchments if set	$attachments = "";	// Allow to add single or multiple attachments	if (!empty($options["attachments"])) {				$attachment_counter = 0;		foreach ($options["attachments"] as $attachment) {						// Alternatively fetch content based on an absolute path to a file on server:			if (empty($attachment["content"]) && !empty($attachment["filepath"])) {				$attachment["content"] = chunk_split(base64_encode(file_get_contents($attachment["filepath"])));			}						// Cannot attach an empty file in any case..			if (empty($attachment["content"])) {				continue;			}						// Count valid attachments			$attachment_counter++;						// Use defaults for other less critical settings			if (empty($attachment["mimetype"])) {				$attachment["mimetype"] = "application/octet-stream";			}			if (empty($attachment["filename"])) {				$attachment["filename"] = "file_" . $attachment_counter;			}						$attachments .= "Content-Type: {" . $attachment["mimetype"] . "};" . PHP_EOL . " name=\"" . $attachment["filename"] . "\"" . PHP_EOL;			$attachments .= "Content-Disposition: attachment;" . PHP_EOL . " filename=\"" . $attachment["filename"] . "\"" . PHP_EOL;			$attachments .= "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL;			$attachments .= $attachment["content"] . PHP_EOL . PHP_EOL;			$attachments .= "--mixed--" . $boundary . PHP_EOL;		}	}		// Use attachments headers for real only if they are valid	if (!empty($attachments)) {		$headers['Content-Type'] = "multipart/mixed; boundary=\"mixed--{$boundary}\"";	} else {		$headers['Content-Type'] = "multipart/alternative; boundary=\"{$boundary}\"";	}		$header_eol = "\r\n";	if (elgg_get_config('broken_mta')) {		// Allow non-RFC 2822 mail headers to support some broken MTAs		$header_eol = "\n";	}		// stringify headers	$headers_string = '';	foreach ($headers as $key => $value) {		$headers_string .= "$key: $value{$header_eol}";	}		// start building the message	$message = "";		// TEXT part of message	$plaintext_message = elgg_extract("plaintext_message", $options);	if (!empty($plaintext_message)) {		// normalize URL's in the text		$plaintext_message = html_email_handler_normalize_urls($plaintext_message);				// add boundry / content type		$message .= "--" . $boundary . PHP_EOL;		$message .= "Content-Type: text/plain; charset=\"utf-8\"" . PHP_EOL;		$message .= "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL;				// add content		$message .= chunk_split(base64_encode($plaintext_message)) . PHP_EOL . PHP_EOL;	}		// HTML part of message	$html_message = elgg_extract("html_message", $options);	if (!empty($html_message)) {		$html_boundary = $boundary;				// normalize URL's in the text		$html_message = html_email_handler_normalize_urls($html_message);		$html_message = html_email_handler_base64_encode_images($html_message);				$image_attachments = html_email_handler_attach_images($html_message);		if (is_array($image_attachments)) {			$html_boundary .= "-alt";			$html_message = elgg_extract("text", $image_attachments);						$message .= "--" . $boundary . PHP_EOL;			$message .= "Content-Type: multipart/related; boundary=\"$html_boundary\"" . PHP_EOL . PHP_EOL;		}				// add boundry / content type		$message .= "--" . $html_boundary . PHP_EOL;		$message .= "Content-Type: text/html; charset=\"utf-8\"" . PHP_EOL;		$message .= "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL;				// add content		$message .= chunk_split(base64_encode($html_message)) . PHP_EOL;				if (is_array($image_attachments)) {			$images = elgg_extract("images", $image_attachments);						foreach ($images as $image_info) {				$message .= "--" . $html_boundary . PHP_EOL;				$message .= "Content-Type: " . elgg_extract("content_type", $image_info) . "; charset=\"utf-8\"" . PHP_EOL;				$message .= "Content-Disposition: inline; filename=\"" . elgg_extract("name", $image_info) . "\"" . PHP_EOL;				$message .= "Content-ID: <" . elgg_extract("uid", $image_info) . ">" . PHP_EOL;				$message .= "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL;								// add content				$message .= chunk_split(elgg_extract("data", $image_info)) . PHP_EOL;			}						$message .= "--" . $html_boundary . "--" . PHP_EOL;		}	}		// Final boundry	$message .= "--" . $boundary . "--" . PHP_EOL;		// Facyla : FILE part of message	if (!empty($attachments)) {		// Build strings that will be added before TEXT/HTML message		$before_message = "--mixed--" . $boundary . PHP_EOL;		$before_message .= "Content-Type: multipart/alternative; boundary=\"" . $boundary . "\"" . PHP_EOL . PHP_EOL;				// Build strings that will be added after TEXT/HTML message		$after_message = PHP_EOL;		$after_message .= "--mixed--" . $boundary . PHP_EOL;		$after_message .= $attachments;				// Wrap TEXT/HTML message into mixed message content		$message = $before_message . PHP_EOL . $message . PHP_EOL . $after_message;	}		// convert to to correct format	$to = implode(", ", $options["to"]);		// encode subject to handle special chars	$subject = $options["subject"];	$subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8'); // Decode any html entities	if ($limit_subject) {		$subject = elgg_get_excerpt($subject, 175);	}	$subject = "=?UTF-8?B?" . base64_encode($subject) . "?=";		return mail($to, $subject, $message, $headers_string, $sendmail_options);}/** * This function converts CSS to inline style, the CSS needs to be found in a <style> element * * @param string $html_text the html text to be converted * * @return false|string */function html_email_handler_css_inliner($html_text) {	$result = false;		if (!empty($html_text) && defined("XML_DOCUMENT_NODE")) {		$css = "";				// set custom error handling		libxml_use_internal_errors(true);				$dom = new DOMDocument();		$dom->loadHTML($html_text);				$styles = $dom->getElementsByTagName("style");				if (!empty($styles)) {			$style_count = $styles->length;						for ($i = 0; $i < $style_count; $i++) {				$css .= $styles->item($i)->nodeValue;			}		}				// clear error log		libxml_clear_errors();				$emo = new Pelago\Emogrifier($html_text, $css);		$result = $emo->emogrify();	}		return $result;}/** * Make the HTML body from a $options array * * @param array  $options the options * @param string $body    the message body * * @return string */function html_email_handler_make_html_body($options = "", $body = "") {	global $CONFIG;		if (!is_array($options)) {		elgg_deprecated_notice("html_email_handler_make_html_body now takes an array as param, please update your code", "1.9");				$options = array(			"subject" => $options,			"body" => $body		);	}		$defaults = array(		"subject" => "",		"body" => "",		"language" => get_current_language()	);		$options = array_merge($defaults, $options);		$options['body'] = parse_urls($options['body']);		// in some cases when pagesetup isn't done yet this can cause problems	// so manualy set is to done	$unset = false;	if (!isset($CONFIG->pagesetupdone)) {		$unset = true;		$CONFIG->pagesetupdone = true;	}		// generate HTML mail body	$result = elgg_view("html_email_handler/notification/body", $options);		// do we need to restore pagesetup	if ($unset) {		unset($CONFIG->pagesetupdone);	}		if (defined("XML_DOCUMENT_NODE")) {		if ($transform = html_email_handler_css_inliner($result)) {			$result = $transform;		}	}		return $result;}/** * Get the plugin settings for sendmail * * @return string */function html_email_handler_get_sendmail_options() {	static $result;		if (!isset($result)) {		$result = "";				$setting = elgg_get_plugin_setting("sendmail_options", "html_email_handler");		if (!empty($setting)) {			$result = $setting;		}	}		return $result;}/** * This function build an RFC822 compliant address * * This function requires the option 'entity' * * @param ElggEntity $entity       entity to use as the basis for the address * @param bool       $use_fallback provides a fallback email if none defined * * @return string the correctly formatted address */function html_email_handler_make_rfc822_address(ElggEntity $entity, $use_fallback = true) {	// get the email address of the entity	$email = $entity->email;	if (empty($email) && $use_fallback) {		// no email found, fallback to site email		$site = elgg_get_site_entity();				$email = $site->email;		if (empty($email)) {			// no site email, default to noreply			$email = "noreply@" . $site->getDomain();		}	}		// build the RFC822 format	if (!empty($entity->name)) {		$name = $entity->name;		if (strstr($name, ",")) {			$name = '"' . $name . '"'; // Protect the name with quotations if it contains a comma		}				$name = "=?UTF-8?B?" . base64_encode($name) . "?="; // Encode the name. If may content non ASCII chars.		$email = $name . " <" . $email . ">";	}		return $email;}/** * Normalize all URL's in the text to full URL's * * @param string $text the text to check for URL's * * @return string */function html_email_handler_normalize_urls($text) {	static $pattern = '/\s(?:href|src)=([\'"]\S+[\'"])/i';		if (empty($text)) {		return $text;	}		// find all matches	$matches = array();	preg_match_all($pattern, $text, $matches);		if (empty($matches) || !isset($matches[1])) {		return $text;	}		// go through all the matches	$urls = $matches[1];	$urls = array_unique($urls);		foreach ($urls as $url) {		// remove wrapping quotes from the url		$real_url = substr($url, 1, -1);		// normalize url		$new_url = elgg_normalize_url($real_url);		// make the correct replacement string		$replacement = str_replace($real_url, $new_url, $url);			// replace the url in the content		$text = str_replace($url, $replacement, $text);	}		return $text;}/** * Convert images to inline images * * This can be enabled with a plugin setting (default: off) * * @param string $text the text of the message to embed the images from * * @return string */function html_email_handler_base64_encode_images($text) {	static $plugin_setting;		if (empty($text)) {		return $text;	}		if (!isset($plugin_setting)) {		$plugin_setting = false;				if (elgg_get_plugin_setting("embed_images", "html_email_handler", "no") === "base64") {			$plugin_setting = true;		}	}		if (!$plugin_setting) {		return $text;	}		$image_urls = html_email_handler_find_images($text);	if (empty($image_urls)) {		return $text;	}		foreach ($image_urls as $url) {		// remove wrapping quotes from the url		$image_url = substr($url, 1, -1);				// get the image contents		$contents = html_email_handler_get_image($image_url);		if (empty($contents)) {			continue;		}				// build inline image		$replacement = str_replace($image_url, "data:" . $contents, $url);				// replace in text		$text = str_replace($url, $replacement, $text);	}		return $text;}/** * Get the contents of an image url for embedding * * @param string $image_url the URL of the image * * @return false|string */function html_email_handler_get_image($image_url) {	static $proxy_host;	static $proxy_port;	static $session_cookie;	static $cache_dir;		if (empty($image_url)) {		return false;	}	$image_url = htmlspecialchars_decode($image_url);	$image_url = elgg_normalize_url($image_url);		// check cache	if (!isset($cache_dir)) {		$cache_dir = elgg_get_config("dataroot") . "html_email_handler/image_cache/";		if (!is_dir($cache_dir)) {			mkdir($cache_dir, "0755", true);		}	}		$cache_file = md5($image_url);	if (file_exists($cache_dir . $cache_file)) {		return file_get_contents($cache_dir . $cache_file);	}		// build cURL options	$ch = curl_init($image_url);		curl_setopt($ch, CURLOPT_HEADER, false);	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);	curl_setopt($ch, CURLOPT_TIMEOUT, 5);		// set proxy settings	if (!isset($proxy_host)) {		$proxy_host = false;				$setting = elgg_get_plugin_setting("proxy_host", "html_email_handler");		if (!empty($setting)) {			$proxy_host = $setting;		}	}		if ($proxy_host) {		curl_setopt($ch, CURLOPT_PROXY, $proxy_host);	}		if (!isset($proxy_port)) {		$proxy_port = false;				$setting = (int) elgg_get_plugin_setting("proxy_port", "html_email_handler");		if ($setting > 0) {			$proxy_port = $setting;		}	}		if ($proxy_port) {		curl_setopt($ch, CURLOPT_PROXYPORT, $proxy_port);	}		// check if local url, so we can send Elgg cookies	if (strpos($image_url, elgg_get_site_url()) !== false) {		if (!isset($session_cookie)) {			$session_cookie = false;						$cookie_settings = elgg_get_config("cookie");			if (!empty($cookie_settings)) {				$cookie_name = elgg_extract("name", $cookie_settings["session"]);								$session_cookie = $cookie_name . "=" . session_id();			}		}				if ($session_cookie) {			curl_setopt($ch, CURLOPT_COOKIE, $session_cookie);		}	}		// get the image	$contents = curl_exec($ch);	$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);	$http_code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);		curl_close($ch);		if (empty($contents) || ($http_code !== 200)) {		return false;	}		// build a valid uri	// https://en.wikipedia.org/wiki/Data_URI_scheme	$base64_result = $content_type . ";charset=UTF-8;base64," . base64_encode($contents);		// write to cache	file_put_contents($cache_dir . $cache_file, $base64_result);		// return result	return $base64_result;}/** * Find img src's in text * * @param string $text the text to search though * * @return false|array */function html_email_handler_find_images($text) {	static $pattern = '/\ssrc=([\'"]\S+[\'"])/i';		if (empty($text)) {		return false;	}		// find all matches	$matches = array();	preg_match_all($pattern, $text, $matches);		if (empty($matches) || !isset($matches[1])) {		return false;	}		// return all the found image urls	return array_unique($matches[1]);}/** * Get information needed for attaching the images to the e-mail * * @param string $text the html text to search images in * * @return string|array */function html_email_handler_attach_images($text) {	static $plugin_setting;		if (empty($text)) {		return $text;	}		// get plugin setting for replacement	if (!isset($plugin_setting)) {		$plugin_setting = false;			if (elgg_get_plugin_setting("embed_images", "html_email_handler", "no") === "attach") {			$plugin_setting = true;		}	}		// check plugin setting	if (!$plugin_setting) {		return $text;	}		// get images	$image_urls = html_email_handler_find_images($text);	if (empty($image_urls)) {		return $text;	}		$result = array(		"images" => array()	);		foreach ($image_urls as $url) {		// remove wrapping quotes from the url		$image_url = substr($url, 1, -1);				// get the image contents		$contents = html_email_handler_get_image($image_url);		if (empty($contents)) {			continue;		}				// make different parts of the result		list($content_type, $data) = explode(";charset=UTF-8;base64,", $contents);				// Unique ID		$uid = uniqid();				$result["images"][] = array(			"uid" => $uid,			"content_type" => $content_type,			"data" => $data,			"name" => basename($image_url)		);				// replace url in the text with uid		$replacement = str_replace($image_url, "cid:" . $uid, $url);				$text = str_replace($url, $replacement, $text);	}		// return new text	$result["text"] = $text;		// return result	return $result;}
 |