1<?php 2/** 3* MailEngine class 4* @author Brian Wong <bwsource@users.sourceforge.net> 5* @version 04-12-05 6* @package MailEngine 7* 8* Copyright (C) 2003 - 2007 MailZu 9* License: GPL, see LICENSE 10*/ 11/** 12* Base directory of application 13*/ 14@define('BASE_DIR', dirname(__FILE__) . '/..'); 15/** 16* CmnFns class 17*/ 18include_once('CmnFns.class.php'); 19/** 20* Pear::DB 21*/ 22if ($GLOBALS['conf']['app']['safeMode']) { 23 ini_set('include_path', ( dirname(__FILE__) . '/pear/' . PATH_SEPARATOR . ini_get('include_path') )); 24 include_once('pear/Mail/mimeDecode.php'); 25} 26else { 27 include_once('Mail/mimeDecode.php'); 28} 29 30/** 31* Include htmlfilter class 32*/ 33include_once('lib/htmlfilter.php'); 34 35 36/** 37* Provide all MIME functionality 38*/ 39 40/** 41* Get full MIME type 42* $param The mime structure object 43*/ 44function GetCtype($struct) { 45 $ctype_p = strtolower(trim($struct->ctype_primary)); 46 $ctype_s = strtolower(trim($struct->ctype_secondary)); 47 $type = $ctype_p . '/' . $ctype_s; 48 return $type; 49} 50 51/** 52* Recursively parse MIME structure 53* $param The mime structure object 54*/ 55$filelist = array (); 56$errors = array (); 57 58function MsgParseBody($struct) { 59 60 global $filelist; 61 global $errors; 62 $ctype_p = strtolower(trim($struct->ctype_primary)); 63 $ctype_s = strtolower(trim($struct->ctype_secondary)); 64 65 switch ($ctype_p) { 66 case "multipart": 67 switch ($ctype_s) { 68 case "alternative": 69 // Handle multipart/alternative parts 70 $alt_entity = FindMultiAlt($struct->parts); 71 // Ignore if we return false NEEDS WORK 72 if ($alt_entity) MsgParseBody($alt_entity); 73 break; 74 case "related": 75 // Handle multipart/related parts 76 $rel_entities = FindMultiRel($struct); 77 foreach ($rel_entities as $ent) { 78 MsgParseBody($ent); 79 } 80 break; 81 default: 82 // Probably multipart/mixed here 83 // Recursively process nested mime entities 84 if ( is_array($struct->parts) || is_object($struct->parts) ) { 85 foreach ($struct->parts as $cur_part) { 86 MsgParseBody($cur_part); 87 } 88 } else { 89 $errors['Invalid or Corrupt MIME Detected.'] = true; 90 } 91 break; 92 } 93 break; 94 95 case "text": 96 // Do not display attached text types 97 if ( isset($struct->d_parameters['filename']) ) { 98 $attachment = $struct->d_parameters['filename']; 99 } elseif ( isset($struct->d_parameters['name']) ) { 100 $attachment = $struct->d_parameters['name']; 101 } else { 102 $attachment = NULL; 103 } 104 if ($attachment) { 105 array_push($filelist, $attachment); 106 break; 107 } 108 switch ($ctype_s) { 109 // Plain text 110 case "plain": 111 MsgBodyPlainText($struct->body); 112 break; 113 // HTML text 114 case "html": 115 MsgBodyHtmlText($struct->body); 116 break; 117 // Text type we do not support 118 default: 119 $errors['Portions of text could not be displayed'] = true; 120 } 121 break; 122 123 default: 124 // Save the listed filename or notify the 125 // reader that this mail is not displayed completely 126 if ( isset( $struct->d_parameters['filename'] )) { 127 $attachment = $struct->d_parameters['filename']; 128 } else { $attachment = NULL; } 129 $attachment ? array_push($filelist, $attachment) : $errors['Unsupported MIME objects present'] = true; 130 131 } 132} 133 134/** 135* Get the best MIME entity for multipart/alternative 136* Adapted from SqurrelMail 137* $param Array of MIME entities 138* $return Single MIME entity 139*/ 140function FindMultiAlt($parts) { 141 $alt_pref = array ('text/plain', 'text/html'); 142 $best_view = 0; 143 // Bad Headers sometimes have invalid MIME.... 144 if ( is_array($parts) || is_object($parts) ) { 145 foreach ($parts as $cur_part) { 146 $type = GetCtype($cur_part); 147 if ($type == 'multipart/related') { 148 if ( isset( $cur_part->d_parameters['type'] )) { $type = $cur_part->d_parameters['type']; } 149 // Mozilla bug. Mozilla does not provide the parameter type. 150 if (!$type) $type = 'text/html'; 151 } 152 $altCount = count($alt_pref); 153 for ($j = $best_view; $j < $altCount; ++$j) { 154 if (($alt_pref[$j] == $type) && ($j >= $best_view)) { 155 $best_view = $j; 156 $struct = $cur_part; 157 } 158 } 159 } 160 return $struct; 161 } else { 162 $errors['Invalid or Corrupt MIME Detected.'] = true; 163 } 164} 165 166/** 167* Get the list of related entities for multipart/related 168* Adapted from SqurrelMail 169* $param multipart/alternative structure 170* @return List of MIME entities 171*/ 172function FindMultiRel($struct) { 173 $entities = array(); 174 if ( isset( $cur_part->d_parameters['type'] )) { $type = $cur_part->d_parameters['type']; } 175 // Mozilla bug. Mozilla does not provide the parameter type. 176 if (!$type) $type = 'text/html'; 177 // Bad Headers sometimes have invalid MIME.... 178 if ( is_array($struct->parts) || is_object($struct->parts) ) { 179 foreach ($struct->parts as $part) { 180 if (GetCtype($part) == $type || GetCtype($part) == "multipart/alternative") { 181 array_push($entities,$part); 182 } 183 } 184 } else { 185 $errors['Invalid or Corrupt MIME Detected.'] = true; 186 } 187 return $entities; 188} 189 190// Wrapper script for htmlfilter. Settings taken 191// from SquirrelMail 192function sanitizeHTML($body) { 193 if (isset($_COOKIE['lang']) && 194 file_exists("img/".substr($_COOKIE['lang'],0,2).".blocked_img.png")) { 195 $secremoveimg = "img/".substr($_COOKIE['lang'],0,2).".blocked_img.png"; 196 } else { 197 $secremoveimg = "img/blocked_img.png"; 198 } 199 $tag_list = Array( 200 false, 201 "object", 202 "meta", 203 "html", 204 "head", 205 "base", 206 "link", 207 "frame", 208 "iframe", 209 "plaintext", 210 "marquee" 211 ); 212 213 $rm_tags_with_content = Array( 214 "script", 215 "applet", 216 "embed", 217 "title", 218 "frameset", 219 "xml", 220 "style" 221 ); 222 223 $self_closing_tags = Array( 224 "img", 225 "br", 226 "hr", 227 "input" 228 ); 229 230 $force_tag_closing = true; 231 232 $rm_attnames = Array( 233 "/.*/" => 234 Array( 235 "/target/i", 236 "/^on.*/i", 237 "/^dynsrc/i", 238 "/^data.*/i", 239 "/^lowsrc.*/i" 240 ) 241 ); 242 243 $bad_attvals = Array( 244 "/.*/" => 245 Array( 246 "/^src|background/i" => 247 Array( 248 Array( 249 "/^([\'\"])\s*\S+script\s*:.*([\'\"])/si", 250 "/^([\'\"])\s*mocha\s*:*.*([\'\"])/si", 251 "/^([\'\"])\s*about\s*:.*([\'\"])/si", 252 "/^([\'\"])\s*https*:.*([\'\"])/si", 253 "/^([\'\"])\s*cid*:.*([\'\"])/si" 254 ), 255 Array( 256 "\\1$secremoveimg\\2", 257 "\\1$secremoveimg\\2", 258 "\\1$secremoveimg\\2", 259 "\\1$secremoveimg\\2", 260 "\\1$secremoveimg\\2" 261 ) 262 ), 263 "/^href|action/i" => 264 Array( 265 Array( 266 "/^([\'\"])\s*\S+script\s*:.*([\'\"])/si", 267 "/^([\'\"])\s*mocha\s*:*.*([\'\"])/si", 268 "/^([\'\"])\s*about\s*:.*([\'\"])/si" 269 ), 270 Array( 271 "\\1#\\1", 272 "\\1#\\1", 273 "\\1#\\1", 274 "\\1#\\1" 275 ) 276 ), 277 "/^style/i" => 278 Array( 279 Array( 280 "/expression/i", 281 "/binding/i", 282 "/behaviou*r/i", 283 "/include-source/i", 284 "/url\s*\(\s*([\'\"])\s*\S+script\s*:.*([\'\"])\s*\)/si", 285 "/url\s*\(\s*([\'\"])\s*mocha\s*:.*([\'\"])\s*\)/si", 286 "/url\s*\(\s*([\'\"])\s*about\s*:.*([\'\"])\s*\)/si", 287 "/(.*)\s*:\s*url\s*\(\s*([\'\"]*)\s*\S+script\s*:.*([\'\"]*)\s*\)/si", 288 "/url\(([\'\"])\s*https*:.*([\'\"])\)/si" 289 ), 290 Array( 291 "idiocy", 292 "idiocy", 293 "idiocy", 294 "idiocy", 295 "url(\\1#\\1)", 296 "url(\\1#\\1)", 297 "url(\\1#\\1)", 298 "url(\\1#\\1)", 299 "url(\\1#\\1)", 300 "\\1:url(\\2#\\3)", 301 "url(\\1$secremoveimg\\1)" 302 ) 303 ) 304 ) 305 ); 306 307 $add_attr_to_tag = Array( 308 "/^a$/i" => 309 Array('target'=>'"_new"' 310 ) 311 ); 312 313 $trusted_html = sanitize($body, 314 $tag_list, 315 $rm_tags_with_content, 316 $self_closing_tags, 317 $force_tag_closing, 318 $rm_attnames, 319 $bad_attvals, 320 $add_attr_to_tag 321 ); 322 323 return $trusted_html; 324} 325