1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 /* Simplistic pathinfo parsing - could optionally have additional features such
6 function mtrack_parse_pathinfo($vars) {
7 $pi = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
8 $data = explode('/', $pi);
10 $return_vars = array();
12 foreach($vars as $name => $value) {
13 if (isset($data[$i])) {
14 $return_vars[$name] = $data[$i];
17 $return_vars[$name] = $value;
23 /* Pathinfo retrieval minus starting slash */
24 function mtrack_get_pathinfo($no_strip = false) {
25 $pi = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : NULL;
26 if ($pi !== NULL && strlen($pi) && $no_strip == false) {
32 function mtrack_calc_root()
34 /* ABSWEB: the absolute URL to the base of the web app */
37 /* if they have one, use the weburl config value for this */
38 $ABSWEB = MTrackConfig::get('core', 'weburl');
39 if (strlen($ABSWEB)) {
43 /* otherwise, determine the root of the app.
44 * This is complicated because the DOCUMENT_ROOT may refer to an area that
45 * is completely unrelated to the actual root of the web application, for
46 * instance, in the case that the user has a public_html dir where they
47 * are running mtrack */
49 /* determine the root of the app */
50 $sdir = dirname($_SERVER['SCRIPT_FILENAME']);
51 $idir = dirname(dirname(__FILE__)) . '/web';
52 $diff = substr($sdir, strlen($idir)+1);
53 $rel = preg_replace('@[^/]+@', '..', $diff);
57 /* $rel is now the relative path to the root of the web app, from the current
60 if (isset($_SERVER['HTTP_HOST'])) {
61 $ABSWEB = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ?
62 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
64 $ABSWEB = 'http://localhost';
67 $bits = explode('/', $rel);
68 $base = $_SERVER['SCRIPT_NAME'];
69 foreach ($bits as $b) {
70 $base = dirname($base);
75 $ABSWEB .= $base . '/';
80 function mtrack_head($title, $navbar = true)
83 static $mtrack_did_head;
85 $whoami = mtrack_username(MTrackAuth::whoami(),
91 if ($mtrack_did_head) {
94 $mtrack_did_head = true;
96 $projectname = htmlentities(MTrackConfig::get('core', 'projectname'),
98 $logo = MTrackConfig::get('core', 'projectlogo');
100 $projectname = "<img alt='$projectname' src='$logo'>";
102 $fav = MTrackConfig::get('core', 'favicon');
105 <link rel="icon" href="$fav" type="image/x-icon" />
106 <link rel="shortcut icon" href="$fav" type="image/x-icon" />
112 $title = htmlentities($title, ENT_QUOTES, 'utf-8');
114 $userinfo = "Logged in as $whoami";
115 MTrackNavigation::augmentUserInfo($userinfo);
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>
125 <link rel="stylesheet" href="${ABSWEB}css.php" type="text/css" />
126 <script language="javascript" type="text/javascript" src="${ABSWEB}js.php"></script>
133 <div id="banner-back">
134 <form id="mainsearch" action="${ABSWEB}search.php">
136 <input type="text" class="search" title="Type and press enter to Search"
137 name="q" accesskey="f">
149 if (MTrackAuth::whoami() !== 'anonymous') {
152 $navcandidates = array(
153 "/browse.php" => array("Browse", 'read', 'Browser'),
154 "/wiki.php" => array("Wiki", 'read', 'Wiki'),
155 "/timeline.php" => array("Timeline", 'read', 'Timeline'),
156 "/roadmap.php" => array("Roadmap", 'read', 'Roadmap'),
157 "/reports.php" => array("Reports", 'read', 'Reports'),
158 "/ticket.php/new" => array("New Ticket", 'create', 'Tickets'),
159 "/snippet.php" => array("Snippets", 'read', 'Snippets'),
160 "/admin/" => array("Administration", 'modify', 'Enumerations', 'Components', 'Projects', 'Browser'),
162 foreach ($navcandidates as $url => $data) {
163 $label = array_shift($data);
164 $right = array_shift($data);
166 foreach ($data as $object) {
167 if (MTrackACL::hasAllRights($object, $right)) {
177 echo mtrack_nav('mainnav', $nav);
182 if (MTrackConfig::get('core', 'admin_party') == 1 &&
183 MTrackAuth::whoami() == 'adminparty' &&
184 ($_SERVER['REMOTE_ADDR'] == '127.0.0.1' ||
185 $_SERVER['REMOTE_ADDR'] == '::1')) {
187 <div class='ui-state-error ui-corner-all'>
188 <span class='ui-icon ui-icon-alert'></span>
189 <b>Welcome to the admin party!</b> Authentication is not yet configured;
190 while it is in this state, any user connecting from the localhost
191 address is treated as having admin rights (that includes you, and this
192 is why you are seeing this message). All other users are treated
193 as anonymous users.<br>
194 <b><a href="{$ABSWEB}admin/auth.php">Click here to Configure Authentication</a></b>
197 } elseif (!MTrackAuth::isAuthConfigured() &&
198 MTrackConfig::get('core', 'admin_party') == 1)
200 $localaddr = preg_replace('@^(https?://)([^/]+)/(.*)$@',
201 "\\1localhost/\\3", $ABSWEB);
204 <div class='ui-state-highlight ui-corner-all'>
205 <span class='ui-icon ui-icon-info'></span>
206 <b>Authentication is not yet configured</b>. If you are the admin,
207 you should use the <b><a href="$localaddr">localhost address</a></b>
208 to reach the system and configure it.
211 } elseif (!MTrackAuth::isAuthConfigured()) {
213 <div class='ui-state-highlight ui-corner-all'>
214 <span class='ui-icon ui-icon-info'></span>
215 <b>Authentication is not yet configured</b>. If you are the admin,
216 you will need to edit the config.ini file to configure authentication.
221 if (ini_get('magic_quotes_gpc') === true ||
222 !strcasecmp(ini_get('magic_quotes_gpc'), 'on')) {
224 <div class='ui-state-error ui-corner-all'>
225 <span class='ui-icon ui-icon-alert'></span>
226 <b>magic_quotes_gpc</b> is enabled. This causes mtrack not to work.
227 Please disable this setting in your server configuration.
239 function mtrack_foot($visible_markup = true)
244 if ($visible_markup) {
247 <div class="navfoot">
248 Powered by <a href="http://bitbucket.org/wez/mtrack/">mtrack</a>
253 \$(document).ready(function () {
254 window.mtrack_footer_position();
259 if (MTrackConfig::get('core', 'debug.footer')) {
262 echo "<!-- " . MTrackDB::$queries . " queries\n";
263 var_export(MTrackDB::$query_strings);
264 echo "\n\nforks\n\n";
271 interface IMTrackExtensionPage {
272 /** called to dispatch a page render */
273 function dispatchRequest();
276 class MTrackExtensionPage {
277 static $locations = array();
278 static function registerLocation($location, IMTrackExtensionPage $page) {
279 self::$locations[$location] = $page;
281 static function locationToURL($location) {
283 return $ABSWEB . 'ext.php/' . $location;
285 static function bindToPage($location) {
286 while (strlen($location)) {
287 if (isset(self::$locations[$location])) {
288 return self::$locations[$location];
290 if (strpos($location, '/') === false) {
293 $location = dirname($location);
298 interface IMTrackNavigationHelper {
299 /** called by mtrack_nav
300 * You may remove items from or add items to the items array by
301 * changing the $items array.
302 * Should you want to suppress the Wiki from navigation, you may
304 * if ($id == 'mainnav') {
305 * unset($items['/wiki.php']);
307 * If you want to add an item, the key is the URL and the value
308 * is the label. The label is raw HTML.
310 function augmentNavigation($id, &$items);
312 /** called by mtrack_head
313 * You may augment or override the "Logged in as user" text by
314 * changing the $content variable */
315 function augmentUserInfo(&$content);
318 class MTrackNavigation {
319 static $helpers = array();
321 static function registerHelper(IMTrackNavigationHelper $helper)
323 self::$helpers[] = $helper;
326 static function augmentNavigation($id, &$items)
328 foreach (self::$helpers as $helper) {
329 $helper->augmentNavigation($id, $items);
333 static function augmentUserInfo(&$content)
335 foreach (self::$helpers as $helper) {
336 $helper->augmentUserInfo($content);
341 function mtrack_nav($id, $nav) {
344 // Allow config file to manipulate the navigation bits
345 $cnav = MTrackConfig::getSection('nav:' . $id);
346 if (is_array($cnav)) {
347 foreach ($cnav as $loc => $label) {
348 if (!strlen($label)) {
356 MTrackNavigation::augmentNavigation($id, $nav);
360 $web = realpath(dirname(__FILE__) . '/../web');
361 $where = substr($_SERVER['SCRIPT_FILENAME'], strlen($web));
362 if (isset($_SERVER['PATH_INFO'])) {
363 $where .= $_SERVER['PATH_INFO'];
368 foreach ($nav as $loc => $label) {
370 if (!strncmp($cloc, $ABSWEB, strlen($ABSWEB))) {
371 $cloc = substr($cloc, strlen($ABSWEB)-1);
373 if ($where == $cloc || $where == rtrim($cloc, '/')) {
378 $where = dirname($where);
379 } while ($active === null && $tries++ < 100);
381 foreach ($nav as $loc => $label) {
384 if (!count($elements)) {
387 if (count($nav) == 0) {
390 if ($active == $loc) {
394 $class = " class=\"" . implode(' ', $class) . "\"";
398 if ($loc[0] == '/') {
399 $url = substr($loc, 1); // trim off leading /
403 if (!preg_match('/^[a-z-]+:/', $url)) {
404 $url = $ABSWEB . $url;
406 $elements[] = "<li$class><a href=\"$url\">$label</a></li>";
408 return "<div id='$id' class='nav'><ul>" .
409 implode('', $elements) . "</ul></div>";
412 function mtrack_date($tstring, $show_full = false)
414 /* database time is always relative to UTC */
415 $d = date_create($tstring, new DateTimeZone('UTC'));
416 if (!is_object($d)) {
417 throw new Exception("could not represent $tstring as a datetime object");
419 $iso8601 = $d->format(DateTime::W3C);
420 /* but we want to render relative to user prefs */
421 date_timezone_set($d, new DateTimeZone(date_default_timezone_get()));
422 $full = $d->format('D, M d Y H:i');
425 return "<abbr title=\"$iso8601\" class='timeinterval'>$full</abbr>";
428 return "<abbr title='$iso8601' class='timeinterval'>$full</abbr> <span class='fulldate'>$full</span>";
431 function mtrack_rmdir($dir)
433 foreach (scandir($dir) as $ent) {
434 if ($ent == '.' || $ent == '..') {
437 $full = $dir . DIRECTORY_SEPARATOR . $ent;
447 function mtrack_make_temp_dir($do_make = true)
449 $tempdir = sys_get_temp_dir();
450 $base = $tempdir . DIRECTORY_SEPARATOR . "mtrack." . uniqid();
451 for ($i = 0; $i < 1024; $i++) {
452 $candidate = $base . sprintf("%04x", $i);
454 if (mkdir($candidate)) {
459 if (!file_exists($candidate) && !is_dir($candidate)) {
464 throw new Exception("unable to make temp dir based on path $candidate");
467 function mtrack_diff_strings($before, $now)
469 $tempdir = sys_get_temp_dir();
470 $afile = tempnam($tempdir, "mtrack");
471 $bfile = tempnam($tempdir, "mtrack");
472 file_put_contents($afile, $before);
473 file_put_contents($bfile, $now);
474 $diff = MTrackConfig::get('tools', 'diff');
475 if (PHP_OS == 'SunOS') {
476 // TODO: make an option to allow use of gnu diff on solaris
477 $diff = shell_exec("$diff -u $afile $bfile");
478 $diff = str_replace($afile, 'before', $diff);
479 $diff = str_replace($bfile, 'now', $diff);
481 $diff = shell_exec("$diff --label before --label now -u $afile $bfile");
485 $diff = htmlentities($diff, ENT_COMPAT, 'utf-8');
489 function mtrack_last_chance_saloon($e)
491 if ($e instanceof MTrackAuthorizationException) {
492 if (MTrackAuth::whoami() == 'anonymous') {
493 MTrackAuth::forceAuthenticate();
495 mtrack_head('Insufficient Privilege');
496 echo '<h1>Insufficient Privilege</h1>';
497 $rights = is_array($e->rights) ? join(', ', $e->rights) : $e->rights;
498 echo "You do not have the required set of rights ($rights) to access this page<br>";
503 $msg = $e->getMessage();
506 mtrack_head('Whoops: ' . $msg);
507 } catch (Exception $doublefault) {
510 echo "<h1>An error occurred!</h1>";
512 echo htmlentities($msg, ENT_QUOTES, 'utf-8');
516 echo nl2br(htmlentities($e->getTraceAsString(), ENT_QUOTES, 'utf-8'));
520 } catch (Exception $doublefault) {
524 function mtrack_canon_username($username)
526 static $canon_map = null;
528 if ($canon_map === null) {
529 $canon_map = array();
530 foreach (MTrackDB::q('select alias, userid from useraliases union select email, userid from userinfo where email <> \'\'')->fetchAll()
532 $canon_map[$row[0]] = $row[1];
538 if (isset($canon_map[$username])) {
539 if ($username == $canon_map[$username]) {
542 $username = $canon_map[$username];
543 } elseif (preg_match('/<([a-z0-9_.+=-]+@[a-z0-9.-]+)>/', $username, $M)) {
544 // look at just the email address
546 if (!isset($canon_map[$username])) {
552 } while ($runaway-- > 0);
557 function mtrack_username($username, $options = array())
559 $username = mtrack_canon_username($username);
560 $userdata = MTrackAuth::getUserData($username);
562 if (isset($userdata['fullname']) && strlen($userdata['fullname'])) {
563 $title = " title='" .
564 htmlentities($userdata['fullname'], ENT_QUOTES, 'utf-8') . "' ";
571 if (!isset($options['size'])) {
572 $options['size'] = 24;
574 if (isset($options['class'])) {
575 $extraclass = " $options[class]";
580 if (!ctype_alnum($username)) {
581 $target = "{$ABSWEB}user.php?user=" . urlencode($username);
582 if (isset($options['edit'])) {
583 $target .= '&edit=1';
586 $target = "{$ABSWEB}user.php/$username";
587 if (isset($options['edit'])) {
588 $target .= '?edit=1';
591 $open_a = "<a $title href='$target' class='userlink$extraclass'>";
594 if ((!isset($options['no_image']) || !$options['no_image'])) {
596 mtrack_avatar($username, $options['size']) .
599 if (!isset($options['no_name']) || !$options['no_name']) {
600 $dispuser = $username;
602 if (strlen($dispuser) > 12) {
603 if (preg_match("/^([^+]*)(\+.*)?@(.*)$/", $dispuser, $M)) {
604 /* looks like an email address, try to shorten it in a reasonable way */
609 if (strlen($extra)) {
613 $dispuser = "$local@$domain";
616 $ret .= "$open_a$dispuser</a>";
621 function mtrack_avatar($username, $size = 24)
625 $id = urlencode($username);
627 return "<img class='gravatar' width='$size' height='$size' src='{$ABSWEB}avatar.php?u=$id&s=$size'>";
630 function mtrack_gravatar($email, $size = 24)
635 return "<img class='gravatar' width='$size' height='$size' src='http://www.gravatar.com/avatar/" . md5(strtolower($email)) . "?s=$size&d=wavatar'>";
638 function mtrack_defrepo()
640 static $defrepo = null;
641 if ($defrepo === null) {
642 $defrepo = MTrackConfig::get('core', 'default.repo');
643 if ($defrepo === null) {
645 foreach (MTrackDB::q(
646 'select parent, shortname from repos order by shortname')
647 ->fetchAll() as $row) {
648 $defrepo = MTrackSCM::makeDisplayName($row);
651 } else if (strpos($defrepo, '/') === false) {
652 $defrepo = 'default/' . $defrepo;
658 function mtrack_changeset_url($cs, $repo = null)
661 if ($repo instanceof MTrackRepo) {
662 $p = $repo->getBrowseRootName() . '/';
663 } elseif ($repo !== null) {
664 if (strpos($repo, '/') === false) {
665 $repo = "default/$repo";
669 static $repos = null;
670 if ($repos === null) {
672 foreach (MTrackDB::q('select r.shortname as repo, p.shortname as proj from repos r left join project_repo_link l using (repoid) left join projects p using (projid) where parent is null or length(parent) = 0')->fetchAll(PDO::FETCH_ASSOC) as $row) {
675 $repos[$row['proj']] = $r;
677 $repos[$row['repo']] = $r;
681 foreach ($repos as $a => $b) {
682 if (!strncasecmp($cs, $a, strlen($a))) {
683 $p = 'default/' . $b;
684 $cs = substr($cs, strlen($a));
689 $p = mtrack_defrepo();
693 return $ABSWEB . "changeset.php/$p$cs";
696 function mtrack_changeset($cs, $repo = null)
699 if (strlen($display) > 12) {
700 $display = substr($display, 0, 12);
702 $url = mtrack_changeset_url($cs, $repo);
703 return "<a class='changesetlink' href='$url'>[$display]</a>";
706 function mtrack_branch($branch, $repo = null)
708 return "<span class='branchname'>$branch</span>";
711 function mtrack_wiki($pagename, $extras = array())
714 if ($pagename instanceof MTrackWikiItem) {
716 } else if (is_string($pagename)) {
717 $wiki = null;//MTrackWikiItem::loadByPageName($pagename);
719 // FIXME: hinted data from reports
720 throw new Exception("FIXME: wiki");
723 $pagename = $wiki->pagename;
725 $html = "<a class='wikilink'";
726 if (isset($extras['#'])) {
727 $anchor = '#' . $extras['#'];
731 $html .= " href=\"{$ABSWEB}wiki.php/$pagename$anchor\">";
732 if (isset($extras['display'])) {
733 $html .= htmlentities($extras['display'], ENT_QUOTES, 'utf-8');
735 $html .= htmlentities($pagename, ENT_QUOTES, 'utf-8');
741 function mtrack_ticket($no, $extras = array())
745 if ($no instanceof MTrackIssue) {
747 } else if (is_string($no) || is_int($no)) {
748 static $cache = array();
751 $no = substr($no, 1);
754 if (!isset($cache[$no])) {
755 $tkt = MTrackIssue::loadByNSIdent($no);
757 $tkt = MTrackIssue::loadById($no);
764 // FIXME: hinted data from reports
766 $tkt->tid = $no['ticket'];
767 $tkt->summary = $no['summary'];
768 if (isset($no['state'])) {
769 $tkt->status = $no['state'];
770 } elseif (isset($no['status'])) {
771 $tkt->status = $no['status'];
772 } elseif (isset($no['__status__'])) {
773 $tkt->status = $no['__status__'];
781 $tkt->summary = 'No such ticket';
782 $tkt->status = 'No such ticket';
784 $html = "<a class='ticketlink";
785 if ($tkt->status == 'closed') {
786 $html .= ' completed';
788 if (!empty($tkt->nsident)) {
789 $ident = $tkt->nsident;
793 if (isset($extras['#'])) {
794 $anchor = '#' . $extras['#'];
798 $html .= "' href=\"{$ABSWEB}ticket.php/$ident$anchor\">";
799 if (isset($extras['display'])) {
800 $html .= htmlentities($extras['display'], ENT_QUOTES, 'utf-8');
802 $html .= '#' . htmlentities($ident, ENT_QUOTES, 'utf-8');
808 function mtrack_tag($tag, $repo = null)
810 return "<span class='tagname'>$tag</span>";
813 function mtrack_keyword($keyword)
816 $kw = urlencode($keyword);
817 return "<a class='keyword' href='{$ABSWEB}search.php?q=keyword%3A$kw'>$keyword</span>";
820 function mtrack_multi_select_box($name, $title, $items, $values = null)
822 $title = htmlentities($title, ENT_QUOTES, 'utf-8');
823 $html = "<select id='$name' name='{$name}[]' multiple='multiple' title='$title'>";
824 foreach ($items as $k => $v) {
825 $html .= "<option value='" .
826 htmlspecialchars($k, ENT_QUOTES, 'utf-8') .
828 if (isset($values[$k])) {
829 $html .= ' selected';
831 $html .= ">" . htmlentities($v, ENT_QUOTES, 'utf-8') . "</option>\n";
833 return $html . "</select>";
836 function mtrack_select_box($name, $items, $value = null, $keyed = true)
838 $html = "<select id='$name' name='$name'>";
839 foreach ($items as $k => $v) {
840 $html .= "<option value='" .
841 htmlspecialchars($k, ENT_QUOTES, 'utf-8') .
843 if (($keyed && $value == $k) || (!$keyed && $value == $v)) {
844 $html .= ' selected';
846 $html .= ">" . htmlentities($v, ENT_QUOTES, 'utf-8') . "</option>\n";
848 return $html . "</select>";
851 function mtrack_radio($name, $value, $curval)
853 $checked = $curval == $value ? " checked='checked'": '';
854 return "<input type='radio' id='$value' name='$name' value='$value'$checked>";
857 function mtrack_diff($diffstr)
861 if (is_resource($diffstr)) {
863 while (($line = fgets($diffstr)) !== false) {
864 $lines[] = rtrim($line, "\r\n");
869 if (is_string($diffstr)) {
870 $abase = md5($diffstr);
871 $diffstr = preg_split("/\r?\n/", $diffstr);
873 $abase = md5(join("\n", $diffstr));
876 /* we could use toggle() below, but it is much faster to determine
877 * if we are hiding or showing based on a single variable than evaluating
878 * that for each possible cell */
880 <button class='togglediffcopy' type='button'>Toggle Diff Line Numbers</button>
882 $html .= "<table class='code diff'>";
883 //$html = "<pre class='code diff'>";
886 if (!count($diffstr)) {
889 $line = array_shift($diffstr);
891 if (!strncmp($line, '@@ ', 3)) {
892 /* done with preamble */
895 $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
896 $line = "<tr class='meta'><td class='lineno'></td><td class='lineno'></td><td class='lineno'></td><td width='100%'>$line</tr>";
897 $html .= $line . "\n";
900 $lines = array(0, 0);
905 if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
907 $lines[0] = (int)$M[1] - 1;
908 $lines[1] = (int)$M[2] - 1;
911 } elseif (strlen($line)) {
912 if ($line[0] == '-') {
915 } elseif ($line[0] == '+') {
926 $row = "<tr class='$class";
930 if ($class != 'meta' && $first) {
938 $row .= "<td class='lineno'></td><td class='lineno'></td>";
941 $row .= "<td class='lineno'></td><td class='lineno'>" . $lines[1] . "</td>";
944 $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'></td>";
947 $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'>" . $lines[1] . "</td>";
949 $anchor = $abase . '.' . $nlines;
950 $row .= "<td class='linelink'><a name='$anchor'></a><a href='#$anchor' title='link to this line'>#</a></td>";
952 $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
953 $row .= "<td class='line' width='100%'>$line</td></tr>\n";
956 if (!count($diffstr)) {
959 $line = array_shift($diffstr);
971 function mtrack_mime_detect($filename, $namehint = null)
973 /* does config tell us how to decide mimetype */
974 $detector = MTrackConfig::get('core', 'mimetype_detect');
976 /* if detector is blank, we'll try to figure out which one to use */
977 if (empty($detector)) {
978 if (function_exists('finfo_open')) {
979 $detector = 'fileinfo';
980 } elseif (function_exists('mime_content_type')) {
981 $detector = 'mime_magic';
987 /* use detector or all mimetypes will be blank */
988 if ($detector === 'fileinfo') {
989 if (defined('FILEINFO_MIME_TYPE')) {
990 $fileinfo = finfo_open(FILEINFO_MIME_TYPE);
992 $magic = MTrackConfig::get('core', 'mime.magic');
993 if (strlen($magic)) {
994 $fileinfo = finfo_open(FILEINFO_MIME, $magic);
996 $fileinfo = finfo_open(FILEINFO_MIME);
999 $mimetype = finfo_file($fileinfo, $filename);
1000 finfo_close($fileinfo);
1001 } elseif ($detector === 'mime_magic') {
1002 $mimetype = mime_content_type($filename);
1003 } elseif (PHP_OS != 'SunOS') {
1004 $mimetype = shell_exec("file -b --mime " . escapeshellarg($filename));
1006 $mimetype = 'application/octet-stream';
1008 $mimetype = trim(preg_replace("/\s*;.*$/", '', $mimetype));
1009 if (empty($mimetype)) {
1010 $mimetype = 'application/octet-stream';
1012 if ($mimetype == 'application/octet-stream') {
1013 if ($namehint === null) {
1014 $namehint = $filename;
1016 $pi = pathinfo($namehint);
1017 switch (strtolower($pi['extension'])) {
1018 case 'bin': return 'application/octet-stream';
1019 case 'exe': return 'application/octet-stream';
1020 case 'dll': return 'application/octet-stream';
1021 case 'iso': return 'application/octet-stream';
1022 case 'so': return 'application/octet-stream';
1023 case 'a': return 'application/octet-stream';
1024 case 'lib': return 'application/octet-stream';
1025 case 'pdf': return 'application/pdf';
1026 case 'ps': return 'application/postscript';
1027 case 'ai': return 'application/postscript';
1028 case 'eps': return 'application/postscript';
1029 case 'ppt': return 'application/vnd.ms-powerpoint';
1030 case 'xls': return 'application/vnd.ms-excel';
1031 case 'tiff': return 'image/tiff';
1032 case 'tif': return 'image/tiff';
1033 case 'wbmp': return 'image/vnd.wap.wbmp';
1034 case 'png': return 'image/png';
1035 case 'gif': return 'image/gif';
1036 case 'jpg': return 'image/jpeg';
1037 case 'jpeg': return 'image/jpeg';
1038 case 'ico': return 'image/x-icon';
1039 case 'bmp': return 'image/bmp';
1040 case 'css': return 'text/css';
1041 case 'htm': return 'text/html';
1042 case 'html': return 'text/html';
1043 case 'txt': return 'text/plain';
1044 case 'xml': return 'text/xml';
1045 case 'eml': return 'message/rfc822';
1046 case 'asc': return 'text/plain';
1047 case 'rtf': return 'application/rtf';
1048 case 'wml': return 'text/vnd.wap.wml';
1049 case 'wmls': return 'text/vnd.wap.wmlscript';
1050 case 'gtar': return 'application/x-gtar';
1051 case 'gz': return 'application/x-gzip';
1052 case 'tgz': return 'application/x-gzip';
1053 case 'tar': return 'application/x-tar';
1054 case 'zip': return 'application/zip';
1055 case 'sql': return 'text/plain';
1057 // if the file is ascii, then treat it as text/plain
1058 $fp = fopen($filename, 'rb');
1059 $mimetype = 'text/plain';
1061 $x = fread($fp, 8192);
1062 if (!strlen($x)) break;
1063 if (preg_match('/([\x80-\xff])/', $x, $M)) {
1064 $mimetype = 'application/octet-stream';
1073 function mtrack_run_tool($toolname, $mode, $args = null)
1077 $tool = MTrackConfig::get('tools', $toolname);
1078 if (!strlen($tool)) {
1081 if (PHP_OS == 'Windows' && strpos($tool, ' ') !== false) {
1082 $tool = '"' . $tool . '"';
1085 if (is_array($args)) {
1086 foreach ($args as $arg) {
1087 if (is_array($arg)) {
1088 foreach ($arg as $a) {
1089 $cmd .= ' ' . escapeshellarg($a);
1092 $cmd .= ' ' . escapeshellarg($arg);
1096 if (!isset($FORKS[$cmd])) {
1101 if (php_sapi_name() == 'cli') {
1105 echo htmlentities($cmd) . "<br>\n";
1110 case 'read': return popen($cmd, 'r');
1111 case 'write': return popen($cmd, 'w');
1112 case 'string': return stream_get_contents(popen($cmd, 'r'));
1115 0 => array('pipe', 'r'),
1116 1 => array('pipe', 'w'),
1117 2 => array('pipe', 'w'),
1119 $proc = proc_open($cmd, $pipedef, $pipes);
1120 return array($proc, $pipes);
1124 if (php_sapi_name() != 'cli') {
1125 set_exception_handler('mtrack_last_chance_saloon');
1126 error_reporting(E_NOTICE|E_ERROR|E_WARNING);
1127 ini_set('display_errors', false);
1128 set_time_limit(300);