php8
[web.mtrack] / MTrack / web.php
1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
3
4 die("web.php not used any more");
5 /* Simplistic pathinfo parsing - could optionally have additional features such
6   as validation added */
7 function mtrack_parse_pathinfo($vars) {
8   $pi = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
9   $data = explode('/', $pi);
10   $i = 0;
11   $return_vars = array();
12   array_shift($data);
13   foreach($vars as $name => $value) {
14     if (isset($data[$i])) {
15       $return_vars[$name] = $data[$i];
16       $i++;
17     } else {
18       $return_vars[$name] = $value;
19     }
20   }
21   return $return_vars;
22 }
23
24 /* Pathinfo retrieval minus starting slash */
25 function mtrack_get_pathinfo($no_strip = false) {
26   $pi = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : NULL;
27   if ($pi !== NULL && strlen($pi) && $no_strip == false) {
28     $pi = substr($pi, 1);
29   }
30   return $pi;
31 }
32
33 function mtrack_calc_root()
34 {
35   /* ABSWEB: the absolute URL to the base of the web app */
36   global $ABSWEB;
37
38   /* if they have one, use the weburl config value for this */
39   $ABSWEB = MTrackConfig::get('core', 'weburl');
40   if (strlen($ABSWEB)) {
41     return;
42   }
43
44   /* otherwise, determine the root of the app.
45    * This is complicated because the DOCUMENT_ROOT may refer to an area that
46    * is completely unrelated to the actual root of the web application, for
47    * instance, in the case that the user has a public_html dir where they
48    * are running mtrack */
49
50   /* determine the root of the app */
51   $sdir = dirname($_SERVER['SCRIPT_FILENAME']);
52   $idir = dirname(dirname(__FILE__)) . '/web';
53   $diff = substr($sdir, strlen($idir)+1);
54   $rel = preg_replace('@[^/]+@', '..', $diff);
55   if (strlen($rel)) {
56     $rel .= '/';
57   }
58   /* $rel is now the relative path to the root of the web app, from the current
59    * page */
60
61   if (isset($_SERVER['HTTP_HOST'])) {
62     $ABSWEB = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ?
63               'https' : 'http') . '://' .  $_SERVER['HTTP_HOST'];
64   } else {
65     $ABSWEB = 'http://localhost';
66   }
67
68   $bits = explode('/', $rel);
69   $base = $_SERVER['SCRIPT_NAME'];
70   foreach ($bits as $b) {
71     $base = dirname($base);
72   }
73   if ($base == '/') {
74     $ABSWEB .= '/';
75   } else {
76     $ABSWEB .= $base . '/';
77   }
78 }
79 mtrack_calc_root();
80
81 function mtrack_head($title, $navbar = true)
82 {
83   global $ABSWEB;
84   static $mtrack_did_head;
85
86   $whoami = mtrack_username(MTrackAuth::whoami(),
87     array(
88       'no_image' => true
89     )
90   );
91
92   if ($mtrack_did_head) {
93     return;
94   }
95   $mtrack_did_head = true;
96
97   $projectname = htmlentities(MTrackConfig::get('core', 'projectname'),
98     ENT_QUOTES, 'utf-8');
99   $logo = MTrackConfig::get('core', 'projectlogo');
100   if (strlen($logo)) {
101     $projectname = "<img alt='$projectname' src='$logo'>";
102   }
103   $fav = MTrackConfig::get('core', 'favicon');
104   if (strlen($fav)) {
105     $fav = <<<HTML
106 <link rel="icon" href="$fav" type="image/x-icon" />
107 <link rel="shortcut icon" href="$fav" type="image/x-icon" />
108 HTML;
109   } else {
110     $fav = '';
111   }
112
113   $title = htmlentities($title, ENT_QUOTES, 'utf-8');
114
115   $userinfo = "Logged in as $whoami";
116   MTrackNavigation::augmentUserInfo($userinfo);
117   
118   echo "<!DOCTYPE html>
119 <html>
120     <head>
121         <meta http-equiv=\"Content-Type\" value=\"text/html; charset=utf-8\">
122         <meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\">
123         <title>$title</title>
124         $fav
125 ";
126
127 /* For licensing and copyright terms, see the file named LICENSE */
128
129     $scripts = array(
130       'css/smoothness/jquery-ui-1.7.2.custom.css',
131       'mtrack.css',
132       'css/ticket.css',
133       'css/markitup/markitup-simple.css',
134       'css/markitup/wiki.css',
135       'css/hyperlight/plain.css',
136       'css/hyperlight/vibrant-ink.css',
137       'css/hyperlight/zenburn.css',
138       'css/hyperlight/wezterm.css',
139     );
140
141     foreach ($scripts as $name) {
142       
143       echo "<link rel=\"stylesheet\" href=\"${ABSWEB}$name\" type=\"text/css\" />";
144       
145     }
146
147  
148     $scripts = array(
149       'excanvas.pack.js',
150       'jquery-1.4.2.min.js',
151       'jquery-ui-1.8.2.custom.min.js',
152       'jquery.asmselect.js',
153       'jquery.flot.pack.js',
154       'jquery.MultiFile.pack.js',
155       'jquery.cookie.js',
156       'jquery.treeview.js',
157       'jquery.tablesorter.js',
158       'jquery.metadata.js',
159       'jquery.markitup.js',
160       'jquery.timeago.js',
161       'json2.js',
162       'mtrack.js',
163        'mtrack.ticket.js',
164         'mtrack.watch.js',
165     );
166     
167     echo '
168     <script type="text/javascript"> 
169     var ABSWEB = "'. $ABSWEB . '";
170     </script>
171     ';
172
173     foreach ($scripts as $name) {
174       echo "<script type=\"text/javascript\" src=\"$ABSWEB/js/$name\"></script>\n";
175     }
176
177
178     $PRI_SWITCH = '';
179     foreach (MTrackDB::q('select priorityname, value from priorities')
180         ->fetchAll() as $row) {
181       $PRI_SWITCH .= "case '$row[0]': return $row[1];\n";
182     }
183     $SEV_SWITCH = '';
184     foreach (MTrackDB::q('select sevname, ordinal from severities')
185         ->fetchAll() as $row) {
186       $SEV_SWITCH .= "case '$row[0]': return $row[1];\n";
187     }
188
189     echo <<<JAVASCRIPT
190
191     <script type="text/javsacript">
192
193     $(document).ready(function() {
194          
195       $.tablesorter.addParser({
196         id: 'priority',
197         is: function(s) {
198           // don't auto-detect
199           return false;
200         },
201         format: function(s) {
202           switch (s) {
203             $PRI_SWITCH
204           }
205           return s;
206         },
207         type: 'numeric'
208       });
209       $.tablesorter.addParser({
210         id: 'severity',
211         is: function(s) {
212           // don't auto-detect
213           return false;
214         },
215         format: function(s) {
216           switch (s) {
217             $SEV_SWITCH
218           }
219           return s;
220         },
221         type: 'numeric'
222       });
223       
224     </script>
225
226 JAVASCRIPT;
227   
228   
229   echo <<<HTML
230 </head>
231 <body>
232 HTML;
233
234   if ($navbar) {
235     echo <<<HTML
236 <div id="banner-back">
237   <form id="mainsearch" action="${ABSWEB}search.php">
238     $userinfo
239     <input type="text" class="search" title="Type and press enter to Search"
240         name="q" accesskey="f">
241   </form>
242   <div id="banner">
243     $projectname
244   </div>
245 HTML;
246
247     echo <<<HTML
248 <div id="header">
249 HTML;
250
251   $nav = array();
252   if (MTrackAuth::whoami() !== 'anonymous') {
253   //  $nav['/'] = 'Today';
254   }
255   $navcandidates = array(
256     "/Browse.php" => array("Browse", 'read', 'Browser'),
257    // "/wiki.php" => array("Wiki", 'read', 'Wiki'),
258     "/timeline.php" => array("Timeline", 'read', 'Timeline'),
259    // "/roadmap.php" => array("Roadmap", 'read', 'Roadmap'),
260     "/report.php/1" => array("Active Tickets", 'read', 'Reports'),
261     "/report.php/3" => array("Pending Review", 'read', 'Reports'),
262     "/Ticket.php/new" => array("New Ticket", 'create', 'Tickets'),
263    // "/snippet.php" => array("Snippets", 'read', 'Snippets'),
264     "/admin/" => array("Administration", 'modify', 'Enumerations', 'Components', 'Projects', 'Browser'),
265   );
266   foreach ($navcandidates as $url => $data) {
267     $label = array_shift($data);
268     $right = array_shift($data);
269     $ok = false;
270     foreach ($data as $object) {
271       if (MTrackACL::hasAllRights($object, $right)) {
272         $ok = true;
273         break;
274       }
275     }
276     if ($ok) {
277       $nav[$url] = $label;
278     }
279   }
280
281   echo mtrack_nav('mainnav', $nav);
282   echo <<<HTML
283   </div>
284 HTML;
285   }
286   if (MTrackConfig::get('core', 'admin_party') == 1 &&
287       MTrackAuth::whoami() == 'adminparty' &&
288       ($_SERVER['REMOTE_ADDR'] == '127.0.0.1' ||
289           $_SERVER['REMOTE_ADDR'] == '::1')) {
290     echo <<<HTML
291 <div class='ui-state-error ui-corner-all'>
292     <span class='ui-icon ui-icon-alert'></span>
293   <b>Welcome to the admin party!</b> Authentication is not yet configured;
294   while it is in this state, any user connecting from the localhost
295   address is treated as having admin rights (that includes you, and this
296   is why you are seeing this message). All other users are treated
297   as anonymous users.<br>
298   <b><a href="{$ABSWEB}admin/auth.php">Click here to Configure Authentication</a></b>
299 </div>
300 HTML;
301   } elseif (!MTrackAuth::isAuthConfigured() &&
302       MTrackConfig::get('core', 'admin_party') == 1)
303   {
304     $localaddr = preg_replace('@^(https?://)([^/]+)/(.*)$@',
305       "\\1localhost/\\3", $ABSWEB);
306
307     echo <<<HTML
308 <div class='ui-state-highlight ui-corner-all'>
309     <span class='ui-icon ui-icon-info'></span>
310   <b>Authentication is not yet configured</b>.  If you are the admin,
311   you should use the <b><a href="$localaddr">localhost address</a></b>
312   to reach the system and configure it.
313 </div>
314 HTML;
315   } elseif (!MTrackAuth::isAuthConfigured()) {
316     echo <<<HTML
317 <div class='ui-state-highlight ui-corner-all'>
318     <span class='ui-icon ui-icon-info'></span>
319   <b>Authentication is not yet configured</b>.  If you are the admin,
320   you will need to edit the config.ini file to configure authentication.
321 </div>
322 HTML;
323   }
324
325   if (ini_get('magic_quotes_gpc') === true ||
326       !strcasecmp(ini_get('magic_quotes_gpc'), 'on')) {
327     echo <<<HTML
328 <div class='ui-state-error ui-corner-all'>
329     <span class='ui-icon ui-icon-alert'></span>
330   <b>magic_quotes_gpc</b> is enabled.  This causes mtrack not to work.
331   Please disable this setting in your server configuration.
332 </div>
333 HTML;
334
335   }
336
337   echo <<<HTML
338 </div>
339 <div id="content">
340 HTML;
341 }
342
343 function mtrack_foot($visible_markup = true)
344 {
345   echo <<<HTML
346 </div>
347 HTML;
348   if ($visible_markup) {
349     echo <<<HTML
350 <div id="footer">
351 <div class="navfoot">
352   Powered by <a href="http://bitbucket.org/wez/mtrack/">mtrack</a>
353 </div>
354 </div>
355 </body>
356 <script>
357 \$(document).ready(function () {
358   window.mtrack_footer_position();
359 });
360 </script>
361 </html>
362 HTML;
363     if (MTrackConfig::get('core', 'debug.footer')) {
364       global $FORKS;
365
366       echo "<!-- " . MTrackDB::$queries . " queries\n";
367       var_export(MTrackDB::$query_strings);
368       echo "\n\nforks\n\n";
369       var_export($FORKS);
370       echo "-->";
371     }
372   }
373 }
374
375 interface IMTrackExtensionPage {
376   /** called to dispatch a page render */
377   function dispatchRequest();
378 }
379
380 class MTrackExtensionPage {
381   static $locations = array();
382   static function registerLocation($location, IMTrackExtensionPage $page) {
383     self::$locations[$location] = $page;
384   }
385   static function locationToURL($location) {
386     global $ABSWEB;
387     return $ABSWEB . 'ext.php/' . $location;
388   }
389   static function bindToPage($location) {
390     while (strlen($location)) {
391       if (isset(self::$locations[$location])) {
392         return self::$locations[$location];
393       }
394       if (strpos($location, '/') === false) {
395         return null;
396       }
397       $location = dirname($location);
398     }
399   }
400 }
401  
402 function mtrack_date($tstring, $show_full = false)
403 {
404   /* database time is always relative to UTC */
405   $d = date_create($tstring, new DateTimeZone('UTC'));
406   if (!is_object($d)) {
407     throw new Exception("could not represent $tstring as a datetime object");
408   }
409   $iso8601 = $d->format(DateTime::W3C);
410   /* but we want to render relative to user prefs */
411   date_timezone_set($d, new DateTimeZone(date_default_timezone_get()));
412   $full = $d->format('D, M d Y H:i');
413
414   if (!$show_full) {
415     return "<abbr title=\"$iso8601\" class='timeinterval'>$full</abbr>";
416   }
417
418   return "<abbr title='$iso8601' class='timeinterval'>$full</abbr> <span class='fulldate'>$full</span>";
419 }
420
421 function mtrack_rmdir($dir)
422 {
423   foreach (scandir($dir) as $ent) {
424     if ($ent == '.' || $ent == '..') {
425       continue;
426     }
427     $full = $dir . DIRECTORY_SEPARATOR . $ent;
428     if (is_dir($full)) {
429       mtrack_rmdir($full);
430     } else {
431       unlink($full);
432     }
433   }
434   rmdir($dir);
435 }
436
437 function mtrack_make_temp_dir($do_make = true)
438 {
439   $tempdir = sys_get_temp_dir();
440   $base = $tempdir . DIRECTORY_SEPARATOR . "mtrack." . uniqid();
441   for ($i = 0; $i < 1024; $i++) {
442     $candidate = $base . sprintf("%04x", $i);
443     if ($do_make) {
444       if (mkdir($candidate)) {
445         return $candidate;
446       }
447     } else {
448       /* racy */
449       if (!file_exists($candidate) && !is_dir($candidate)) {
450         return $candidate;
451       }
452     }
453   }
454   throw new Exception("unable to make temp dir based on path $candidate");
455 }
456 /// moved to mtrack_dataobjects_event (currently)
457 //function mtrack_diff_strings($before, $now)
458
459
460 function mtrack_last_chance_saloon($e)
461 {
462   // adding headers on exceptions here causes nightmares for dynamic content..
463   
464   if ($e instanceof MTrackAuthorizationException) {
465     if (MTrackAuth::whoami() == 'anonymous') {
466       MTrackAuth::forceAuthenticate();
467     }
468   //  mtrack_head('Insufficient Privilege');
469     echo '<h1>Insufficient Privilege</h1>';
470     $rights = is_array($e->rights) ? join(', ', $e->rights) : $e->rights;
471     echo "You do not have the required set of rights ($rights) to access this page<br>";
472     //mtrack_foot();
473     exit;
474   }
475
476   $msg = $e->getMessage();
477
478   try {
479   //  mtrack_head('Whoops: ' . $msg);
480   } catch (Exception $doublefault) {
481   }
482
483   echo "<h1>An error occurred!</h1>";
484
485   echo htmlentities($msg, ENT_QUOTES, 'utf-8');
486
487   echo "<br>";
488
489   echo nl2br(htmlentities($e->getTraceAsString(), ENT_QUOTES, 'utf-8'));
490
491   try {
492   //  mtrack_foot();
493   } catch (Exception $doublefault) {
494   }
495 }
496  
497
498  
499 function mtrack_gravatar($email, $size = 24)
500 {
501   // d=identicon
502   // d=monsterid
503   // d=wavatar
504   return "<img class='gravatar' width='$size' height='$size' src='http://www.gravatar.com/avatar/" .  md5(strtolower($email)) . "?s=$size&amp;d=wavatar'>";
505 }
506 // mtrack_defrepo ==> MTrack_Repo::defaultRepo($cfg); 
507 // mtrack_changeset_url ==> MTrack_Repo::loadByChangeSet = see  linkhandler::changeset
508  
509  
510 function mtrack_tag($tag, $repo = null)
511 {
512   return "<span class='tagname'>$tag</span>";
513 }
514
515 function mtrack_keyword($keyword)
516 {
517   global $ABSWEB;
518   $kw = urlencode($keyword);
519   return "<a class='keyword' href='{$ABSWEB}search.php?q=keyword%3A$kw'>$keyword</span>";
520 }
521
522 function mtrack_multi_select_box($name, $title, $items, $values = null)
523 {
524   $title = htmlentities($title, ENT_QUOTES, 'utf-8');
525   $html = "<select id='$name' name='{$name}[]' multiple='multiple' title='$title'>";
526   foreach ($items as $k => $v) {
527     $html .= "<option value='" .
528       htmlspecialchars($k, ENT_QUOTES, 'utf-8') .
529       "'";
530     if (isset($values[$k])) {
531       $html .= ' selected';
532     }
533     $html .= ">" . htmlentities($v, ENT_QUOTES, 'utf-8') . "</option>\n";
534   }
535   return $html . "</select>";
536 }
537
538 function mtrack_select_box($name, $items, $value = null, $keyed = true)
539 {
540   $html = "<select id='$name' name='$name'>";
541   foreach ($items as $k => $v) {
542     $html .= "<option value='" .
543       htmlspecialchars($k, ENT_QUOTES, 'utf-8') .
544       "'";
545     if (($keyed && $value == $k) || (!$keyed && $value == $v)) {
546       $html .= ' selected';
547     }
548     $html .= ">" . htmlentities($v, ENT_QUOTES, 'utf-8') . "</option>\n";
549   }
550   return $html . "</select>";
551 }
552
553 function mtrack_radio($name, $value, $curval)
554 {
555   $checked = $curval == $value ? " checked='checked'": '';
556   return "<input type='radio' id='$value' name='$name' value='$value'$checked>";
557 }
558
559 function mtrack_diff($diffstr)
560 {
561   $nlines = 0;
562
563   if (is_resource($diffstr)) {
564     $lines = array();
565     while (($line = fgets($diffstr)) !== false) {
566       $lines[] = rtrim($line, "\r\n");
567     }
568     $diffstr = $lines;
569   }
570
571   if (is_string($diffstr)) {
572     $abase = md5($diffstr);
573     $diffstr = preg_split("/\r?\n/", $diffstr);
574   } else {
575     $abase = md5(join("\n", $diffstr));
576   }
577
578   /* we could use toggle() below, but it is much faster to determine
579    * if we are hiding or showing based on a single variable than evaluating
580    * that for each possible cell */
581   $html = <<<HTML
582 <button class='togglediffcopy' type='button'>Toggle Diff Line Numbers</button>
583 HTML;
584   $html .= "<table class='code diff'>";
585   //$html = "<pre class='code diff'>";
586
587   while (true) {
588     if (!count($diffstr)) {
589       break;
590     }
591     $line = array_shift($diffstr);
592     $nlines++;
593     if (!strncmp($line, '@@ ', 3)) {
594       /* done with preamble */
595       break;
596     }
597     $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
598     $line = "<tr class='meta'><td class='lineno'></td><td class='lineno'></td><td class='lineno'></td><td width='100%'>$line</tr>";
599     $html .= $line . "\n";
600   }
601
602   $lines = array(0, 0);
603   $first = false;
604   while (true) {
605     $class = 'unmod';
606
607     if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
608         $line, $M)) {
609       $lines[0] = (int)$M[1] - 1;
610       $lines[1] = (int)$M[2] - 1;
611       $class = 'meta';
612       $first = true;
613     } elseif (strlen($line)) {
614       if ($line[0] == '-') {
615         $lines[0]++;
616         $class = 'removed';
617       } elseif ($line[0] == '+') {
618         $lines[1]++;
619         $class = 'added';
620       } else {
621         $lines[0]++;
622         $lines[1]++;
623       }
624     } else {
625       $lines[0]++;
626       $lines[1]++;
627     }
628     $row = "<tr class='$class";
629     if ($first) {
630       $row .= ' first';
631     }
632     if ($class != 'meta' && $first) {
633       $first = false;
634     }
635     $row .= "'>";
636
637     switch ($class) {
638       case 'meta':
639         $line_info = '';
640         $row .= "<td class='lineno'></td><td class='lineno'></td>";
641         break;
642       case 'added':
643         $row .= "<td class='lineno'></td><td class='lineno'>" . $lines[1] . "</td>";
644         break;
645       case 'removed':
646         $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'></td>";
647         break;
648       default:
649         $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'>" . $lines[1] . "</td>";
650     }
651     $anchor = $abase . '.' . $nlines;
652     $row .= "<td class='linelink'><a name='$anchor'></a><a href='#$anchor' title='link to this line'>#</a></td>";
653
654     $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
655     $row .= "<td class='line' width='100%'>$line</td></tr>\n";
656     $html .= $row;
657
658     if (!count($diffstr)) {
659       break;
660     }
661     $line = array_shift($diffstr);
662     $nlines++;
663   }
664
665   if ($nlines == 0) {
666     return null;
667   }
668
669   $html .= "</table>";
670   return $html;
671 }
672
673 function mtrack_mime_detect($filename, $namehint = null)
674 {
675   /* does config tell us how to decide mimetype */
676   $detector = MTrackConfig::get('core', 'mimetype_detect');
677
678   /* if detector is blank, we'll try to figure out which one to use */
679   if (empty($detector)) {
680     if (function_exists('finfo_open')) {
681       $detector = 'fileinfo';
682     } elseif (function_exists('mime_content_type')) {
683       $detector = 'mime_magic';
684     } else {
685       $detector = 'file';
686     }
687   }
688
689   /* use detector or all mimetypes will be blank */
690   if ($detector === 'fileinfo') {
691     if (defined('FILEINFO_MIME_TYPE')) {
692       $fileinfo = finfo_open(FILEINFO_MIME_TYPE);
693     } else {
694       $magic = MTrackConfig::get('core', 'mime.magic');
695       if (strlen($magic)) {
696         $fileinfo = finfo_open(FILEINFO_MIME, $magic);
697       } else {
698         $fileinfo = finfo_open(FILEINFO_MIME);
699       }
700     }
701     $mimetype = finfo_file($fileinfo, $filename);
702     finfo_close($fileinfo);
703   } elseif ($detector === 'mime_magic') {
704     $mimetype = mime_content_type($filename);
705   } elseif (PHP_OS != 'SunOS') {
706     $mimetype = shell_exec("file -b --mime " . escapeshellarg($filename));
707   } else {
708     $mimetype = 'application/octet-stream';
709   }
710   $mimetype = trim(preg_replace("/\s*;.*$/", '', $mimetype));
711   if (empty($mimetype)) {
712     $mimetype = 'application/octet-stream';
713   }
714   if ($mimetype == 'application/octet-stream') {
715     if ($namehint === null) {
716       $namehint = $filename;
717     }
718     $pi = pathinfo($namehint);
719     switch (strtolower($pi['extension'])) {
720       case 'bin': return 'application/octet-stream';
721       case 'exe': return 'application/octet-stream';
722       case 'dll': return 'application/octet-stream';
723       case 'iso': return 'application/octet-stream';
724       case 'so': return 'application/octet-stream';
725       case 'a': return 'application/octet-stream';
726       case 'lib': return 'application/octet-stream';
727       case 'pdf': return 'application/pdf';
728       case 'ps': return 'application/postscript';
729       case 'ai': return 'application/postscript';
730       case 'eps': return 'application/postscript';
731       case 'ppt': return 'application/vnd.ms-powerpoint';
732       case 'xls': return 'application/vnd.ms-excel';
733       case 'tiff': return 'image/tiff';
734       case 'tif': return 'image/tiff';
735       case 'wbmp': return 'image/vnd.wap.wbmp';
736       case 'png': return 'image/png';
737       case 'gif': return 'image/gif';
738       case 'jpg': return 'image/jpeg';
739       case 'jpeg': return 'image/jpeg';
740       case 'ico': return 'image/x-icon';
741       case 'bmp': return 'image/bmp';
742       case 'css': return 'text/css';
743       case 'htm': return 'text/html';
744       case 'html': return 'text/html';
745       case 'txt': return 'text/plain';
746       case 'xml': return 'text/xml';
747       case 'eml': return 'message/rfc822';
748       case 'asc': return 'text/plain';
749       case 'rtf': return 'application/rtf';
750       case 'wml': return 'text/vnd.wap.wml';
751       case 'wmls': return 'text/vnd.wap.wmlscript';
752       case 'gtar': return 'application/x-gtar';
753       case 'gz': return 'application/x-gzip';
754       case 'tgz': return 'application/x-gzip';
755       case 'tar': return 'application/x-tar';
756       case 'zip': return 'application/zip';
757       case 'sql': return 'text/plain';
758     }
759     // if the file is ascii, then treat it as text/plain
760     $fp = fopen($filename, 'rb');
761     $mimetype = 'text/plain';
762     do {
763       $x = fread($fp, 8192);
764       if (!strlen($x)) break;
765       if (preg_match('/([\x80-\xff])/', $x, $M)) {
766         $mimetype = 'application/octet-stream';
767         break;
768       }
769     } while (true);
770     $fp = null;
771   }
772   return $mimetype;
773 }
774  
775
776 if (php_sapi_name() != 'cli') {
777   set_exception_handler('mtrack_last_chance_saloon');
778   error_reporting(E_NOTICE|E_ERROR|E_WARNING);
779  // ini_set('display_errors', false);
780   set_time_limit(300);
781 }
782
783