/** * list images * * @param resource the SQL result * @return string the rendered text * * @see layouts/layout.php **/ function layout($result) { global $context; // empty list if (!SQL::count($result)) { $output = array(); return $output; } // we return an array of ($url => $attributes) $items = array(); // process all items in the list while ($item = SQL::fetch($result)) { // get the anchor for this image if ($item['anchor']) { $anchor = Anchors::get($item['anchor']); } // url to view the image $url = $context['url_to_home'] . $context['url_to_root'] . Images::get_url($item['id']); // time of last update $time = SQL::strtotime($item['edit_date']); // the title as the label if ($item['title']) { $label = ucfirst($item['title']) . ' (' . $item['image_name'] . ')'; } else { $label = $item['image_name']; } // the section $section = ''; if (is_object($anchor)) { $section = ucfirst($anchor->get_title()); } // the author(s) is an e-mail address, according to rss 2.0 spec $author = $item['create_address'] . ' (' . $item['create_name'] . ')'; if ($item['create_address'] != $item['edit_address']) { if ($author) { $author .= ', '; } $author .= $item['edit_address'] . ' (' . $item['edit_name'] . ')'; } // the description $description = Codes::beautify($item['description']); // cap the number of words $description = Skin::cap($description, 300); // fix image references $description = preg_replace('#"/([^">]+?)"#', '"' . $context['url_to_home'] . '/$1"', $description); $introduction = $description; // other rss fields $extensions = array(); // url for enclosure $type = Files::get_mime_type($item['image_name']); $extensions[] = '<enclosure url="' . $context['url_to_home'] . $context['url_to_root'] . Files::get_path($item['anchor'], 'images') . '/' . $item['image_name'] . '"' . ' length="' . $item['image_size'] . '"' . ' type="' . $type . '" />'; // list all components for this item $items[$url] = array($time, $label, $author, $section, NULL, $introduction, $description, $extensions); } // end of processing SQL::free($result); return $items; }
/** * embed an interactive object * * The id designates the target file. * It can also include width and height of the target canvas, as in: '12, 100%, 250px' * * @param string id of the target file * @return string the rendered string **/ public static function render_embed($id) { global $context; // split parameters $attributes = preg_split("/\\s*,\\s*/", $id, 4); $id = $attributes[0]; // get the file if (!($item = Files::get($id))) { $output = '[embed=' . $id . ']'; return $output; } // stream in a separate page if (isset($attributes[1]) && preg_match('/window/i', $attributes[1])) { if (!isset($attributes[2])) { $attributes[2] = i18n::s('Play in a separate window'); } $output = '<a href="' . $context['url_to_home'] . $context['url_to_root'] . Files::get_url($item['id'], 'stream', $item['file_name']) . '" onclick="window.open(this.href); return false;" class="button"><span>' . $attributes[2] . '</span></a>'; return $output; } // file extension $extension = strtolower(substr($item['file_name'], -3)); // set a default size if (!isset($attributes[1])) { if (!strcmp($extension, 'gan')) { $attributes[1] = '98%'; } elseif (!strcmp($extension, 'mm') && isset($context['skins_freemind_canvas_width'])) { $attributes[1] = $context['skins_freemind_canvas_width']; } else { $attributes[1] = 480; } } if (!isset($attributes[2])) { if (!strcmp($extension, 'gan')) { $attributes[2] = '300px'; } elseif (!strcmp($extension, 'mm') && isset($context['skins_freemind_canvas_height'])) { $attributes[2] = $context['skins_freemind_canvas_height']; } else { $attributes[2] = 360; } } // object attributes $width = $attributes[1]; $height = $attributes[2]; $flashvars = ''; if (isset($attributes[3])) { $flashvars = $attributes[3]; } // rendering depends on file extension switch ($extension) { // stream a video case '3gp': case 'flv': case 'm4v': case 'mov': case 'mp4': // a flash player to stream a flash video $flvplayer_url = $context['url_to_home'] . $context['url_to_root'] . 'included/browser/player_flv_maxi.swf'; // file is elsewhere if (isset($item['file_href']) && $item['file_href']) { $url = $item['file_href']; } else { $url = $context['url_to_home'] . $context['url_to_root'] . Files::get_url($item['id'], 'fetch', $item['file_name']); } // pass parameters to the player if ($flashvars) { $flashvars = str_replace('autostart=true', 'autoplay=1', $flashvars) . '&'; } $flashvars .= 'width=' . $width . '&height=' . $height; // if there is a static image for this video, use it if (isset($item['icon_url']) && $item['icon_url']) { $flashvars .= '&startimage=' . urlencode($item['icon_url']); } // if there is a subtitle file for this video, use it if (isset($item['file_name']) && ($srt = 'files/' . str_replace(':', '/', $item['anchor']) . '/' . str_replace('.' . $extension, '.srt', $item['file_name'])) && file_exists($context['path_to_root'] . $srt)) { $flashvars .= '&srt=1&srturl=' . urlencode($context['url_to_home'] . $context['url_to_root'] . $srt); } // if there is a logo file in the skin, use it Skin::define_img_href('FLV_IMG_HREF', 'codes/flvplayer_logo.png', ''); if (FLV_IMG_HREF) { $flashvars .= '&top1=' . urlencode(FLV_IMG_HREF . '|10|10'); } // rely on Flash if (Surfer::has_flash()) { // the full object is built in Javascript --see parameters at http://flv-player.net/players/maxi/documentation/ $output = '<div id="flv_' . $item['id'] . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n"; Page::insert_script('var flashvars = { flv:"' . $url . '", ' . str_replace(array('&', '='), array('", ', ':"'), $flashvars) . '", autoload:0, margin:1, showiconplay:1, playeralpha:50, iconplaybgalpha:30, showfullscreen:1, showloading:"always", ondoubleclick:"fullscreen" }' . "\n" . 'var params = { allowfullscreen: "true", allowscriptaccess: "always" }' . "\n" . 'var attributes = { id: "file_' . $item['id'] . '", name: "file_' . $item['id'] . '"}' . "\n" . 'swfobject.embedSWF("' . $flvplayer_url . '", "flv_' . $item['id'] . '", "' . $width . '", "' . $height . '", "9", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", flashvars, params);' . "\n"); // native support } else { // <video> is HTML5, <object> is legacy $output = '<video width="' . $width . '" height="' . $height . '" autoplay="" controls="" src="' . $url . '" >' . "\n" . ' <object width="' . $width . '" height="' . $height . '" data="' . $url . '" type="' . Files::get_mime_type($item['file_name']) . '">' . "\n" . ' <param value="' . $url . '" name="movie" />' . "\n" . ' <param value="true" name="allowFullScreen" />' . "\n" . ' <param value="always" name="allowscriptaccess" />' . "\n" . ' <a href="' . $url . '">No video playback capabilities, please download the file</a>' . "\n" . ' </object>' . "\n" . '</video>' . "\n"; } // job done return $output; // a ganttproject timeline // a ganttproject timeline case 'gan': // where the file is $path = Files::get_path($item['anchor']) . '/' . rawurlencode($item['file_name']); // we actually use a transformed version of the file $cache_id = Cache::hash($path) . '.xml'; // apply the transformation if (!file_exists($context['path_to_root'] . $cache_id) || filemtime($context['path_to_root'] . $cache_id) < filemtime($context['path_to_root'] . $path) || !($text = Safe::file_get_contents($context['path_to_root'] . $cache_id))) { // transform from GanttProject to SIMILE Timeline $text = Files::transform_gan_to_simile($path); // put in cache Safe::file_put_contents($cache_id, $text); } // load the SIMILE Timeline javascript library in shared/global.php $context['javascript']['timeline'] = TRUE; // cache would kill the loading of the library cache::poison(); // 1 week ago $now = gmdate('M d Y H:i:s', time() - 7 * 24 * 60 * 60); // load the right file $output = '<div id="gantt" style="height: ' . $height . '; width: ' . $width . '; border: 1px solid #aaa; font-family: Trebuchet MS, Helvetica, Arial, sans serif; font-size: 8pt"></div>' . "\n"; Page::insert_script('var simile_handle;' . "\n" . 'function onLoad() {' . "\n" . ' var eventSource = new Timeline.DefaultEventSource();' . "\n" . ' var theme = Timeline.ClassicTheme.create();' . "\n" . ' theme.event.bubble.width = 350;' . "\n" . ' theme.event.bubble.height = 300;' . "\n" . ' var bandInfos = [' . "\n" . ' Timeline.createBandInfo({' . "\n" . ' eventSource: eventSource,' . "\n" . ' date: "' . $now . '",' . "\n" . ' width: "80%",' . "\n" . ' intervalUnit: Timeline.DateTime.WEEK,' . "\n" . ' intervalPixels: 200,' . "\n" . ' theme: theme,' . "\n" . ' layout: "original" // original, overview, detailed' . "\n" . ' }),' . "\n" . ' Timeline.createBandInfo({' . "\n" . ' showEventText: false,' . "\n" . ' trackHeight: 0.5,' . "\n" . ' trackGap: 0.2,' . "\n" . ' eventSource: eventSource,' . "\n" . ' date: "' . $now . '",' . "\n" . ' width: "20%",' . "\n" . ' intervalUnit: Timeline.DateTime.MONTH,' . "\n" . ' intervalPixels: 50' . "\n" . ' })' . "\n" . ' ];' . "\n" . ' bandInfos[1].syncWith = 0;' . "\n" . ' bandInfos[1].highlight = true;' . "\n" . ' bandInfos[1].eventPainter.setLayout(bandInfos[0].eventPainter.getLayout());' . "\n" . ' simile_handle = Timeline.create(document.getElementById("gantt"), bandInfos, Timeline.HORIZONTAL);' . "\n" . ' simile_handle.showLoadingMessage();' . "\n" . ' Timeline.loadXML("' . $context['url_to_home'] . $context['url_to_root'] . $cache_id . '", function(xml, url) { eventSource.loadXML(xml, url); });' . "\n" . ' simile_handle.hideLoadingMessage();' . "\n" . '}' . "\n" . "\n" . 'var resizeTimerID = null;' . "\n" . 'function onResize() {' . "\n" . ' if (resizeTimerID == null) {' . "\n" . ' resizeTimerID = window.setTimeout(function() {' . "\n" . ' resizeTimerID = null;' . "\n" . ' simile_handle.layout();' . "\n" . ' }, 500);' . "\n" . ' }' . "\n" . '}' . "\n" . "\n" . '// observe page major events' . "\n" . '$(document).ready( onLoad);' . "\n" . '$(window).resize(onResize);' . "\n"); // job done return $output; // a Freemind map // a Freemind map case 'mm': // if we have an external reference, use it if (isset($item['file_href']) && $item['file_href']) { $target_href = $item['file_href']; // else redirect to ourself } else { // ensure a valid file name $file_name = utf8::to_ascii($item['file_name']); // where the file is $path = Files::get_path($item['anchor']) . '/' . rawurlencode($item['file_name']); // map the file on the regular web space $url_prefix = $context['url_to_home'] . $context['url_to_root']; // redirect to the actual file $target_href = $url_prefix . $path; } // allow several viewers to co-exist in the same page static $freemind_viewer_index; if (!isset($freemind_viewer_index)) { $freemind_viewer_index = 1; } else { $freemind_viewer_index++; } // load flash player $url = $context['url_to_home'] . $context['url_to_root'] . 'included/browser/visorFreemind.swf'; // variables $flashvars = 'initLoadFile=' . $target_href . '&openUrl=_self'; $output = '<div id="freemind_viewer_' . $freemind_viewer_index . '">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n"; Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.menu = "false";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $url . '", "freemind_viewer_' . $freemind_viewer_index . '", "' . $width . '", "' . $height . '", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n"); // offer to download a copy of the map $menu = array($target_href => i18n::s('Browse this map with Freemind')); // display menu commands below the viewer $output .= Skin::build_list($menu, 'menu_bar'); // job done return $output; // native flash // native flash case 'swf': // where to get the file if (isset($item['file_href']) && $item['file_href']) { $url = $item['file_href']; } else { $url = $context['url_to_home'] . $context['url_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . rawurlencode($item['file_name']); } $output = '<div id="swf_' . $item['id'] . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n"; Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.allowfullscreen = "true";' . "\n" . 'params.allowscriptaccess = "always";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $url . '", "swf_' . $item['id'] . '", "' . $width . '", "' . $height . '", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n"); return $output; // link to file page // link to file page default: // link label $text = Skin::strip($item['title'] ? $item['title'] : str_replace('_', ' ', $item['file_name'])); // make a link to the target page $url = Files::get_permalink($item); // return a complete anchor $output =& Skin::build_link($url, $text); return $output; } }
if ($name = implode(' - ', $name)) { Safe::header("icy-name: " . utf8::to_iso8859(utf8::transcode($name))); } // genre if ($value = implode(', ', @$data['comments_html']['genre'])) { Safe::header("icy-genre: " . utf8::to_iso8859(utf8::transcode($value))); } // audio bitrate if ($value = @$data['audio']['bitrate']) { Safe::header("icy-br: " . substr($value, 0, -3)); } // this server Safe::header("icy-url: " . $context['url_to_home'] . $context['url_to_root']); } // serve the right type Safe::header('Content-Type: ' . Files::get_mime_type($item['file_name'])); // suggest a name for the saved file $file_name = utf8::to_ascii($item['file_name']); Safe::header('Content-Disposition: attachment; filename="' . str_replace('"', '', $file_name) . '"'); // we accepted (limited) range requests Safe::header('Accept-Ranges: bytes'); // provide only a slice of the file if (isset($_SERVER['HTTP_RANGE']) && !strncmp('bytes=', $_SERVER['HTTP_RANGE'], 6)) { // maybe several ranges $range = substr($_SERVER['HTTP_RANGE'], 6); // process only the first range, if several are specified if ($position = strpos($range, ',')) { $range = substr($range, 0, $position); } // beginning and end of the range list($offset, $end) = explode('-', $range);
/** * integrate some player for the file, if any * * @param array the file to look at * @param int width for video player * @param int height of video player * @return string tags to be put in the HTML flow, or an empty string */ public static function interact($item, $width = 320, $height = 240, $flashvars = '', $with_icon = TRUE) { global $context; static $counter; if (!isset($counter)) { $counter = 1; } else { $counter++; } // display explicit title, if any $title = ''; if ($item['title']) { $title = '<p>' . Skin::strip($item['title']) . '</p>'; } // several ways to play flash switch (strtolower(substr($item['file_name'], -3))) { // audio file handled by dewplayer case 'mp3': // only if the player is available if (file_exists($context['path_to_root'] . 'included/browser/dewplayer.swf')) { // the player $dewplayer_url = $context['url_to_root'] . 'included/browser/dewplayer.swf'; // the mp3 file if (isset($item['file_href']) && $item['file_href']) { $mp3_url = $item['file_href']; } else { $mp3_url = $context['url_to_root'] . Files::get_url($item['id'], 'fetch', $item['file_name']); } $flashvars = 'son=' . $mp3_url; // combine the two in a single object $output = '<div id="interact_' . $counter . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n" . $title; Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($mp3_url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.menu = "false";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $dewplayer_url . '", "interact_' . $counter . '", "200", "20", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n"); return $output; } // native flash // native flash case 'swf': // where to get the file if (isset($item['file_href']) && $item['file_href']) { $url = $item['file_href']; } else { $url = $context['url_to_home'] . $context['url_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . rawurlencode($item['file_name']); } $output = '<div id="interact_' . $counter . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div><br />' . "\n" . $title; Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.allowfullscreen = "true";' . "\n" . 'params.allowscriptaccess = "always";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $url . '", "interact_' . $counter . '", "' . $width . '", "' . $height . '", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n"); return $output; // stream a video // stream a video case '3gp': case 'flv': case 'm4v': case 'mov': case 'mp4': // a flash player to stream a flash video $flvplayer_url = $context['url_to_home'] . $context['url_to_root'] . 'included/browser/player_flv_maxi.swf'; // file is elsewhere if (isset($item['file_href']) && $item['file_href']) { $url = $item['file_href']; } else { $url = $context['url_to_home'] . $context['url_to_root'] . Files::get_url($item['id'], 'fetch', $item['file_name']); } // pass parameters to the player if ($flashvars) { $flashvars = str_replace('autostart=true', 'autoplay=1', $flashvars) . '&'; } $flashvars .= 'width=' . $width . '&height=' . $height; // rely on Flash if (Surfer::has_flash()) { // the full object is built in Javascript --see parameters at http://flv-player.net/players/maxi/documentation/ $output = '<div id="interact_' . $counter . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n" . $title; Page::insert_script('var flashvars = { flv:"' . $url . '", ' . str_replace(array('&', '='), array('", ', ':"'), $flashvars) . '", autoload:0, margin:1, showiconplay:1, playeralpha:50, iconplaybgalpha:30, showloading:"always", ondoubleclick:"fullscreen" }' . "\n" . 'var params = { allowfullscreen: "true", allowscriptaccess: "always" }' . "\n" . 'var attributes = { id: "interact_' . $counter . '", name: "file_' . $item['id'] . '"}' . "\n" . 'swfobject.embedSWF("' . $flvplayer_url . '", "interact_' . $counter . '", "' . $width . '", "' . $height . '", "9", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", flashvars, params);' . "\n"); // native support } else { $output = '<object width="' . $width . '" height="' . $height . '" data="' . $url . '" type="' . Files::get_mime_type($item['file_name']) . '">' . "\n" . ' <param value="' . $url . '" name="movie" />' . "\n" . ' <param value="true" name="allowFullScreen" />' . "\n" . ' <param value="always" name="allowscriptaccess" />' . "\n" . ' <a href="' . $url . '">No video playback capabilities, please download the file</a>' . "\n" . '</object>' . "\n" . $title; } return $output; } if (!$with_icon) { return ''; } // this is a reasonably large image if (Files::is_image($item['file_name']) && ($image_information = Safe::GetImageSize($context['path_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . $item['file_name'])) && $image_information[0] <= 600) { // provide a direct link to it! $src = $context['url_to_home'] . $context['url_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . rawurlencode($item['file_name']); $icon = '<img src="' . $src . '" width="' . $image_information[0] . '" height="' . $image_information[1] . '" alt="" style="padding: 3px"/>' . BR; return Skin::build_link(Files::get_download_url($item), $icon, 'basic') . $title; } // explicit icon if ($item['thumbnail_url']) { $icon = $item['thumbnail_url']; } else { $icon = $context['url_to_root'] . Files::get_icon_url($item['file_name']); } // a clickable image to access the file if ($icon) { $icon = '<img src="' . $icon . '" alt="" style="padding: 3px"/>'; // label for this file $text = ''; // signal restricted and private files if ($item['active'] == 'N') { $text .= PRIVATE_FLAG; } elseif ($item['active'] == 'R') { $text .= RESTRICTED_FLAG; } // use file name, or regular title $text .= Skin::strip($item['title'] ? $item['title'] : str_replace('_', ' ', $item['file_name'])); // flag files uploaded recently if ($item['create_date'] >= $context['fresh']) { $text .= NEW_FLAG; } elseif ($item['edit_date'] >= $context['fresh']) { $text .= UPDATED_FLAG; } // make a link to the target page $url = Files::get_download_url($item); return Skin::build_link($url, $icon, 'basic') . BR . Skin::build_link($url, $text, 'basic'); } // nothing special return ''; }
/** * build and transmit a complex e-mail messages * * This function allows for individual posts, textual and HTML messages, and attached files. * * For this to work, e-mail service has to be explicitly activated in the * main configuration panel, at [script]control/configure.php[/script]. * * You can refer to local images in HTML parts, and the function will automatically attach these * to the message, else mail clients would not display them correctly. * * The message actually sent has a complex structure, with several parts assembled together, * as [explained at altepeter.net|http://altepeter.net/tech/articles/html-emails]. * * @link http://altepeter.net/tech/articles/html-emails * * Several recipients can be provided as a list of addresses separated by * commas. For bulk posts, recipients can be transmitted as an array of strings. * In all cases, this function sends one separate message per recipient. * * This function will ensure that only one mail message is send to a recipient, * by maintaining an internal list of addresses that have been processed. * Therefore, if this function is called several times, with some repeated recipients, * those will receive only the first message, and other messages to the same address * will be dropped. * * Bracketed recipients, such as ##Foo Bar <*****@*****.**>##, are handled properly, * meaning ##foo@bar.com## is transmitted to the mailing function, while * the string ##To: Foo Bar <*****@*****.**>## is added to headers. * * If an array of messages is provided to the function, it is turned to a multi-part * message, as in the following example: * * [php] * $message = array(); * $message['text/plain; charset=utf-8'] = 'This is a plain message'; * $message['text/html'] = '<html><head><body>This is an HTML message</body></html>'; * Mailer::post($from, $to, $subject, $message); * [/php] * * It is recommended to begin with the bare text, and to have the rich format part coming * after, as in the example. Also, if you don't provide a charset, then UTF-8 is used. * * Long lines of text/plain parts are wrapped according to * [link=Dan's suggestion]http://mailformat.dan.info/body/linelength.html[/link]. * * @link http://mailformat.dan.info/body/linelength.html Dan's Mail Format Site: Body: Line Length * * Message parts are encoded either as quoted-printable (textual entities) or as base-64 (others). * * A list of files to be attached to the message can be provided as in the following example: * * [php] * $attachments = array(); * $attachments[] = 'special/report.pdf'; * $attachments[] = 'skins/my_skin/newsletters/image.png'; * Mailer::post($from, $to, $subject, $message, $attachments); * [/php] * * Files are named from the installation directory of yacs, as visible in the examples. * * This function returns the number of successful posts, * and populates the error context, where applicable. * * @param string sender address * @param mixed recipient address(es) * @param string subject * @param mixed actual message, either a string, or an array of message parts * @param array attachments, if any * @param mixed additional headers, if any * @return the number of actual posts, or 0 * * @see articles/mail.php * @see letters/new.php * @see users/mail.php */ public static function post($from, $to, $subject, $message, $attachments = NULL, $headers = '') { global $context; // ensure that we have a sender if (!$from) { $from = Mailer::get_from_recipient(); } // email services have to be activated if (!isset($context['with_email']) || $context['with_email'] != 'Y') { Logger::error(i18n::s('E-mail has not been enabled on this system.')); return 0; // check sender address } elseif (!$from) { Logger::error(i18n::s('Empty sender address')); return 0; // check recipient address } elseif (!$to) { Logger::error(i18n::s('Empty recipient address')); return 0; // check mail subject } elseif (!$subject) { Logger::error(i18n::s('No subject')); return 0; // check mail content } elseif (!$message) { Logger::error(i18n::s('No message')); return 0; } // the end of line string for mail messages if (!defined('M_EOL')) { define('M_EOL', "\n"); } // encode the subject line $subject = Mailer::encode_subject($subject); // make some text out of an array if (is_array($headers)) { $headers = implode(M_EOL, $headers); } // From: header if (!preg_match('/^From: /im', $headers)) { $headers .= M_EOL . 'From: ' . $from; } // Reply-To: header if (!preg_match('/^Reply-To: /im', $headers)) { $headers .= M_EOL . 'Reply-To: ' . $from; } // Return-Path: header --to process errors if (!preg_match('/^Return-Path: /im', $headers)) { $headers .= M_EOL . 'Return-Path: ' . $from; } // Message-ID: header --helps to avoid spam filters if (!preg_match('/^Message-ID: /im', $headers)) { $headers .= M_EOL . 'Message-ID: <uniqid.' . uniqid() . '@' . $context['host_name'] . '>'; } // MIME-Version: header if (!preg_match('/^MIME-Version: /im', $headers)) { $headers .= M_EOL . 'MIME-Version: 1.0'; } // arrays are easier to manage if (is_string($message)) { // turn HTML entities to UTF-8 $message = Safe::html_entity_decode($message, ENT_QUOTES, 'UTF-8'); $copy = $message; $message = array(); $message['text/plain; charset=utf-8'] = $copy; unset($copy); } // turn attachments to some array too if (is_string($attachments)) { $attachments = array($attachments); } elseif (!is_array($attachments)) { $attachments = array(); } // we only consider objects from this server $my_prefix = $context['url_to_home'] . $context['url_to_root']; // transcode objects that will be transmitted along the message (i.e., images) foreach ($message as $type => $part) { // search throughout the full text $head = 0; while ($head = strpos($part, ' src="', $head)) { $head += strlen(' src="'); // a link has been found if ($tail = strpos($part, '"', $head + 1)) { $reference = substr($part, $head, $tail - $head); // remember local links only if (!strncmp($reference, $my_prefix, strlen($my_prefix))) { // local name $name = urldecode(substr($reference, strlen($my_prefix))); // content-id to be used instead of the link $cid = sprintf('%u@%s', crc32($name), $context['host_name']); // transcode the link in this part $part = substr($part, 0, $head) . 'cid:' . $cid . substr($part, $tail); // remember to put content in attachments of this message, if not done yet if (!in_array($name, $attachments)) { $attachments[] = $name; } } } } // remember the transcoded part $message[$type] = $part; } // we need some boundary string if (count($message) + count($attachments) > 1) { $boundary = md5(time()); } // wrapping threshold if (!defined('WRAPPING_LENGTH')) { define('WRAPPING_LENGTH', 70); } // combine message parts $content_type = ''; $body = ''; foreach ($message as $type => $part) { // quote textual entities if (!strncmp($type, 'text/', 5)) { $content_encoding = 'quoted-printable'; $part = quoted_printable_encode($part); // encode everything else } else { $content_encoding = 'base64'; $part = chunk_split(base64_encode($content), 76, M_EOL); } // only one part if (count($message) == 1) { $content_type = $type; $body = $part; // one part among several } else { // let user agent select among various alternatives if (!$content_type) { $content_type = 'multipart/alternative; boundary="' . $boundary . '-internal"'; } // introduction to assembled parts if (!$body) { $body = 'This is a multi-part message in MIME format.'; } // this part only --second EOL is part of the boundary chain $body .= M_EOL . M_EOL . '--' . $boundary . '-internal' . M_EOL . 'Content-Type: ' . $type . M_EOL . 'Content-Transfer-Encoding: ' . $content_encoding . M_EOL . M_EOL . $part; } } // finalize the body if (count($message) > 1) { $body .= M_EOL . M_EOL . '--' . $boundary . '-internal--'; } // a mix of things if (count($attachments)) { // encoding is irrelevant if there are multiple parts if (!strncmp($content_type, 'multipart/', 10)) { $content_encoding = ''; } else { $content_encoding = M_EOL . 'Content-Transfer-Encoding: ' . $content_encoding; } // identify the main part of the overall message $content_start = 'mainpart'; // the current body becomes the first part of a larger message $body = 'This is a multi-part message in MIME format.' . M_EOL . M_EOL . '--' . $boundary . '-external' . M_EOL . 'Content-Type: ' . $content_type . $content_encoding . M_EOL . 'Content-ID: <' . $content_start . '>' . M_EOL . M_EOL . $body; // message parts should be considered as an aggregate whole --see RFC 2387 $content_type = 'multipart/related; type="multipart/alternative"; boundary="' . $boundary . '-external"'; $content_encoding = ''; // process every file foreach ($attachments as $name => $content) { // read external file content if (preg_match('/^[0-9]+$/', $name)) { // only a file name has been provided $name = $content; // read file content from the file system if (!($content = Safe::file_get_contents($name))) { continue; } } // file name is the file type if (preg_match('/name="(.+)?"/', $name, $matches)) { $type = $name; $name = $matches[1]; } else { $type = Files::get_mime_type($name); } // a unique id for for this file $cid = sprintf('%u@%s', crc32($name), $context['host_name']); // set a name that avoids problems $basename = utf8::to_ascii(basename($name)); // headers for one file $body .= M_EOL . M_EOL . '--' . $boundary . '-external' . M_EOL . 'Content-Type: ' . $type . M_EOL . 'Content-Disposition: inline; filename="' . str_replace('"', '', $basename) . '"' . M_EOL . 'Content-ID: <' . $cid . '>'; // transfer textual entities as they are if (!strncmp($type, 'text/', 5)) { $body .= M_EOL . 'Content-Transfer-Encoding: quoted-printable' . M_EOL . M_EOL . quoted_printable_encode($content); // encode everything else } else { $body .= M_EOL . 'Content-Transfer-Encoding: base64' . M_EOL . M_EOL . chunk_split(base64_encode($content), 76, M_EOL); } } // the closing boundary $body .= M_EOL . M_EOL . '--' . $boundary . '-external--'; } // Content-Type: header if ($content_type && !preg_match('/^Content-Type: /im', $headers)) { $headers .= M_EOL . 'Content-Type: ' . $content_type; } // Content-Transfer-Encoding: header if (!isset($boundary) && $content_encoding && !preg_match('/^Content-Transfer-Encoding: /im', $headers)) { $headers .= M_EOL . 'Content-Transfer-Encoding: ' . $content_encoding; } // Start: header if (isset($boundary) && isset($content_start) && $content_start && !preg_match('/^Start: /im', $headers)) { $headers .= M_EOL . 'Start: ' . $content_start; } // X-Mailer: header --helps to avoid spam filters if (!preg_match('/^X-Mailer: /im', $headers)) { $headers .= M_EOL . 'X-Mailer: yacs'; } // strip leading spaces and newlines $headers = trim($headers); // make an array of recipients if (!is_array($to)) { $to = Mailer::explode_recipients($to); } // the list of recipients contacted during overall script execution if (!isset($context['mailer_recipients'])) { $context['mailer_recipients'] = array(); } // process every recipient $posts = 0; foreach ($to as $recipient) { // clean the provided string $recipient = trim(str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $recipient)); // this e-mail address has already been processed if (in_array($recipient, $context['mailer_recipients'])) { if (isset($context['debug_mail']) && $context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: Skipping recipient already processed', $recipient, 'debug'); } continue; // remember this recipient } else { $context['mailer_recipients'][] = $recipient; } // queue the message Mailer::queue($recipient, $subject, $body, $headers); $posts++; } // track last submission include_once $context['path_to_root'] . 'shared/values.php'; Values::set('mailer.last.queued', $subject . ' (' . $posts . ' recipients)'); // return the number of actual posts return $posts; }