diff --git cdn.basic.farfuture.inc cdn.basic.farfuture.inc index 6bd1e13..6df4369 100644 --- cdn.basic.farfuture.inc +++ cdn.basic.farfuture.inc @@ -187,7 +187,10 @@ function cdn_basic_farfuture_get_identifier($path) { // Determine which UFI method should be used. Note that we keep on trying to // find another method until the end: the order of rules matters! + // However, specificity also matters. The directory pattern "foo/bar/*" + // should *always* override the less specific pattern "foo/*". $ufi_method = FALSE; + $current_specificity = 0; foreach (array_keys($mapping) as $directory) { if (drupal_match_path($path, $directory)) { // Parse the file extension from the given path; convert it to lower case. @@ -195,17 +198,22 @@ function cdn_basic_farfuture_get_identifier($path) { // Based on the file extension, determine which key should be used to find // the CDN URLs in the mapping lookup table, if any. + $extension = NULL; if (array_key_exists($file_extension, $mapping[$directory])) { $key = $file_extension; } elseif (array_key_exists('*', $mapping[$directory])) { - $key = '*'; - } - else { - $key = NULL; + $extension = '*'; } - $ufi_method = $mapping[$directory][$key]; + // If a matching extension was found, assign the corresponding UFI method. + if (isset($extension)) { + $specificity = $mapping[$directory][$extension]['specificity']; + if ($specificity > $current_specificity) { + $ufi_method = $mapping[$directory][$extension]['ufi method']; + $current_specificity = $specificity; + } + } } } @@ -250,6 +258,7 @@ function _cdn_basic_farfuture_parse_raw_mapping($mapping_raw) { // file extensions. $parts = explode('|', $line); $directories = explode(':', $parts[0]); + $specificity = 0; // There may be 2 or 3 parts: // - part 1: directories @@ -267,8 +276,14 @@ function _cdn_basic_farfuture_parse_raw_mapping($mapping_raw) { // Create the mapping lookup table. foreach ($directories as $directory) { + $directory_specificity = 10 * count(split('/', $directory)); foreach ($extensions as $extension) { - $mapping[$directory][$extension] = $ufi_method; + $extension_specificity = ($extension == '*') ? 0 : 1; + + $mapping[$directory][$extension] = array( + 'ufi method' => $ufi_method, + 'specificity' => $directory_specificity + $extension_specificity, + ); } } } @@ -363,15 +378,10 @@ function _cdn_transfer_file($path) { * @return * The internet media type registered for the extension or * application/octet-stream for unknown extensions. - * - * @see file_default_mimetype_mapping() */ function _cdn_basic_farfuture_get_mimetype($path) { - static $mapping; - - if (!isset($mapping)) { - $mapping = _cdn_basic_farfuture_mimetype_mapping();; - } + cdn_load_include('mimetypes'); + $mapping = cdn_mimetype_mapping(); $extension = ''; $file_parts = explode('.', basename($path)); @@ -395,836 +405,6 @@ function _cdn_basic_farfuture_get_mimetype($path) { } /** - * Default MIME extension mapping. - * Borrowed from Drupal 7. - * - * @return - * Array of mimetypes correlated to the extensions that relate to them. - */ -function _cdn_basic_farfuture_mimetype_mapping() { - return array( - 'mimetypes' => array( - 0 => 'application/andrew-inset', - 1 => 'application/atom', - 2 => 'application/atomcat+xml', - 3 => 'application/atomserv+xml', - 4 => 'application/cap', - 5 => 'application/cu-seeme', - 6 => 'application/dsptype', - 7 => 'application/hta', - 8 => 'application/java-archive', - 9 => 'application/java-serialized-object', - 10 => 'application/java-vm', - 11 => 'application/mac-binhex40', - 12 => 'application/mathematica', - 13 => 'application/msaccess', - 14 => 'application/msword', - 15 => 'application/octet-stream', - 16 => 'application/oda', - 17 => 'application/ogg', - 18 => 'application/pdf', - 19 => 'application/pgp-keys', - 20 => 'application/pgp-signature', - 21 => 'application/pics-rules', - 22 => 'application/postscript', - 23 => 'application/rar', - 24 => 'application/rdf+xml', - 25 => 'application/rss+xml', - 26 => 'application/rtf', - 27 => 'application/smil', - 28 => 'application/vnd.cinderella', - 29 => 'application/vnd.google-earth.kml+xml', - 30 => 'application/vnd.google-earth.kmz', - 31 => 'application/vnd.mozilla.xul+xml', - 32 => 'application/vnd.ms-excel', - 33 => 'application/vnd.ms-excel.addin.macroEnabled.12', - 34 => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', - 35 => 'application/vnd.ms-excel.sheet.macroEnabled.12', - 36 => 'application/vnd.ms-excel.template.macroEnabled.12', - 37 => 'application/vnd.ms-pki.seccat', - 38 => 'application/vnd.ms-pki.stl', - 39 => 'application/vnd.ms-powerpoint', - 40 => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', - 41 => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', - 42 => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', - 43 => 'application/vnd.ms-powerpoint.template.macroEnabled.12', - 44 => 'application/vnd.ms-word.document.macroEnabled.12', - 45 => 'application/vnd.ms-word.template.macroEnabled.12', - 46 => 'application/vnd.ms-xpsdocument', - 47 => 'application/vnd.oasis.opendocument.chart', - 48 => 'application/vnd.oasis.opendocument.database', - 49 => 'application/vnd.oasis.opendocument.formula', - 50 => 'application/vnd.oasis.opendocument.graphics', - 51 => 'application/vnd.oasis.opendocument.graphics-template', - 52 => 'application/vnd.oasis.opendocument.image', - 53 => 'application/vnd.oasis.opendocument.presentation', - 54 => 'application/vnd.oasis.opendocument.presentation-template', - 55 => 'application/vnd.oasis.opendocument.spreadsheet', - 56 => 'application/vnd.oasis.opendocument.spreadsheet-template', - 57 => 'application/vnd.oasis.opendocument.text', - 58 => 'application/vnd.oasis.opendocument.text-master', - 59 => 'application/vnd.oasis.opendocument.text-template', - 60 => 'application/vnd.oasis.opendocument.text-web', - 61 => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 62 => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 63 => 'application/vnd.openxmlformats-officedocument.presentationml.template', - 64 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 65 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 66 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 67 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 68 => 'application/vnd.rim.cod', - 69 => 'application/vnd.smaf', - 70 => 'application/vnd.stardivision.calc', - 71 => 'application/vnd.stardivision.chart', - 72 => 'application/vnd.stardivision.draw', - 73 => 'application/vnd.stardivision.impress', - 74 => 'application/vnd.stardivision.math', - 75 => 'application/vnd.stardivision.writer', - 76 => 'application/vnd.stardivision.writer-global', - 77 => 'application/vnd.sun.xml.calc', - 78 => 'application/vnd.sun.xml.calc.template', - 79 => 'application/vnd.sun.xml.draw', - 80 => 'application/vnd.sun.xml.draw.template', - 81 => 'application/vnd.sun.xml.impress', - 82 => 'application/vnd.sun.xml.impress.template', - 83 => 'application/vnd.sun.xml.math', - 84 => 'application/vnd.sun.xml.writer', - 85 => 'application/vnd.sun.xml.writer.global', - 86 => 'application/vnd.sun.xml.writer.template', - 87 => 'application/vnd.symbian.install', - 88 => 'application/vnd.visio', - 89 => 'application/vnd.wap.wbxml', - 90 => 'application/vnd.wap.wmlc', - 91 => 'application/vnd.wap.wmlscriptc', - 92 => 'application/wordperfect', - 93 => 'application/wordperfect5.1', - 94 => 'application/x-123', - 95 => 'application/x-7z-compressed', - 96 => 'application/x-abiword', - 97 => 'application/x-apple-diskimage', - 98 => 'application/x-bcpio', - 99 => 'application/x-bittorrent', - 100 => 'application/x-cab', - 101 => 'application/x-cbr', - 102 => 'application/x-cbz', - 103 => 'application/x-cdf', - 104 => 'application/x-cdlink', - 105 => 'application/x-chess-pgn', - 106 => 'application/x-cpio', - 107 => 'application/x-debian-package', - 108 => 'application/x-director', - 109 => 'application/x-dms', - 110 => 'application/x-doom', - 111 => 'application/x-dvi', - 112 => 'application/x-flac', - 113 => 'application/x-font', - 114 => 'application/x-freemind', - 115 => 'application/x-futuresplash', - 116 => 'application/x-gnumeric', - 117 => 'application/x-go-sgf', - 118 => 'application/x-graphing-calculator', - 119 => 'application/x-gtar', - 120 => 'application/x-hdf', - 121 => 'application/x-httpd-eruby', - 122 => 'application/x-httpd-php', - 123 => 'application/x-httpd-php-source', - 124 => 'application/x-httpd-php3', - 125 => 'application/x-httpd-php3-preprocessed', - 126 => 'application/x-httpd-php4', - 127 => 'application/x-ica', - 128 => 'application/x-internet-signup', - 129 => 'application/x-iphone', - 130 => 'application/x-iso9660-image', - 131 => 'application/x-java-jnlp-file', - 132 => 'application/x-javascript', - 133 => 'application/x-jmol', - 134 => 'application/x-kchart', - 135 => 'application/x-killustrator', - 136 => 'application/x-koan', - 137 => 'application/x-kpresenter', - 138 => 'application/x-kspread', - 139 => 'application/x-kword', - 140 => 'application/x-latex', - 141 => 'application/x-lha', - 142 => 'application/x-lyx', - 143 => 'application/x-lzh', - 144 => 'application/x-lzx', - 145 => 'application/x-maker', - 146 => 'application/x-mif', - 147 => 'application/x-ms-wmd', - 148 => 'application/x-ms-wmz', - 149 => 'application/x-msdos-program', - 150 => 'application/x-msi', - 151 => 'application/x-netcdf', - 152 => 'application/x-ns-proxy-autoconfig', - 153 => 'application/x-nwc', - 154 => 'application/x-object', - 155 => 'application/x-oz-application', - 156 => 'application/x-pkcs7-certreqresp', - 157 => 'application/x-pkcs7-crl', - 158 => 'application/x-python-code', - 159 => 'application/x-quicktimeplayer', - 160 => 'application/x-redhat-package-manager', - 161 => 'application/x-shar', - 162 => 'application/x-shockwave-flash', - 163 => 'application/x-stuffit', - 164 => 'application/x-sv4cpio', - 165 => 'application/x-sv4crc', - 166 => 'application/x-tar', - 167 => 'application/x-tcl', - 168 => 'application/x-tex-gf', - 169 => 'application/x-tex-pk', - 170 => 'application/x-texinfo', - 171 => 'application/x-trash', - 172 => 'application/x-troff', - 173 => 'application/x-troff-man', - 174 => 'application/x-troff-me', - 175 => 'application/x-troff-ms', - 176 => 'application/x-ustar', - 177 => 'application/x-wais-source', - 178 => 'application/x-wingz', - 179 => 'application/x-x509-ca-cert', - 180 => 'application/x-xcf', - 181 => 'application/x-xfig', - 182 => 'application/x-xpinstall', - 183 => 'application/xhtml+xml', - 184 => 'application/xml', - 185 => 'application/zip', - 186 => 'audio/basic', - 187 => 'audio/midi', - 346 => 'audio/mp4', - 188 => 'audio/mpeg', - 189 => 'audio/ogg', - 190 => 'audio/prs.sid', - 191 => 'audio/x-aiff', - 192 => 'audio/x-gsm', - 193 => 'audio/x-mpegurl', - 194 => 'audio/x-ms-wax', - 195 => 'audio/x-ms-wma', - 196 => 'audio/x-pn-realaudio', - 197 => 'audio/x-realaudio', - 198 => 'audio/x-scpls', - 199 => 'audio/x-sd2', - 200 => 'audio/x-wav', - 201 => 'chemical/x-alchemy', - 202 => 'chemical/x-cache', - 203 => 'chemical/x-cache-csf', - 204 => 'chemical/x-cactvs-binary', - 205 => 'chemical/x-cdx', - 206 => 'chemical/x-cerius', - 207 => 'chemical/x-chem3d', - 208 => 'chemical/x-chemdraw', - 209 => 'chemical/x-cif', - 210 => 'chemical/x-cmdf', - 211 => 'chemical/x-cml', - 212 => 'chemical/x-compass', - 213 => 'chemical/x-crossfire', - 214 => 'chemical/x-csml', - 215 => 'chemical/x-ctx', - 216 => 'chemical/x-cxf', - 217 => 'chemical/x-embl-dl-nucleotide', - 218 => 'chemical/x-galactic-spc', - 219 => 'chemical/x-gamess-input', - 220 => 'chemical/x-gaussian-checkpoint', - 221 => 'chemical/x-gaussian-cube', - 222 => 'chemical/x-gaussian-input', - 223 => 'chemical/x-gaussian-log', - 224 => 'chemical/x-gcg8-sequence', - 225 => 'chemical/x-genbank', - 226 => 'chemical/x-hin', - 227 => 'chemical/x-isostar', - 228 => 'chemical/x-jcamp-dx', - 229 => 'chemical/x-kinemage', - 230 => 'chemical/x-macmolecule', - 231 => 'chemical/x-macromodel-input', - 232 => 'chemical/x-mdl-molfile', - 233 => 'chemical/x-mdl-rdfile', - 234 => 'chemical/x-mdl-rxnfile', - 235 => 'chemical/x-mdl-sdfile', - 236 => 'chemical/x-mdl-tgf', - 237 => 'chemical/x-mmcif', - 238 => 'chemical/x-mol2', - 239 => 'chemical/x-molconn-Z', - 240 => 'chemical/x-mopac-graph', - 241 => 'chemical/x-mopac-input', - 242 => 'chemical/x-mopac-out', - 243 => 'chemical/x-mopac-vib', - 244 => 'chemical/x-ncbi-asn1-ascii', - 245 => 'chemical/x-ncbi-asn1-binary', - 246 => 'chemical/x-ncbi-asn1-spec', - 247 => 'chemical/x-pdb', - 248 => 'chemical/x-rosdal', - 249 => 'chemical/x-swissprot', - 250 => 'chemical/x-vamas-iso14976', - 251 => 'chemical/x-vmd', - 252 => 'chemical/x-xtel', - 253 => 'chemical/x-xyz', - 254 => 'image/gif', - 255 => 'image/ief', - 256 => 'image/jpeg', - 257 => 'image/pcx', - 258 => 'image/png', - 259 => 'image/svg+xml', - 260 => 'image/tiff', - 261 => 'image/vnd.djvu', - 262 => 'image/vnd.microsoft.icon', - 263 => 'image/vnd.wap.wbmp', - 264 => 'image/x-cmu-raster', - 265 => 'image/x-coreldraw', - 266 => 'image/x-coreldrawpattern', - 267 => 'image/x-coreldrawtemplate', - 268 => 'image/x-corelphotopaint', - 269 => 'image/x-jg', - 270 => 'image/x-jng', - 271 => 'image/x-ms-bmp', - 272 => 'image/x-photoshop', - 273 => 'image/x-portable-anymap', - 274 => 'image/x-portable-bitmap', - 275 => 'image/x-portable-graymap', - 276 => 'image/x-portable-pixmap', - 277 => 'image/x-rgb', - 278 => 'image/x-xbitmap', - 279 => 'image/x-xpixmap', - 280 => 'image/x-xwindowdump', - 281 => 'message/rfc822', - 282 => 'model/iges', - 283 => 'model/mesh', - 284 => 'model/vrml', - 285 => 'text/calendar', - 286 => 'text/css', - 287 => 'text/csv', - 288 => 'text/h323', - 289 => 'text/html', - 290 => 'text/iuls', - 291 => 'text/mathml', - 292 => 'text/plain', - 293 => 'text/richtext', - 294 => 'text/scriptlet', - 295 => 'text/tab-separated-values', - 296 => 'text/texmacs', - 297 => 'text/vnd.sun.j2me.app-descriptor', - 298 => 'text/vnd.wap.wml', - 299 => 'text/vnd.wap.wmlscript', - 300 => 'text/x-bibtex', - 301 => 'text/x-boo', - 302 => 'text/x-c++hdr', - 303 => 'text/x-c++src', - 304 => 'text/x-chdr', - 305 => 'text/x-component', - 306 => 'text/x-csh', - 307 => 'text/x-csrc', - 308 => 'text/x-diff', - 309 => 'text/x-dsrc', - 310 => 'text/x-haskell', - 311 => 'text/x-java', - 312 => 'text/x-literate-haskell', - 313 => 'text/x-moc', - 314 => 'text/x-pascal', - 315 => 'text/x-pcs-gcd', - 316 => 'text/x-perl', - 317 => 'text/x-python', - 318 => 'text/x-setext', - 319 => 'text/x-sh', - 320 => 'text/x-tcl', - 321 => 'text/x-tex', - 322 => 'text/x-vcalendar', - 323 => 'text/x-vcard', - 324 => 'video/3gpp', - 325 => 'video/dl', - 326 => 'video/dv', - 327 => 'video/fli', - 328 => 'video/gl', - 329 => 'video/mp4', - 330 => 'video/mpeg', - 331 => 'video/ogg', - 332 => 'video/quicktime', - 333 => 'video/vnd.mpegurl', - 347 => 'video/x-flv', - 334 => 'video/x-la-asf', - 335 => 'video/x-mng', - 336 => 'video/x-ms-asf', - 337 => 'video/x-ms-wm', - 338 => 'video/x-ms-wmv', - 339 => 'video/x-ms-wmx', - 340 => 'video/x-ms-wvx', - 341 => 'video/x-msvideo', - 342 => 'video/x-sgi-movie', - 343 => 'x-conference/x-cooltalk', - 344 => 'x-epoc/x-sisx-app', - 345 => 'x-world/x-vrml', - ), - - // Extensions added to this list MUST be lower-case. - 'extensions' => array( - 'ez' => 0, - 'atom' => 1, - 'atomcat' => 2, - 'atomsrv' => 3, - 'cap' => 4, - 'pcap' => 4, - 'cu' => 5, - 'tsp' => 6, - 'hta' => 7, - 'jar' => 8, - 'ser' => 9, - 'class' => 10, - 'hqx' => 11, - 'nb' => 12, - 'mdb' => 13, - 'dot' => 14, - 'doc' => 14, - 'bin' => 15, - 'oda' => 16, - 'ogg' => 17, - 'ogx' => 17, - 'pdf' => 18, - 'key' => 19, - 'pgp' => 20, - 'prf' => 21, - 'eps' => 22, - 'ai' => 22, - 'ps' => 22, - 'rar' => 23, - 'rdf' => 24, - 'rss' => 25, - 'rtf' => 26, - 'smi' => 27, - 'smil' => 27, - 'cdy' => 28, - 'kml' => 29, - 'kmz' => 30, - 'xul' => 31, - 'xlb' => 32, - 'xlt' => 32, - 'xls' => 32, - 'xlam' => 33, - 'xlsb' => 34, - 'xlsm' => 35, - 'xltm' => 36, - 'cat' => 37, - 'stl' => 38, - 'pps' => 39, - 'ppt' => 39, - 'ppam' => 40, - 'pptm' => 41, - 'ppsm' => 42, - 'potm' => 43, - 'docm' => 44, - 'dotm' => 45, - 'xps' => 46, - 'odc' => 47, - 'odb' => 48, - 'odf' => 49, - 'odg' => 50, - 'otg' => 51, - 'odi' => 52, - 'odp' => 53, - 'otp' => 54, - 'ods' => 55, - 'ots' => 56, - 'odt' => 57, - 'odm' => 58, - 'ott' => 59, - 'oth' => 60, - 'pptx' => 61, - 'ppsx' => 62, - 'potx' => 63, - 'xlsx' => 64, - 'xltx' => 65, - 'docx' => 66, - 'dotx' => 67, - 'cod' => 68, - 'mmf' => 69, - 'sdc' => 70, - 'sds' => 71, - 'sda' => 72, - 'sdd' => 73, - 'sdw' => 75, - 'sgl' => 76, - 'sxc' => 77, - 'stc' => 78, - 'sxd' => 79, - 'std' => 80, - 'sxi' => 81, - 'sti' => 82, - 'sxm' => 83, - 'sxw' => 84, - 'sxg' => 85, - 'stw' => 86, - 'sis' => 87, - 'vsd' => 88, - 'wbxml' => 89, - 'wmlc' => 90, - 'wmlsc' => 91, - 'wpd' => 92, - 'wp5' => 93, - 'wk' => 94, - '7z' => 95, - 'abw' => 96, - 'dmg' => 97, - 'bcpio' => 98, - 'torrent' => 99, - 'cab' => 100, - 'cbr' => 101, - 'cbz' => 102, - 'cdf' => 103, - 'vcd' => 104, - 'pgn' => 105, - 'cpio' => 106, - 'udeb' => 107, - 'deb' => 107, - 'dir' => 108, - 'dxr' => 108, - 'dcr' => 108, - 'dms' => 109, - 'wad' => 110, - 'dvi' => 111, - 'flac' => 112, - 'pfa' => 113, - 'pfb' => 113, - 'pcf' => 113, - 'gsf' => 113, - 'pcf.z' => 113, - 'mm' => 114, - 'spl' => 115, - 'gnumeric' => 116, - 'sgf' => 117, - 'gcf' => 118, - 'taz' => 119, - 'gtar' => 119, - 'tgz' => 119, - 'hdf' => 120, - 'rhtml' => 121, - 'phtml' => 122, - 'pht' => 122, - 'php' => 122, - 'phps' => 123, - 'php3' => 124, - 'php3p' => 125, - 'php4' => 126, - 'ica' => 127, - 'ins' => 128, - 'isp' => 128, - 'iii' => 129, - 'iso' => 130, - 'jnlp' => 131, - 'js' => 132, - 'jmz' => 133, - 'chrt' => 134, - 'kil' => 135, - 'skp' => 136, - 'skd' => 136, - 'skm' => 136, - 'skt' => 136, - 'kpr' => 137, - 'kpt' => 137, - 'ksp' => 138, - 'kwd' => 139, - 'kwt' => 139, - 'latex' => 140, - 'lha' => 141, - 'lyx' => 142, - 'lzh' => 143, - 'lzx' => 144, - 'maker' => 145, - 'frm' => 145, - 'frame' => 145, - 'fm' => 145, - 'book' => 145, - 'fb' => 145, - 'fbdoc' => 145, - 'mif' => 146, - 'wmd' => 147, - 'wmz' => 148, - 'dll' => 149, - 'bat' => 149, - 'exe' => 149, - 'com' => 149, - 'msi' => 150, - 'nc' => 151, - 'pac' => 152, - 'nwc' => 153, - 'o' => 154, - 'oza' => 155, - 'p7r' => 156, - 'crl' => 157, - 'pyo' => 158, - 'pyc' => 158, - 'qtl' => 159, - 'rpm' => 160, - 'shar' => 161, - 'swf' => 162, - 'swfl' => 162, - 'sitx' => 163, - 'sit' => 163, - 'sv4cpio' => 164, - 'sv4crc' => 165, - 'tar' => 166, - 'gf' => 168, - 'pk' => 169, - 'texi' => 170, - 'texinfo' => 170, - 'sik' => 171, - '~' => 171, - 'bak' => 171, - '%' => 171, - 'old' => 171, - 't' => 172, - 'roff' => 172, - 'tr' => 172, - 'man' => 173, - 'me' => 174, - 'ms' => 175, - 'ustar' => 176, - 'src' => 177, - 'wz' => 178, - 'crt' => 179, - 'xcf' => 180, - 'fig' => 181, - 'xpi' => 182, - 'xht' => 183, - 'xhtml' => 183, - 'xml' => 184, - 'xsl' => 184, - 'zip' => 185, - 'au' => 186, - 'snd' => 186, - 'mid' => 187, - 'midi' => 187, - 'kar' => 187, - 'mpega' => 188, - 'mpga' => 188, - 'm4a' => 188, - 'mp3' => 188, - 'mp2' => 188, - 'oga' => 189, - 'spx' => 189, - 'sid' => 190, - 'aif' => 191, - 'aiff' => 191, - 'aifc' => 191, - 'gsm' => 192, - 'm3u' => 193, - 'wax' => 194, - 'wma' => 195, - 'rm' => 196, - 'ram' => 196, - 'ra' => 197, - 'pls' => 198, - 'sd2' => 199, - 'wav' => 200, - 'alc' => 201, - 'cac' => 202, - 'cache' => 202, - 'csf' => 203, - 'cascii' => 204, - 'cbin' => 204, - 'ctab' => 204, - 'cdx' => 205, - 'cer' => 206, - 'c3d' => 207, - 'chm' => 208, - 'cif' => 209, - 'cmdf' => 210, - 'cml' => 211, - 'cpa' => 212, - 'bsd' => 213, - 'csml' => 214, - 'csm' => 214, - 'ctx' => 215, - 'cxf' => 216, - 'cef' => 216, - 'emb' => 217, - 'embl' => 217, - 'spc' => 218, - 'gam' => 219, - 'inp' => 219, - 'gamin' => 219, - 'fchk' => 220, - 'fch' => 220, - 'cub' => 221, - 'gau' => 222, - 'gjf' => 222, - 'gjc' => 222, - 'gal' => 223, - 'gcg' => 224, - 'gen' => 225, - 'hin' => 226, - 'istr' => 227, - 'ist' => 227, - 'dx' => 228, - 'jdx' => 228, - 'kin' => 229, - 'mcm' => 230, - 'mmd' => 231, - 'mmod' => 231, - 'mol' => 232, - 'rd' => 233, - 'rxn' => 234, - 'sdf' => 235, - 'sd' => 235, - 'tgf' => 236, - 'mcif' => 237, - 'mol2' => 238, - 'b' => 239, - 'gpt' => 240, - 'mopcrt' => 241, - 'zmt' => 241, - 'mpc' => 241, - 'dat' => 241, - 'mop' => 241, - 'moo' => 242, - 'mvb' => 243, - 'prt' => 244, - 'aso' => 245, - 'val' => 245, - 'asn' => 246, - 'ent' => 247, - 'pdb' => 247, - 'ros' => 248, - 'sw' => 249, - 'vms' => 250, - 'vmd' => 251, - 'xtel' => 252, - 'xyz' => 253, - 'gif' => 254, - 'ief' => 255, - 'jpeg' => 256, - 'jpe' => 256, - 'jpg' => 256, - 'pcx' => 257, - 'png' => 258, - 'svgz' => 259, - 'svg' => 259, - 'tif' => 260, - 'tiff' => 260, - 'djvu' => 261, - 'djv' => 261, - 'ico' => 262, - 'wbmp' => 263, - 'ras' => 264, - 'cdr' => 265, - 'pat' => 266, - 'cdt' => 267, - 'cpt' => 268, - 'art' => 269, - 'jng' => 270, - 'bmp' => 271, - 'psd' => 272, - 'pnm' => 273, - 'pbm' => 274, - 'pgm' => 275, - 'ppm' => 276, - 'rgb' => 277, - 'xbm' => 278, - 'xpm' => 279, - 'xwd' => 280, - 'eml' => 281, - 'igs' => 282, - 'iges' => 282, - 'silo' => 283, - 'msh' => 283, - 'mesh' => 283, - 'icz' => 285, - 'ics' => 285, - 'css' => 286, - 'csv' => 287, - '323' => 288, - 'html' => 289, - 'htm' => 289, - 'shtml' => 289, - 'uls' => 290, - 'mml' => 291, - 'txt' => 292, - 'pot' => 292, - 'text' => 292, - 'asc' => 292, - 'rtx' => 293, - 'wsc' => 294, - 'sct' => 294, - 'tsv' => 295, - 'ts' => 296, - 'tm' => 296, - 'jad' => 297, - 'wml' => 298, - 'wmls' => 299, - 'bib' => 300, - 'boo' => 301, - 'hpp' => 302, - 'hh' => 302, - 'h++' => 302, - 'hxx' => 302, - 'cxx' => 303, - 'cc' => 303, - 'cpp' => 303, - 'c++' => 303, - 'h' => 304, - 'htc' => 305, - 'csh' => 306, - 'c' => 307, - 'patch' => 308, - 'diff' => 308, - 'd' => 309, - 'hs' => 310, - 'java' => 311, - 'lhs' => 312, - 'moc' => 313, - 'pas' => 314, - 'p' => 314, - 'gcd' => 315, - 'pm' => 316, - 'pl' => 316, - 'py' => 317, - 'etx' => 318, - 'sh' => 319, - 'tk' => 320, - 'tcl' => 320, - 'cls' => 321, - 'ltx' => 321, - 'sty' => 321, - 'tex' => 321, - 'vcs' => 322, - 'vcf' => 323, - '3gp' => 324, - 'dl' => 325, - 'dif' => 326, - 'dv' => 326, - 'fli' => 327, - 'gl' => 328, - 'mp4' => 329, - 'f4v' => 329, - 'f4p' => 329, - 'mpe' => 330, - 'mpeg' => 330, - 'mpg' => 330, - 'ogv' => 331, - 'qt' => 332, - 'mov' => 332, - 'mxu' => 333, - 'lsf' => 334, - 'lsx' => 334, - 'mng' => 335, - 'asx' => 336, - 'asf' => 336, - 'wm' => 337, - 'wmv' => 338, - 'wmx' => 339, - 'wvx' => 340, - 'avi' => 341, - 'movie' => 342, - 'ice' => 343, - 'sisx' => 344, - 'wrl' => 345, - 'vrm' => 345, - 'vrml' => 345, - 'f4a' => 346, - 'f4b' => 346, - 'flv' => 347, - ), - ); -} - -/** * file_check_directory() doesn't support creating directory trees. */ function _cdn_basic_farfuture_create_directory_structure($path) { diff --git README.txt README.txt index c7229ff..31ca4c6 100644 --- README.txt +++ README.txt @@ -56,6 +56,17 @@ To learn more about parallelizing downloads by using subdomains (or CDNs!), see http://drupal.org/project/parallel. +Module compatibility +-------------------- +This module has been verified to be compatible with the following modules: +- CSS Gzip + (This module is obsolete when using Origin Pull mode with the Far Future + expiration setting enabled.) +- ImageCache + (This module requires one of the patches that ships with the CDN module.) +- Javascript Aggregator + + Installation ------------ 1) Note: skip this step if you want to rely on the fallback mechanism! diff --git cdn.admin.inc cdn.admin.inc index c424457..77f07c6 100644 --- cdn.admin.inc +++ cdn.admin.inc @@ -152,7 +152,7 @@ function cdn_admin_details_form(&$form_state) { t("@format-url is the CDN URL that should be used. E.g.: 'http://cdn-a.com'.", $format_variables), t("@format-extensions is an optional - setting to limit which files should be served from a CDN. E.g.: + setting to limit which file types should be served from a CDN. E.g.: '.css .jpg .jpeg .png'.", $format_variables), ); $example_list_items = array( @@ -181,6 +181,114 @@ function cdn_admin_details_form(&$form_state) { '#dependency' => array('radio:' . CDN_MODE_VARIABLE => array(CDN_MODE_BASIC)), ); + $farfuture_extensions = variable_get(CDN_BASIC_FARFUTURE_EXTENSIONS_VARIABLE, CDN_BASIC_FARFUTURE_EXTENSIONS_DEFAULT); + $farfuture_variables = array( + '!extensions' => "." . implode(", .", explode("\n", $farfuture_extensions)) . "", + '!extensions-compressed' => "." . implode(", .", explode("\n", CDN_BASIC_FARFUTURE_GZIP_EXTENSIONS)) . "", + ); + $form['settings'][CDN_BASIC_FARFUTURE_VARIABLE] = array( + '#type' => 'checkbox', + '#title' => t('Far Future expiration'), + '#description' => t('Recommended for maximum performance boost! +
+ Mark all files served from the CDN to expire in the + far future (decades from now). This is the same as + telling browsers to always use the cached + files. This can significantly speed up page loads. + Files are also automatically compressed (when the + browser supports it), to speed up page loads even + further. +
+ Of course, you still want visitors to immediately get + new versions of files when they change. That is why + unique filenames are generated automatically. +
+ For the experts: the following HTTP + headers are set: Expires, + Cache-Control, + Last-Modified, Vary, for + files with one of the following extensions: !extensions + and of these extensions, some will also be + automatically compressed: !extensions-compressed.', $farfuture_variables + ), + '#default_value' => variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT), + '#process' => array('ctools_dependent_process'), + '#dependency' => array('radio:' . CDN_MODE_VARIABLE => array(CDN_MODE_BASIC)), + ); + + $format_variables = array( + '@format-directory' => '<' . t('directory') . '>', + '@format-extensions' => '<' . t('extensions') . '>', + '@format-unique-identifier-method' => '<' . t('unique identifier method') . '>', + ); + + $methods = array(); + $ufi_info = module_invoke_all('cdn_unique_file_identifier_info'); + foreach ($ufi_info as $ufi) { + $methods[] = $ufi['label'] + . ' (' . $ufi['machine_name'] . '): ' + . $ufi['description']; + } + $format_variables['!format-available-unique-identifier-methods'] = theme('item_list', $methods); + // '@format-available-unique-identifier-methods' + + + $format_explanation_list_items = array( + t("@format-directory is the directory (may include + wildcards) to which a unique identifier method will be applied. Multiple + directories may be listed, separated with semi-colons + (:). E.g.: + 'sites/*/modules/*:sites/*/themes/*'.", $format_variables), + t("@format-extensions is an optional + setting to limit which file types should use this unique identifier + method. E.g.: + '.css .jpg .jpeg .png'.", $format_variables), + t("@format-unique-identifier-method sets the unique + identifier method that should be applied to the aforementioned + directories, and only to (optionally) the listed file types. Available + methods are: !format-available-unique-identifier-methods.", + $format_variables), + ); + $example_list_items = array( + t("This would generate a unique identifier for Drupal core files based on + the Drupal core version, files in the site directory would get unique + identifiers based on the last time they were modified and movie files + would not receive a unique identifier (they're so large browser can't + cache them anyway): +
misc/*:modules/*:themes/*|drupal_version\nsites/*|mtime\nsites/*|.avi .m4v .mov .mp4 .wmv .flv|perpetual
"), + t("In this second example, we're dealing with a more high-traffic website, + where it is too costly to access the filesystem for every served file. + Therefor, this site defines a CDN_DEPLOYMENT_ID constant + somewhere in its codebase. This constant changes whenever a module or + theme changes. This is therefor far more efficient. See the last line: +
misc/*:modules/*:themes/*|drupal_version\nsites/*|mtime\nsites/*|.avi .m4v .mov .mp4 .wmv .flv|perpetual\nsites/*/modules/*:sites/*/themes/*|deployment_id
"), + ); + $form['settings'][CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE] = array( + '#type' => 'textarea', + '#title' => t('Unique file identifier generation'), + '#description' => t("Define how unique file identifiers are generated.
+
+ Enter one rule per line, in the format @format-directory[|@format-extensions]|@format-unique-identifier-method:
+ !format-explanation-list +

+ Note that if no unique file identifier generation + method is specified for a file because none of the + above rules apply to it, the CDN module will fall + back to the !default-ufi-method method. +

+ Sample mappings: + !example-list", $format_variables + array( + '!format-explanation-list' => theme('item_list', $format_explanation_list_items), + '!example-list' => theme('item_list', $example_list_items), + '!default-ufi-method' => '' . CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_DEFAULT . '', + )), + '#size' => 35, + '#default_value' => variable_get(CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE, CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_DEFAULT), + '#process' => array('ctools_dependent_process'), + '#dependency' => array('edit-cdn-farfuture-status' => array('1')), + ); + + // // Advanced mode settings. // @@ -243,7 +351,6 @@ function cdn_admin_other_settings_form(&$form_state) { '#type' => 'checkbox', '#title' => t('CDN supports HTTPS'), '#default_value' => variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE), - '#process' => array('ctools_dependent_process'), ); $path_explanation = t( diff --git cdn.basic.farfuture.inc cdn.basic.farfuture.inc new file mode 100644 index 0000000..6bd1e13 --- /dev/null +++ cdn.basic.farfuture.inc @@ -0,0 +1,1238 @@ +=5.3 + if (function_exists('header_remove')) { + header_remove($header); + } + else { + // In PHP <5.3, we cannot remove headers. At least shorten them to save + // every byte possible and to stop leaking information needlessly. + drupal_set_header($header . ':'); + } + } + } + + // Remove all previously set Cache-Control headers, because we're going to + // override it. Since multiple Cache-Control headers might have been set, + // simply setting a new, overriding header isn't enough: that would only + // override the *last* Cache-Control header. Yay for PHP! + if (function_exists('header_remove')) { + header_remove('Cache-Control'); + } + else { + drupal_set_header("Cache-Control:"); + drupal_set_header("Cache-Control:"); + } + + // Default caching rules: no caching/immediate expiration. + drupal_set_header("Cache-Control: private, must-revalidate, proxy-revalidate"); + drupal_set_header("Expires: " . gmdate("D, d M Y H:i:s", time() - 86400) . "GMT"); + + // Instead of being powered by PHP, tell the world this resource was powered + // by the CDN module! + drupal_set_header("X-Powered-By: Drupal CDN module"); + // Instruct intermediate HTTP caches to store both a compressed (gzipped) + // and uncompressed version of the resource. + drupal_set_header("Vary: Accept-Encoding"); + // Determine the content type. + drupal_set_header("Content-Type: " . _cdn_basic_farfuture_get_mimetype(basename($path))); + // Support partial content requests. + drupal_set_header("Accept-Ranges: bytes"); + + // If the extension of the file that's being served is one of the far future + // extensions (by default: images, fonts and flash content), then cache it + // in the far future. + $farfuture_extensions = variable_get(CDN_BASIC_FARFUTURE_EXTENSIONS_VARIABLE, CDN_BASIC_FARFUTURE_EXTENSIONS_DEFAULT); + $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); + if (in_array($extension, explode("\n", $farfuture_extensions))) { + // Remove all previously set Cache-Control headers, because we're going to + // override it. Since multiple Cache-Control headers might have been set, + // simply setting a new, overriding header isn't enough: that would only + // override the *last* Cache-Control header. Yay for PHP! + if (function_exists('header_remove')) { + header_remove('Cache-Control'); + } + else { + drupal_set_header("Cache-Control:"); + drupal_set_header("Cache-Control:"); + } + // Set a far future Cache-Control header (480 weeks), which prevents + // intermediate caches from transforming the data and allows any + // intermediate cache to cache it, since it's marked as a public resource. + drupal_set_header("Cache-Control: max-age=290304000, no-transform, public"); + // Set a far future Expires header. The maximum UNIX timestamp is somewhere + // in 2038. Set it to a date in 2037, just to be safe. + drupal_set_header("Expires: Tue, 20 Jan 2037 04:20:42 GMT"); + // Pretend the file was last modified a long time ago in the past, this will + // prevent browsers that don't support Cache-Control nor Expires headers to + // still request a new version too soon (these browsers calculate a + // heuristic to determine when to request a new version, based on the last + // time the resource has been modified). + // Also see http://code.google.com/speed/page-speed/docs/caching.html. + drupal_set_header("Last-Modified: Wed, 20 Jan 1988 04:20:42 GMT"); + } + + // GET requests with an "Accept-Encoding" header that lists "gzip". + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { + // Only send gzipped files for some file extensions (it doesn't make sense + // to gzip images, for example). + if (in_array($extension, explode("\n", CDN_BASIC_FARFUTURE_GZIP_EXTENSIONS))) { + // Ensure a gzipped version of the file is stored on disk, instead of + // gzipping the file on every request. + $gzip_path = file_directory_path() . '/' . CDN_BASIC_FARFUTURE_GZIP_DIRECTORY . "/$path.$mtime.gz"; + if (!file_exists($gzip_path)) { + _cdn_basic_farfuture_create_directory_structure(dirname($gzip_path)); + file_put_contents($gzip_path, gzencode(file_get_contents($path), 9)); + } + // Make sure zlib.output_compression does not gzip our gzipped output. + ini_set('zlib.output_compression', '0'); + // Prepare for gzipped output. + drupal_set_header("Content-Encoding: gzip"); + $path = $gzip_path; + } + } + + // Conditional GET requests (i.e. with If-Modified-Since header). + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + // All files served by this function are designed to expire in the far + // future. Hence we can simply always tell the client the requested file + // was not modified. + drupal_set_header("HTTP/1.1 304 Not Modified"); + } + // "Normal" GET requests. + else { + _cdn_transfer_file($path); + } + + exit(); +} + + +//---------------------------------------------------------------------------- +// Public functions. + +/** + * Gets the servers on which a file is available when basic mode is enabled. + * + * @param $path + * The path to get the servers for. + */ +function cdn_basic_farfuture_get_identifier($path) { + static $mapping; + static $ufi_info; + $servers = array(); + + // Gather all unique file identifier info. + if (!isset($ufi_info)) { + $ufi_info = module_invoke_all('cdn_unique_file_identifier_info'); + } + + // We only need to parse the textual CDN mapping once into a lookup table. + if (!isset($mapping)) { + $mapping = _cdn_basic_farfuture_parse_raw_mapping(variable_get(CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE, CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_DEFAULT)); + } + + // Determine which UFI method should be used. Note that we keep on trying to + // find another method until the end: the order of rules matters! + $ufi_method = FALSE; + foreach (array_keys($mapping) as $directory) { + if (drupal_match_path($path, $directory)) { + // Parse the file extension from the given path; convert it to lower case. + $file_extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); + + // Based on the file extension, determine which key should be used to find + // the CDN URLs in the mapping lookup table, if any. + if (array_key_exists($file_extension, $mapping[$directory])) { + $key = $file_extension; + } + elseif (array_key_exists('*', $mapping[$directory])) { + $key = '*'; + } + else { + $key = NULL; + } + + $ufi_method = $mapping[$directory][$key]; + } + } + + // Fall back to the default UFI method in case no UFI method is defined by + // the user. + if ($ufi_method === FALSE) { + $ufi_method = CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_DEFAULT; + } + + $prefix = $ufi_info[$ufi_method]['prefix']; + if (isset($ufi_info[$ufi_method]['value'])) { + $value = $ufi_info[$ufi_method]['value']; + } + else { + $callback = $ufi_info[$ufi_method]['callback']; + $value = call_user_func_array($callback, array($path)); + } + + return "$prefix:$value"; +} + + +//---------------------------------------------------------------------------- +// Private functions. + +/** + * Parse the raw (textual) mapping into a lookup table, where the key is the + * file extension and the value is a list of CDN URLs that serve the file. + * + * @param $mapping_raw + * A raw (textual) mapping. + * @return + * The corresponding mapping lookup table. + */ +function _cdn_basic_farfuture_parse_raw_mapping($mapping_raw) { + $mapping = array(); + + if (!empty($mapping_raw)) { + $lines = preg_split("/[\n\r]+/", $mapping_raw, -1, PREG_SPLIT_NO_EMPTY); + foreach ($lines as $line) { + // Parse this line. It may or may not limit the CDN URL to a list of + // file extensions. + $parts = explode('|', $line); + $directories = explode(':', $parts[0]); + + // There may be 2 or 3 parts: + // - part 1: directories + // - part 2: file extensions (optional) + // - part 3: unique file identifier method + if (count($parts) == 2) { + $extensions = array('*'); // Use the asterisk as a wildcard. + $ufi_method = strtolower(trim($parts[1])); + } + else if (count($parts) == 3) { + // Convert to lower case, remove periods, whitespace and split on ' '. + $extensions = explode(' ', trim(str_replace('.', '', strtolower($parts[1])))); + $ufi_method = strtolower(trim($parts[2])); + } + + // Create the mapping lookup table. + foreach ($directories as $directory) { + foreach ($extensions as $extension) { + $mapping[$directory][$extension] = $ufi_method; + } + } + } + } + + return $mapping; +} + +/** + * Variant of Drupal's file_transfer(), based on + * http://www.thomthom.net/blog/2007/09/php-resumable-download-server/ + * to support ranged requests as well. + * + * Note: ranged requests that request multiple ranges are not supported. They + * are responded to with a 416. See + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html + */ +function _cdn_transfer_file($path) { + $fp = @fopen($path, 'rb'); + + $size = filesize($path); // File size + $length = $size; // Content length + $start = 0; // Start byte + $end = $size - 1; // End byte + + // In case of a range request, seek within the file to the correct location. + if (isset($_SERVER['HTTP_RANGE'])) { + $c_start = $start; + $c_end = $end; + + // Extract the string containing the requested range. + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + + // If the client requested multiple ranges, repond with a 416. + if (strpos($range, ',') !== FALSE) { + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + exit(); + } + + // Case "Range: -n": final n bytes are requested. + if ($range[0] == '-') { + $c_start = $size - substr($range, 1); + } + // Case "Range: m-n": bytes m through n are requested. When n is empty or + // non-numeric, n is the last byte. + else { + $range = explode('-', $range); + $c_start = intval($range[0]); + $c_end = (isset($range[1]) && is_numeric($range[1])) ? intval($range[1]) : $size; + } + // Minor normalization: end bytes can not be larger than $end. + $c_end = ($c_end > $end) ? $end : $c_end; + + // If the requested range is not valid, respond with a 416. + if ($c_start > $c_end || $c_start > $end || $c_end > $end) { + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + exit(); + } + + $start = $c_start; + $end = $c_end; + $length = $end - $start + 1; + fseek($fp, $start); + + // The ranged request is valid and will be performed, respond with a 206. + header('HTTP/1.1 206 Partial Content'); + header("Content-Range: bytes $start-$end/$size"); + } + header("Content-Length: $length"); + + // Start buffered download. Prevent reading too far for a ranged request. + $buffer = 1024 * 8; + while (!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + $buffer = $end - $p + 1; + } + set_time_limit(0); // Reset time limit for big files. + echo fread($fp, $buffer); + flush(); // Free up memory, to prevent triggering PHP's memory limit. + } + fclose($fp); +} + +/** + * Determine an Internet Media Type, or MIME type from a filename. + * Borrowed from Drupal 7. + * + * @param $path + * A string containing the file path. + * @return + * The internet media type registered for the extension or + * application/octet-stream for unknown extensions. + * + * @see file_default_mimetype_mapping() + */ +function _cdn_basic_farfuture_get_mimetype($path) { + static $mapping; + + if (!isset($mapping)) { + $mapping = _cdn_basic_farfuture_mimetype_mapping();; + } + + $extension = ''; + $file_parts = explode('.', basename($path)); + + // Remove the first part: a full filename should not match an extension. + array_shift($file_parts); + + // Iterate over the file parts, trying to find a match. + // For my.awesome.image.jpeg, we try: + // - jpeg + // - image.jpeg, and + // - awesome.image.jpeg + while ($additional_part = array_pop($file_parts)) { + $extension = strtolower($additional_part . ($extension ? '.' . $extension : '')); + if (isset($mapping['extensions'][$extension])) { + return $mapping['mimetypes'][$mapping['extensions'][$extension]]; + } + } + + return 'application/octet-stream'; +} + +/** + * Default MIME extension mapping. + * Borrowed from Drupal 7. + * + * @return + * Array of mimetypes correlated to the extensions that relate to them. + */ +function _cdn_basic_farfuture_mimetype_mapping() { + return array( + 'mimetypes' => array( + 0 => 'application/andrew-inset', + 1 => 'application/atom', + 2 => 'application/atomcat+xml', + 3 => 'application/atomserv+xml', + 4 => 'application/cap', + 5 => 'application/cu-seeme', + 6 => 'application/dsptype', + 7 => 'application/hta', + 8 => 'application/java-archive', + 9 => 'application/java-serialized-object', + 10 => 'application/java-vm', + 11 => 'application/mac-binhex40', + 12 => 'application/mathematica', + 13 => 'application/msaccess', + 14 => 'application/msword', + 15 => 'application/octet-stream', + 16 => 'application/oda', + 17 => 'application/ogg', + 18 => 'application/pdf', + 19 => 'application/pgp-keys', + 20 => 'application/pgp-signature', + 21 => 'application/pics-rules', + 22 => 'application/postscript', + 23 => 'application/rar', + 24 => 'application/rdf+xml', + 25 => 'application/rss+xml', + 26 => 'application/rtf', + 27 => 'application/smil', + 28 => 'application/vnd.cinderella', + 29 => 'application/vnd.google-earth.kml+xml', + 30 => 'application/vnd.google-earth.kmz', + 31 => 'application/vnd.mozilla.xul+xml', + 32 => 'application/vnd.ms-excel', + 33 => 'application/vnd.ms-excel.addin.macroEnabled.12', + 34 => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 35 => 'application/vnd.ms-excel.sheet.macroEnabled.12', + 36 => 'application/vnd.ms-excel.template.macroEnabled.12', + 37 => 'application/vnd.ms-pki.seccat', + 38 => 'application/vnd.ms-pki.stl', + 39 => 'application/vnd.ms-powerpoint', + 40 => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', + 41 => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 42 => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', + 43 => 'application/vnd.ms-powerpoint.template.macroEnabled.12', + 44 => 'application/vnd.ms-word.document.macroEnabled.12', + 45 => 'application/vnd.ms-word.template.macroEnabled.12', + 46 => 'application/vnd.ms-xpsdocument', + 47 => 'application/vnd.oasis.opendocument.chart', + 48 => 'application/vnd.oasis.opendocument.database', + 49 => 'application/vnd.oasis.opendocument.formula', + 50 => 'application/vnd.oasis.opendocument.graphics', + 51 => 'application/vnd.oasis.opendocument.graphics-template', + 52 => 'application/vnd.oasis.opendocument.image', + 53 => 'application/vnd.oasis.opendocument.presentation', + 54 => 'application/vnd.oasis.opendocument.presentation-template', + 55 => 'application/vnd.oasis.opendocument.spreadsheet', + 56 => 'application/vnd.oasis.opendocument.spreadsheet-template', + 57 => 'application/vnd.oasis.opendocument.text', + 58 => 'application/vnd.oasis.opendocument.text-master', + 59 => 'application/vnd.oasis.opendocument.text-template', + 60 => 'application/vnd.oasis.opendocument.text-web', + 61 => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 62 => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 63 => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 64 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 65 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 66 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 67 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 68 => 'application/vnd.rim.cod', + 69 => 'application/vnd.smaf', + 70 => 'application/vnd.stardivision.calc', + 71 => 'application/vnd.stardivision.chart', + 72 => 'application/vnd.stardivision.draw', + 73 => 'application/vnd.stardivision.impress', + 74 => 'application/vnd.stardivision.math', + 75 => 'application/vnd.stardivision.writer', + 76 => 'application/vnd.stardivision.writer-global', + 77 => 'application/vnd.sun.xml.calc', + 78 => 'application/vnd.sun.xml.calc.template', + 79 => 'application/vnd.sun.xml.draw', + 80 => 'application/vnd.sun.xml.draw.template', + 81 => 'application/vnd.sun.xml.impress', + 82 => 'application/vnd.sun.xml.impress.template', + 83 => 'application/vnd.sun.xml.math', + 84 => 'application/vnd.sun.xml.writer', + 85 => 'application/vnd.sun.xml.writer.global', + 86 => 'application/vnd.sun.xml.writer.template', + 87 => 'application/vnd.symbian.install', + 88 => 'application/vnd.visio', + 89 => 'application/vnd.wap.wbxml', + 90 => 'application/vnd.wap.wmlc', + 91 => 'application/vnd.wap.wmlscriptc', + 92 => 'application/wordperfect', + 93 => 'application/wordperfect5.1', + 94 => 'application/x-123', + 95 => 'application/x-7z-compressed', + 96 => 'application/x-abiword', + 97 => 'application/x-apple-diskimage', + 98 => 'application/x-bcpio', + 99 => 'application/x-bittorrent', + 100 => 'application/x-cab', + 101 => 'application/x-cbr', + 102 => 'application/x-cbz', + 103 => 'application/x-cdf', + 104 => 'application/x-cdlink', + 105 => 'application/x-chess-pgn', + 106 => 'application/x-cpio', + 107 => 'application/x-debian-package', + 108 => 'application/x-director', + 109 => 'application/x-dms', + 110 => 'application/x-doom', + 111 => 'application/x-dvi', + 112 => 'application/x-flac', + 113 => 'application/x-font', + 114 => 'application/x-freemind', + 115 => 'application/x-futuresplash', + 116 => 'application/x-gnumeric', + 117 => 'application/x-go-sgf', + 118 => 'application/x-graphing-calculator', + 119 => 'application/x-gtar', + 120 => 'application/x-hdf', + 121 => 'application/x-httpd-eruby', + 122 => 'application/x-httpd-php', + 123 => 'application/x-httpd-php-source', + 124 => 'application/x-httpd-php3', + 125 => 'application/x-httpd-php3-preprocessed', + 126 => 'application/x-httpd-php4', + 127 => 'application/x-ica', + 128 => 'application/x-internet-signup', + 129 => 'application/x-iphone', + 130 => 'application/x-iso9660-image', + 131 => 'application/x-java-jnlp-file', + 132 => 'application/x-javascript', + 133 => 'application/x-jmol', + 134 => 'application/x-kchart', + 135 => 'application/x-killustrator', + 136 => 'application/x-koan', + 137 => 'application/x-kpresenter', + 138 => 'application/x-kspread', + 139 => 'application/x-kword', + 140 => 'application/x-latex', + 141 => 'application/x-lha', + 142 => 'application/x-lyx', + 143 => 'application/x-lzh', + 144 => 'application/x-lzx', + 145 => 'application/x-maker', + 146 => 'application/x-mif', + 147 => 'application/x-ms-wmd', + 148 => 'application/x-ms-wmz', + 149 => 'application/x-msdos-program', + 150 => 'application/x-msi', + 151 => 'application/x-netcdf', + 152 => 'application/x-ns-proxy-autoconfig', + 153 => 'application/x-nwc', + 154 => 'application/x-object', + 155 => 'application/x-oz-application', + 156 => 'application/x-pkcs7-certreqresp', + 157 => 'application/x-pkcs7-crl', + 158 => 'application/x-python-code', + 159 => 'application/x-quicktimeplayer', + 160 => 'application/x-redhat-package-manager', + 161 => 'application/x-shar', + 162 => 'application/x-shockwave-flash', + 163 => 'application/x-stuffit', + 164 => 'application/x-sv4cpio', + 165 => 'application/x-sv4crc', + 166 => 'application/x-tar', + 167 => 'application/x-tcl', + 168 => 'application/x-tex-gf', + 169 => 'application/x-tex-pk', + 170 => 'application/x-texinfo', + 171 => 'application/x-trash', + 172 => 'application/x-troff', + 173 => 'application/x-troff-man', + 174 => 'application/x-troff-me', + 175 => 'application/x-troff-ms', + 176 => 'application/x-ustar', + 177 => 'application/x-wais-source', + 178 => 'application/x-wingz', + 179 => 'application/x-x509-ca-cert', + 180 => 'application/x-xcf', + 181 => 'application/x-xfig', + 182 => 'application/x-xpinstall', + 183 => 'application/xhtml+xml', + 184 => 'application/xml', + 185 => 'application/zip', + 186 => 'audio/basic', + 187 => 'audio/midi', + 346 => 'audio/mp4', + 188 => 'audio/mpeg', + 189 => 'audio/ogg', + 190 => 'audio/prs.sid', + 191 => 'audio/x-aiff', + 192 => 'audio/x-gsm', + 193 => 'audio/x-mpegurl', + 194 => 'audio/x-ms-wax', + 195 => 'audio/x-ms-wma', + 196 => 'audio/x-pn-realaudio', + 197 => 'audio/x-realaudio', + 198 => 'audio/x-scpls', + 199 => 'audio/x-sd2', + 200 => 'audio/x-wav', + 201 => 'chemical/x-alchemy', + 202 => 'chemical/x-cache', + 203 => 'chemical/x-cache-csf', + 204 => 'chemical/x-cactvs-binary', + 205 => 'chemical/x-cdx', + 206 => 'chemical/x-cerius', + 207 => 'chemical/x-chem3d', + 208 => 'chemical/x-chemdraw', + 209 => 'chemical/x-cif', + 210 => 'chemical/x-cmdf', + 211 => 'chemical/x-cml', + 212 => 'chemical/x-compass', + 213 => 'chemical/x-crossfire', + 214 => 'chemical/x-csml', + 215 => 'chemical/x-ctx', + 216 => 'chemical/x-cxf', + 217 => 'chemical/x-embl-dl-nucleotide', + 218 => 'chemical/x-galactic-spc', + 219 => 'chemical/x-gamess-input', + 220 => 'chemical/x-gaussian-checkpoint', + 221 => 'chemical/x-gaussian-cube', + 222 => 'chemical/x-gaussian-input', + 223 => 'chemical/x-gaussian-log', + 224 => 'chemical/x-gcg8-sequence', + 225 => 'chemical/x-genbank', + 226 => 'chemical/x-hin', + 227 => 'chemical/x-isostar', + 228 => 'chemical/x-jcamp-dx', + 229 => 'chemical/x-kinemage', + 230 => 'chemical/x-macmolecule', + 231 => 'chemical/x-macromodel-input', + 232 => 'chemical/x-mdl-molfile', + 233 => 'chemical/x-mdl-rdfile', + 234 => 'chemical/x-mdl-rxnfile', + 235 => 'chemical/x-mdl-sdfile', + 236 => 'chemical/x-mdl-tgf', + 237 => 'chemical/x-mmcif', + 238 => 'chemical/x-mol2', + 239 => 'chemical/x-molconn-Z', + 240 => 'chemical/x-mopac-graph', + 241 => 'chemical/x-mopac-input', + 242 => 'chemical/x-mopac-out', + 243 => 'chemical/x-mopac-vib', + 244 => 'chemical/x-ncbi-asn1-ascii', + 245 => 'chemical/x-ncbi-asn1-binary', + 246 => 'chemical/x-ncbi-asn1-spec', + 247 => 'chemical/x-pdb', + 248 => 'chemical/x-rosdal', + 249 => 'chemical/x-swissprot', + 250 => 'chemical/x-vamas-iso14976', + 251 => 'chemical/x-vmd', + 252 => 'chemical/x-xtel', + 253 => 'chemical/x-xyz', + 254 => 'image/gif', + 255 => 'image/ief', + 256 => 'image/jpeg', + 257 => 'image/pcx', + 258 => 'image/png', + 259 => 'image/svg+xml', + 260 => 'image/tiff', + 261 => 'image/vnd.djvu', + 262 => 'image/vnd.microsoft.icon', + 263 => 'image/vnd.wap.wbmp', + 264 => 'image/x-cmu-raster', + 265 => 'image/x-coreldraw', + 266 => 'image/x-coreldrawpattern', + 267 => 'image/x-coreldrawtemplate', + 268 => 'image/x-corelphotopaint', + 269 => 'image/x-jg', + 270 => 'image/x-jng', + 271 => 'image/x-ms-bmp', + 272 => 'image/x-photoshop', + 273 => 'image/x-portable-anymap', + 274 => 'image/x-portable-bitmap', + 275 => 'image/x-portable-graymap', + 276 => 'image/x-portable-pixmap', + 277 => 'image/x-rgb', + 278 => 'image/x-xbitmap', + 279 => 'image/x-xpixmap', + 280 => 'image/x-xwindowdump', + 281 => 'message/rfc822', + 282 => 'model/iges', + 283 => 'model/mesh', + 284 => 'model/vrml', + 285 => 'text/calendar', + 286 => 'text/css', + 287 => 'text/csv', + 288 => 'text/h323', + 289 => 'text/html', + 290 => 'text/iuls', + 291 => 'text/mathml', + 292 => 'text/plain', + 293 => 'text/richtext', + 294 => 'text/scriptlet', + 295 => 'text/tab-separated-values', + 296 => 'text/texmacs', + 297 => 'text/vnd.sun.j2me.app-descriptor', + 298 => 'text/vnd.wap.wml', + 299 => 'text/vnd.wap.wmlscript', + 300 => 'text/x-bibtex', + 301 => 'text/x-boo', + 302 => 'text/x-c++hdr', + 303 => 'text/x-c++src', + 304 => 'text/x-chdr', + 305 => 'text/x-component', + 306 => 'text/x-csh', + 307 => 'text/x-csrc', + 308 => 'text/x-diff', + 309 => 'text/x-dsrc', + 310 => 'text/x-haskell', + 311 => 'text/x-java', + 312 => 'text/x-literate-haskell', + 313 => 'text/x-moc', + 314 => 'text/x-pascal', + 315 => 'text/x-pcs-gcd', + 316 => 'text/x-perl', + 317 => 'text/x-python', + 318 => 'text/x-setext', + 319 => 'text/x-sh', + 320 => 'text/x-tcl', + 321 => 'text/x-tex', + 322 => 'text/x-vcalendar', + 323 => 'text/x-vcard', + 324 => 'video/3gpp', + 325 => 'video/dl', + 326 => 'video/dv', + 327 => 'video/fli', + 328 => 'video/gl', + 329 => 'video/mp4', + 330 => 'video/mpeg', + 331 => 'video/ogg', + 332 => 'video/quicktime', + 333 => 'video/vnd.mpegurl', + 347 => 'video/x-flv', + 334 => 'video/x-la-asf', + 335 => 'video/x-mng', + 336 => 'video/x-ms-asf', + 337 => 'video/x-ms-wm', + 338 => 'video/x-ms-wmv', + 339 => 'video/x-ms-wmx', + 340 => 'video/x-ms-wvx', + 341 => 'video/x-msvideo', + 342 => 'video/x-sgi-movie', + 343 => 'x-conference/x-cooltalk', + 344 => 'x-epoc/x-sisx-app', + 345 => 'x-world/x-vrml', + ), + + // Extensions added to this list MUST be lower-case. + 'extensions' => array( + 'ez' => 0, + 'atom' => 1, + 'atomcat' => 2, + 'atomsrv' => 3, + 'cap' => 4, + 'pcap' => 4, + 'cu' => 5, + 'tsp' => 6, + 'hta' => 7, + 'jar' => 8, + 'ser' => 9, + 'class' => 10, + 'hqx' => 11, + 'nb' => 12, + 'mdb' => 13, + 'dot' => 14, + 'doc' => 14, + 'bin' => 15, + 'oda' => 16, + 'ogg' => 17, + 'ogx' => 17, + 'pdf' => 18, + 'key' => 19, + 'pgp' => 20, + 'prf' => 21, + 'eps' => 22, + 'ai' => 22, + 'ps' => 22, + 'rar' => 23, + 'rdf' => 24, + 'rss' => 25, + 'rtf' => 26, + 'smi' => 27, + 'smil' => 27, + 'cdy' => 28, + 'kml' => 29, + 'kmz' => 30, + 'xul' => 31, + 'xlb' => 32, + 'xlt' => 32, + 'xls' => 32, + 'xlam' => 33, + 'xlsb' => 34, + 'xlsm' => 35, + 'xltm' => 36, + 'cat' => 37, + 'stl' => 38, + 'pps' => 39, + 'ppt' => 39, + 'ppam' => 40, + 'pptm' => 41, + 'ppsm' => 42, + 'potm' => 43, + 'docm' => 44, + 'dotm' => 45, + 'xps' => 46, + 'odc' => 47, + 'odb' => 48, + 'odf' => 49, + 'odg' => 50, + 'otg' => 51, + 'odi' => 52, + 'odp' => 53, + 'otp' => 54, + 'ods' => 55, + 'ots' => 56, + 'odt' => 57, + 'odm' => 58, + 'ott' => 59, + 'oth' => 60, + 'pptx' => 61, + 'ppsx' => 62, + 'potx' => 63, + 'xlsx' => 64, + 'xltx' => 65, + 'docx' => 66, + 'dotx' => 67, + 'cod' => 68, + 'mmf' => 69, + 'sdc' => 70, + 'sds' => 71, + 'sda' => 72, + 'sdd' => 73, + 'sdw' => 75, + 'sgl' => 76, + 'sxc' => 77, + 'stc' => 78, + 'sxd' => 79, + 'std' => 80, + 'sxi' => 81, + 'sti' => 82, + 'sxm' => 83, + 'sxw' => 84, + 'sxg' => 85, + 'stw' => 86, + 'sis' => 87, + 'vsd' => 88, + 'wbxml' => 89, + 'wmlc' => 90, + 'wmlsc' => 91, + 'wpd' => 92, + 'wp5' => 93, + 'wk' => 94, + '7z' => 95, + 'abw' => 96, + 'dmg' => 97, + 'bcpio' => 98, + 'torrent' => 99, + 'cab' => 100, + 'cbr' => 101, + 'cbz' => 102, + 'cdf' => 103, + 'vcd' => 104, + 'pgn' => 105, + 'cpio' => 106, + 'udeb' => 107, + 'deb' => 107, + 'dir' => 108, + 'dxr' => 108, + 'dcr' => 108, + 'dms' => 109, + 'wad' => 110, + 'dvi' => 111, + 'flac' => 112, + 'pfa' => 113, + 'pfb' => 113, + 'pcf' => 113, + 'gsf' => 113, + 'pcf.z' => 113, + 'mm' => 114, + 'spl' => 115, + 'gnumeric' => 116, + 'sgf' => 117, + 'gcf' => 118, + 'taz' => 119, + 'gtar' => 119, + 'tgz' => 119, + 'hdf' => 120, + 'rhtml' => 121, + 'phtml' => 122, + 'pht' => 122, + 'php' => 122, + 'phps' => 123, + 'php3' => 124, + 'php3p' => 125, + 'php4' => 126, + 'ica' => 127, + 'ins' => 128, + 'isp' => 128, + 'iii' => 129, + 'iso' => 130, + 'jnlp' => 131, + 'js' => 132, + 'jmz' => 133, + 'chrt' => 134, + 'kil' => 135, + 'skp' => 136, + 'skd' => 136, + 'skm' => 136, + 'skt' => 136, + 'kpr' => 137, + 'kpt' => 137, + 'ksp' => 138, + 'kwd' => 139, + 'kwt' => 139, + 'latex' => 140, + 'lha' => 141, + 'lyx' => 142, + 'lzh' => 143, + 'lzx' => 144, + 'maker' => 145, + 'frm' => 145, + 'frame' => 145, + 'fm' => 145, + 'book' => 145, + 'fb' => 145, + 'fbdoc' => 145, + 'mif' => 146, + 'wmd' => 147, + 'wmz' => 148, + 'dll' => 149, + 'bat' => 149, + 'exe' => 149, + 'com' => 149, + 'msi' => 150, + 'nc' => 151, + 'pac' => 152, + 'nwc' => 153, + 'o' => 154, + 'oza' => 155, + 'p7r' => 156, + 'crl' => 157, + 'pyo' => 158, + 'pyc' => 158, + 'qtl' => 159, + 'rpm' => 160, + 'shar' => 161, + 'swf' => 162, + 'swfl' => 162, + 'sitx' => 163, + 'sit' => 163, + 'sv4cpio' => 164, + 'sv4crc' => 165, + 'tar' => 166, + 'gf' => 168, + 'pk' => 169, + 'texi' => 170, + 'texinfo' => 170, + 'sik' => 171, + '~' => 171, + 'bak' => 171, + '%' => 171, + 'old' => 171, + 't' => 172, + 'roff' => 172, + 'tr' => 172, + 'man' => 173, + 'me' => 174, + 'ms' => 175, + 'ustar' => 176, + 'src' => 177, + 'wz' => 178, + 'crt' => 179, + 'xcf' => 180, + 'fig' => 181, + 'xpi' => 182, + 'xht' => 183, + 'xhtml' => 183, + 'xml' => 184, + 'xsl' => 184, + 'zip' => 185, + 'au' => 186, + 'snd' => 186, + 'mid' => 187, + 'midi' => 187, + 'kar' => 187, + 'mpega' => 188, + 'mpga' => 188, + 'm4a' => 188, + 'mp3' => 188, + 'mp2' => 188, + 'oga' => 189, + 'spx' => 189, + 'sid' => 190, + 'aif' => 191, + 'aiff' => 191, + 'aifc' => 191, + 'gsm' => 192, + 'm3u' => 193, + 'wax' => 194, + 'wma' => 195, + 'rm' => 196, + 'ram' => 196, + 'ra' => 197, + 'pls' => 198, + 'sd2' => 199, + 'wav' => 200, + 'alc' => 201, + 'cac' => 202, + 'cache' => 202, + 'csf' => 203, + 'cascii' => 204, + 'cbin' => 204, + 'ctab' => 204, + 'cdx' => 205, + 'cer' => 206, + 'c3d' => 207, + 'chm' => 208, + 'cif' => 209, + 'cmdf' => 210, + 'cml' => 211, + 'cpa' => 212, + 'bsd' => 213, + 'csml' => 214, + 'csm' => 214, + 'ctx' => 215, + 'cxf' => 216, + 'cef' => 216, + 'emb' => 217, + 'embl' => 217, + 'spc' => 218, + 'gam' => 219, + 'inp' => 219, + 'gamin' => 219, + 'fchk' => 220, + 'fch' => 220, + 'cub' => 221, + 'gau' => 222, + 'gjf' => 222, + 'gjc' => 222, + 'gal' => 223, + 'gcg' => 224, + 'gen' => 225, + 'hin' => 226, + 'istr' => 227, + 'ist' => 227, + 'dx' => 228, + 'jdx' => 228, + 'kin' => 229, + 'mcm' => 230, + 'mmd' => 231, + 'mmod' => 231, + 'mol' => 232, + 'rd' => 233, + 'rxn' => 234, + 'sdf' => 235, + 'sd' => 235, + 'tgf' => 236, + 'mcif' => 237, + 'mol2' => 238, + 'b' => 239, + 'gpt' => 240, + 'mopcrt' => 241, + 'zmt' => 241, + 'mpc' => 241, + 'dat' => 241, + 'mop' => 241, + 'moo' => 242, + 'mvb' => 243, + 'prt' => 244, + 'aso' => 245, + 'val' => 245, + 'asn' => 246, + 'ent' => 247, + 'pdb' => 247, + 'ros' => 248, + 'sw' => 249, + 'vms' => 250, + 'vmd' => 251, + 'xtel' => 252, + 'xyz' => 253, + 'gif' => 254, + 'ief' => 255, + 'jpeg' => 256, + 'jpe' => 256, + 'jpg' => 256, + 'pcx' => 257, + 'png' => 258, + 'svgz' => 259, + 'svg' => 259, + 'tif' => 260, + 'tiff' => 260, + 'djvu' => 261, + 'djv' => 261, + 'ico' => 262, + 'wbmp' => 263, + 'ras' => 264, + 'cdr' => 265, + 'pat' => 266, + 'cdt' => 267, + 'cpt' => 268, + 'art' => 269, + 'jng' => 270, + 'bmp' => 271, + 'psd' => 272, + 'pnm' => 273, + 'pbm' => 274, + 'pgm' => 275, + 'ppm' => 276, + 'rgb' => 277, + 'xbm' => 278, + 'xpm' => 279, + 'xwd' => 280, + 'eml' => 281, + 'igs' => 282, + 'iges' => 282, + 'silo' => 283, + 'msh' => 283, + 'mesh' => 283, + 'icz' => 285, + 'ics' => 285, + 'css' => 286, + 'csv' => 287, + '323' => 288, + 'html' => 289, + 'htm' => 289, + 'shtml' => 289, + 'uls' => 290, + 'mml' => 291, + 'txt' => 292, + 'pot' => 292, + 'text' => 292, + 'asc' => 292, + 'rtx' => 293, + 'wsc' => 294, + 'sct' => 294, + 'tsv' => 295, + 'ts' => 296, + 'tm' => 296, + 'jad' => 297, + 'wml' => 298, + 'wmls' => 299, + 'bib' => 300, + 'boo' => 301, + 'hpp' => 302, + 'hh' => 302, + 'h++' => 302, + 'hxx' => 302, + 'cxx' => 303, + 'cc' => 303, + 'cpp' => 303, + 'c++' => 303, + 'h' => 304, + 'htc' => 305, + 'csh' => 306, + 'c' => 307, + 'patch' => 308, + 'diff' => 308, + 'd' => 309, + 'hs' => 310, + 'java' => 311, + 'lhs' => 312, + 'moc' => 313, + 'pas' => 314, + 'p' => 314, + 'gcd' => 315, + 'pm' => 316, + 'pl' => 316, + 'py' => 317, + 'etx' => 318, + 'sh' => 319, + 'tk' => 320, + 'tcl' => 320, + 'cls' => 321, + 'ltx' => 321, + 'sty' => 321, + 'tex' => 321, + 'vcs' => 322, + 'vcf' => 323, + '3gp' => 324, + 'dl' => 325, + 'dif' => 326, + 'dv' => 326, + 'fli' => 327, + 'gl' => 328, + 'mp4' => 329, + 'f4v' => 329, + 'f4p' => 329, + 'mpe' => 330, + 'mpeg' => 330, + 'mpg' => 330, + 'ogv' => 331, + 'qt' => 332, + 'mov' => 332, + 'mxu' => 333, + 'lsf' => 334, + 'lsx' => 334, + 'mng' => 335, + 'asx' => 336, + 'asf' => 336, + 'wm' => 337, + 'wmv' => 338, + 'wmx' => 339, + 'wvx' => 340, + 'avi' => 341, + 'movie' => 342, + 'ice' => 343, + 'sisx' => 344, + 'wrl' => 345, + 'vrm' => 345, + 'vrml' => 345, + 'f4a' => 346, + 'f4b' => 346, + 'flv' => 347, + ), + ); +} + +/** + * file_check_directory() doesn't support creating directory trees. + */ +function _cdn_basic_farfuture_create_directory_structure($path) { + // Create the directory structure in which the file will be stored. Because + // it's nested, file_check_directory() can't do this in one run. + $parts = explode('/', $path); + for ($i = 0; $i < count($parts); $i++) { + $directory = implode('/', array_slice($parts, 0, $i + 1)); + file_check_directory(file_create_path($directory), FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + } +} diff --git cdn.module cdn.module index e5a018d..f2b205f 100644 --- cdn.module +++ cdn.module @@ -5,7 +5,6 @@ * Implementation of the core hooks, defines, public and private functions. */ - define('CDN_DISABLED', 0); define('CDN_TESTING', 1); define('CDN_ENABLED', 2); @@ -38,6 +37,16 @@ define('CDN_EXCEPTION_AUTH_USERS_BLACKLIST_DEFAULT', ''); // Variables for basic mode. define('CDN_BASIC_MAPPING_VARIABLE', 'cdn_basic_mapping'); +define('CDN_BASIC_FARFUTURE_VARIABLE', 'cdn_farfuture_status'); +define('CDN_BASIC_FARFUTURE_DEFAULT', TRUE); +define('CDN_BASIC_FARFUTURE_EXTENSIONS_VARIABLE', "cdn_farfuture_extensions"); +define('CDN_BASIC_FARFUTURE_EXTENSIONS_DEFAULT', "css\njs\nsvg\nico\ngif\njpg\njpeg\npng\notf\nttf\neot\nwoff\nflv\nswf"); +define('CDN_BASIC_FARFUTURE_GZIP_EXTENSIONS', "css\njs\nico\nsvg\neot\notf\nttf"); +define('CDN_BASIC_FARFUTURE_GZIP_DIRECTORY', "cdn/farfuture/gzip"); +define('CDN_BASIC_FARFUTURE_REMOVE_HEADERS', "Set-Cookie"); +define('CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE', 'cdn_farfuture_unique_identifier_mapping'); +define('CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_DEFAULT', "misc/*:modules/*:themes/*|drupal_version\nsites/*|mtime\nsites/*|.avi .m4v .mov .mp4 .wmv .flv|perpetual"); +define('CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_DEFAULT', 'mtime'); // Variables for advanced mode. define('CDN_ADVANCED_SYNCED_FILES_DB_VARIABLE', 'cdn_advanced_synced_files_db'); @@ -58,6 +67,7 @@ define('CDN_DAEMON_PID_FILE', 'daemon.pid'); function cdn_file_url_alter(&$path) { $status = variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED); $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC); + $farfuture = variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT); $stats = variable_get(CDN_STATS_VARIABLE, FALSE) && user_access(CDN_PERM_ACCESS_STATS); $file_path_blacklist = variable_get(CDN_EXCEPTION_FILE_PATH_BLACKLIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_BLACKLIST_DEFAULT); $file_path_whitelist = variable_get(CDN_EXCEPTION_FILE_PATH_WHITELIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_WHITELIST_DEFAULT); @@ -89,7 +99,7 @@ function cdn_file_url_alter(&$path) { return; } } - + // If the current Drupal path matches one of the blacklisted Drupal paths, // return immediately. if (drupal_match_path($_GET['q'], $drupal_path_blacklist)) { @@ -106,6 +116,24 @@ function cdn_file_url_alter(&$path) { $start = microtime(); } + // Alter the file path when using Origin Pull mode and using that mode's + // Far Future setting. + if ($mode == CDN_MODE_BASIC && $farfuture) { + $path = urldecode($path); + // If the file does not yet exist, perform a normal HTTP request to this + // file, to generate it. (E.g. when ImageCache is used, this will + // generate the derivative file.) + if (!file_exists($path)) { + drupal_http_request(url($path, array('absolute' => TRUE))); + } + // Generate a unique file identifier (UFI). + cdn_load_include('basic.farfuture'); + $ufi = cdn_basic_farfuture_get_identifier($path); + // Generate the new path. + $path_before_farfuture = $path; + $path = "cdn/farfuture/$ufi/$path"; + } + // Load the include file that contains the logic for the mode that's // currently enabled. cdn_load_include(($mode == CDN_MODE_BASIC) ? 'basic' : 'advanced'); @@ -151,7 +179,8 @@ function cdn_file_url_alter(&$path) { // If the user can access it, add this to the per-page statistics. if ($stats) { $end = microtime(); - _cdn_devel_page_stats($path, $cdn_url, $server, $end - $start); + $source_path = ($mode == CDN_MODE_BASIC && $farfuture) ? $path_before_farfuture : $path; + _cdn_devel_page_stats($source_path, $cdn_url, $server, $end - $start); } // Override the path with the corresponding CDN URL, *if* the file is @@ -159,10 +188,75 @@ function cdn_file_url_alter(&$path) { if ($cdn_url !== FALSE) { $path = $cdn_url; } + else { + // If the file is not available on the CDN and Origin Pull mode's Far + // Future setting is enabled, then don't serve it using CDN module's + // non-scalable Far Future functionality, but using Drupal's default. + if ($mode == CDN_MODE_BASIC && $farfuture) { + $path = $path_before_farfuture; + } + } } } /** + * Implementation of hook_cdn_unique_file_identifier_info(). + */ +function cdn_cdn_unique_file_identifier_info() { + return array( + 'md5_hash' => array( + 'label' => t('MD5 hash'), + 'machine_name' => 'md5_hash', + 'prefix' => 'md5', + 'description' => t('MD5 hash of the file.'), + 'filesystem' => TRUE, + 'callback' => 'md5_file', + ), + 'mtime' => array( + 'label' => t('Last modification time'), + 'machine_name' => 'mtime', + 'prefix' => 'mtime', + 'description' => t('Last modification time of the file.'), + 'filesystem' => TRUE, + 'callback' => 'filemtime', + ), + 'perpetual' => array( + 'label' => t('Perpetual'), + 'machine_name' => 'perpetual', + 'prefix' => 'perpetual', + 'description' => t('Perpetual files never change (or are never cached + by the browser, e.g. video files).'), + 'filesystem' => FALSE, + 'value' => 'forever', + ), + 'drupal_version' => array( + 'label' => t('Drupal version'), + 'machine_name' => 'drupal_version', + 'prefix' => 'drupal', + 'description' => t('Drupal core version — this should only be applied + to files that ship with Drupal core.'), + 'filesystem' => FALSE, + 'value' => VERSION, + ), + 'deployment_id' => array( + 'label' => t('Deployment ID'), + 'machine_name' => 'deployment_id', + 'prefix' => 'deployment', + 'description' => t('A developer-defined deployment ID. Can be an + arbitrary string or number, as long as it uniquely + identifies deployments and therefore the affected + files.
+ Define this deployment ID in any enabled module or + in settings.php as the + CDN_DEPLOYMENT_ID + constant, and it will be picked up instantaneously.'), + 'filesystem' => FALSE, + 'callback' => '_cdn_ufi_deployment_id', + ), + ); +} + +/** * Implementation of hook_menu(). */ function cdn_menu() { @@ -214,6 +308,14 @@ function cdn_menu() { 'file' => 'cdn.stats.inc', ); + $items['cdn/farfuture'] = array( + 'title' => 'Download a far futured file', + 'access callback' => TRUE, + 'page callback' => 'cdn_basic_farfuture_download', + 'type' => MENU_CALLBACK, + 'file' => 'cdn.basic.farfuture.inc', + ); + return $items; } @@ -251,7 +353,6 @@ function cdn_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { } } - /** * Implementation of hook_theme(). */ @@ -422,6 +523,16 @@ function cdn_load_include($basename) { // Private functions. /** + * Callback for generating a unique file identifier. + * + * @param $path + * The file path to the file for which to generate a unique identifier. + */ +function _cdn_ufi_deployment_id($path) { + return CDN_DEPLOYMENT_ID; +} + +/** * Helper function to evaluate CDN_PICK_SERVER_PHP_CODE_VARIABLE, when that is * being used instead of an actual cdn_pick_server() function. *