* @license GPL-3+ * */ class Downloads { /** */ function __construct() { } /** * @param string $ua * * @return array * * @todo unit tests or use something else * Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; fr) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/4.1.2 Safari/533.18.5 * Mozilla/5.0 (X11; Linux i686; rv:2.0) Gecko/20110330 Firefox/4.0 * other old ones * * @todo refactor this */ public static function get_platform($ua = null) { if ($ua == '') return array( 'arch' => 'i586', 'system' => 'unknown', 'locale' => 'en', 'browser' => null ); $locale = null; if (preg_match_all('/([.^\(^\)]*) \((.*)\) (.*)/', $ua, $r)) { $r = $r[2][0]; $r = explode(';', $r); if (isset($r[3])) { $r = explode(')', trim($r[3])); if (strlen($r[0]) > 5) $r = substr($r[0], 0, 5); else $r = $r[0]; } else $r = null; $locale = $r; } $arch = 'i586'; if (strpos($ua, 'x86_64') !== false) $arch = 'x86_64'; $sys = null; if (strpos($ua, 'Windows') !== false) $sys = 'win'; elseif (strpos($ua, 'Macintosh') !== false || strpos($ua, 'Mac OS X') !== false) $sys = 'mac'; elseif (strpos($ua, 'Linux') !== false) $sys = 'linux'; $browser = null; if (strpos($ua, 'Firefox') !== false) $browser = 'firefox'; elseif (strpos($ua, 'MSIE') !== false) $browser = 'msie'; elseif (strpos($ua, 'Safari') !== false) $browser = 'safari'; elseif (strpos($ua, 'Opera') !== false) $browser = 'opera'; return array( 'arch' => $arch, 'system' => $sys, 'locale' => $locale, // FIXME (rda) use Accept-Language instead 'browser' => $browser ); } /** * Sort 2D array by multiple associative or numeric keys. * $sorted_array = self::sort_2d_array_by_multiple_keys($unsorted_array, 'first key', 'second', ...); * * based on SortArray http://php.net/manual/en/function.usort.php#42535 * * @param array $unsorted_array * * @param string first key to order by * * @param string second key to order by * * @param string add as many keys to order by as needed * * @return array $sorted_array */ public static function sort_2d_array_by_multiple_keys() { $arguments = func_get_args(); $array = $arguments[0]; $anonymous_function = ''; $num_of_arguments = count($arguments); for ($cur_argument = 1; $cur_argument < $num_of_arguments; $cur_argument++) { $anonymous_function .= "if (\$first['$arguments[$cur_argument]'] != \$second['$arguments[$cur_argument]']) {"; $anonymous_function .= " \$compare_result = strcoll(\$first['$arguments[$cur_argument]'], \$second['$arguments[$cur_argument]']);"; $anonymous_function .= " if (0 == \$compare_result) { return 0; };"; $anonymous_function .= " return ((0 > \$compare_result) ? -1 : 1);"; $anonymous_function .= "}"; } $anonymous_function .= "return 0;"; $compare_function = create_function("\$first, \$second", $anonymous_function); usort($array, $compare_function); return $array; } /** * Get mirrors list from mirrors.mageia.org, * store/cache it in a different key/value format * (keys are: "$country" and "_C:$continent"), * and return it. * * Note that the mirrors list doesn't change with versions, for now; * it's a full or nothing list. * * @param boolean $prod * @param boolean $documentation * @param boolean $mirrorlist * * @return array */ public static function get_all_mirrors($prod = true, $documentation = false, $mirrorlist = false) { if ($documentation) { $cache_file = realpath(__DIR__ . '/cached.list_doc.php'); } else if ($mirrorlist) { $cache_file = realpath(__DIR__ . '/cached.list_mirrorlist.php'); } else { $cache_file = realpath(__DIR__ . '/cached.list.php'); } if ($prod) { require $cache_file; } else { $data = file('http://mirrors.mageia.org/api/mageia.7.i586.list'); $mirrors = array(); $num_up = 0; $num_dn = 0; foreach ($data as $line) { $line = explode(',', trim($line)); $m = array(); foreach ($line as $val) { $val = explode('=', trim($val)); if (!empty($val[1])) { $m[$val[0]] = $val[1]; } } $pu = parse_url($m['url']); if (in_array($pu['scheme'], array('http', 'https', 'ftp'))) { $item = array( 'zone' => isset($m['zone']) ? $m['zone'] : '?', 'country' => isset($m['country']) ? $m['country'] : '?', 'city' => isset($m['city']) ? utf8_encode($m['city']) : '-', // BEWARE of the path substitution here. Must match. 'url' => str_replace('/distrib/7/i586', '', $m['url']) ); if ($documentation) { $test_file = $item['url'].'/doc/mga7/date.txt'; } else if ($mirrorlist) { $test_file = $item['url'].'/distrib/7/x86_64/media/core/updates/repodata/repomd.xml'; } else { $test_file = $item['url'].'/iso/7.1/torrents/Mageia-7.1-x86_64.torrent'; } if (false === @file_get_contents($test_file)) { $num_dn++; echo "Down ($num_dn) $test_file \n"; } else { $num_up++; echo "Up ($num_up) $test_file \n"; // $mirrors[$m['country']][] = $item; $mirrors['_C:' . $m['continent']][] = $item; } } } ksort($mirrors); foreach ($mirrors as &$continent) { $continent = self::sort_2d_array_by_multiple_keys($continent, 'zone', 'country', 'city', 'url'); } unset($continent); echo "\nThere are $num_up servers with the file and $num_dn with some kind of issue.\n"; file_put_contents($cache_file, sprintf('' . PHP_EOL, var_export($mirrors, true))); } return $mirrors; } /** * Get mirrors from stored dictionary and find best matching mirror: * - if it exists in the country otherwise * - on continent if it exists otherwise * - random mirror * It can also prepare a list of mirrors for mirrorlist page * * @param string $country * @param string $continent * @param boolean $prod * @param boolean $documentation * @param boolean $mirrorlist * * @return array */ function get_mirror($country, $continent = null, $prod = true, $documentation = false, $mirrorlist = false) { $mirs = self::get_all_mirrors($prod, $documentation, $mirrorlist); $continent = '_C:' . $continent; $mirrors = array(); $fr_mirr_asist = array(); $list_of_mirrs = array( 'country' => array(), 'continent_minus_country' => array(), 'other_continents' => array() ); foreach ($mirs as $curr_continent => $continent_mirrors) { if ($continent != $curr_continent) { if ($mirrorlist) { foreach ($continent_mirrors as $mirror) { $list_of_mirrs['other_continents'][] = $mirror['url']; } } continue; } foreach ($continent_mirrors as $mirror) { if (strpos($mirror['url'], 'distrib-coffee.ipsl.jussieu.fr') !== false) { // exclude source server to drop it's DL load continue; } // keep assisting the french mirrors with german ones if ($mirror['country'] == 'DE') { $fr_mirr_asist[] = $mirror; } // only add german mirrors when french are on turn // sorting of mirror db cache must be kept to work properly (DE before FR) if ($country == 'FR' && $mirror['country'] == 'FR' && count($fr_mirr_asist) > 0) { $mirrors[$continent] = $fr_mirr_asist; $fr_mirr_asist = array(); } if ($mirrorlist) { if ($mirror['country'] == $country) { $list_of_mirrs['country'][] = $mirror['url']; } else { $list_of_mirrs['continent_minus_country'][] = $mirror['url']; } } else { if ($mirror['country'] == $country) { $mirrors[$continent][] = $mirror; } } } if (count($mirrors) == 0) { // add all continent mirrors if country doesn't have any $mirrors[$continent] = $continent_mirrors; } } if (count($mirrors) > 0) { $mirs = $mirrors; } shuffle($mirs); $mirr_continent = $mirs[0]; $mirs = array_shift($mirs); shuffle($mirs); $one_mirror = array_shift($mirs); $one_mirror['continent'] = $mirr_continent; if ($mirrorlist) { shuffle($list_of_mirrs['country']); shuffle($list_of_mirrs['continent_minus_country']); shuffle($list_of_mirrs['other_continents']); $resulting_mirrs = array_merge( $list_of_mirrs['country'], $list_of_mirrs['continent_minus_country'], $list_of_mirrs['other_continents'] ); $one_mirror['mirrors_list'] = array_slice($resulting_mirrs, 0, 10); } return $one_mirror; } function prepare_download($force = false, $country = null, $prod = true, $documentation = false, $mirrorlist = false) { return $this->get_one_mirror($force, $country, $prod, $documentation, $mirrorlist); } /** * Setup session data about current visitor for downloads. * * @param boolean $force * @param string $country * @param boolean $prod * @param boolean $documentation * @param boolean $mirrorlist * * @return array * * TODO extract as much as possible $_SESSION(read) and $_SERVER and $_GET */ function get_one_mirror($force = false, $country = null, $prod = true, $documentation = false, $mirrorlist = false) { $fuzzy_mirror = false; if (!is_null($country)) $force = true; // FIXME break this into smaller parts and extract globals so we can test st if (!$force && isset($_SESSION['dl-data'])) { //error_log(sprintf('Got session data: %s', print_r($_SESSION['dl-data'], true))); $system = $_SESSION['dl-data']['system']; if (isset($_GET['mirror'])) { $mirror = array('url' => $_GET['mirror']); $mirror['purl'] = parse_url($mirror['url']); $_SESSION['dl-data']['mirror'] = $mirror; $country = ''; } else { $country = $_SESSION['dl-data']['country']; $mirror = $_SESSION['dl-data']['mirror']; } } else { //error_log('getting platform'); $system = self::get_platform($_SERVER['HTTP_USER_AGENT']); if (isset($_GET['mirror'])) { $mirror = array('url' => $_GET['mirror']); $mirror['purl'] = parse_url($mirror['url']); $country = null; } else { //error_log('no mirror set yet'); if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $str = $_SERVER['HTTP_X_FORWARDED_FOR']) { $arr = explode(', ', $str); $ip = $arr[0]; } else $ip = $_SERVER['REMOTE_ADDR']; $_SESSION['ip'] = $ip; if (is_null($country)) { require_once realpath(__DIR__ . '/mga_geoip.php'); $country = MGA_Geoip::mga_geoip_country_by_ip($ip, false); $continent = MGA_Geoip::mga_geoip_continent_by_country($country); $fuzzy_mirror = true; $_SESSION['country'] = $country; $_SESSION['continent'] = $continent; } if (!isset($continent)) { $continent = null; } $mirror = $this->get_mirror($country, $continent, $prod, $documentation, $mirrorlist); $mirror['purl'] = parse_url($mirror['url']); // reassign country, as get_one_mirror() may have decided // to return a mirror from another country than the one // requested initially - @see get_one_mirror() $country = $mirror['zone']; $continent = $mirror['continent']; if (is_null($mirror)) { // @todo? } } // write to session $_SESSION['dl-data'] = array( 'system' => $system, 'country' => $country, 'continent' => $continent, 'mirror' => $mirror ); } if (!isset($mirror['mirrors_list'])) { $mirror['mirrors_list'] = array(); } return array( 'arch' => $system['arch'], 'mirror_host' => $mirror['purl']['host'], 'mirror_scheme' => $mirror['purl']['scheme'], 'mirror_url' => $mirror['url'], 'country' => $country, 'continent' => $continent, 'city' => $mirror['city'], 'fuzzy_mirror' => $fuzzy_mirror, 'mirrors_list' => $mirror['mirrors_list'], ); } }