website: move page/code folder up a level

This commit is contained in:
Jon Atkins
2013-10-30 03:44:35 +00:00
parent c9f8180778
commit 932cdcd842
14 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,76 @@
<?php
include_once dirname(__FILE__) . "/lib/ApkArchive.php";
include_once dirname(__FILE__) . "/lib/ApkXmlParser.php";
include_once dirname(__FILE__) . "/lib/ApkManifest.php";
/**
* @author Tufan Baris YILDIRIM
* @version v0.1
* @since 27.03.2012
* @link https://github.com/tufanbarisyildirim/php-apk-parser
*
* Main Class.
* - Set the apk path on construction,
* - Get the Manifest object.
* - Print the Manifest XML.
*
* @todo Add getPackageName();
* @todo Add getVersion();
* @todo Add getUsesSdk();
* @todo Add getMinSdk();
*/
class ApkParser
{
/**
* @var ApkArchive
*/
private $apk;
/**
* AndrodiManifest.xml
*
* @var ApkManifest
*/
private $manifest;
public function __construct($apkFile)
{
$this->apk = new ApkArchive($apkFile);
$this->manifest = new ApkManifest(new ApkXmlParser($this->apk->getManifestStream()));
}
/**
* Get Manifest Object
* @return ApkManifest
*/
public function getManifest()
{
return $this->manifest;
}
/**
* Get the apk. Zip handler.
* - Extract all(or sp. entries) files,
* - add file,
* - recompress
* - and other ZipArchive features.
*
* @return ApkArchive
*/
public function getApkArchive()
{
return $this->apk;
}
/**
* Extract apk content directly
*
* @param mixed $destination
* @param array $entries
* @return bool
*/
public function extractTo($destination,$entries = NULL)
{
return $this->apk->extractTo($destination,$entries);
}
}

View File

@ -0,0 +1,47 @@
<?php
/**
* @todo : Write comments!
*/
class ApkAndroidPlatform
{
public $level = NULL;
public $platform = NULL;
private static $platforms = array(
2 => array('name' => 'Android 1.1 Platfrom'),
3 => array('name' => 'Android 1.5 Platfrom'),
4 => array('name' => 'Android 1.6 Platfrom'),
5 => array('name' => 'Android 2.0 Platfrom'),
6 => array('name' => 'Android 2.0.1 Platfrom'),
7 => array('name' => 'Android 2.1 Platfrom'),
8 => array('name' => 'Android 2.2 Platfrom'),
9 => array('name' => 'Android 2.3 Platfrom'),
10 => array('name' => 'Android 2.3.3 Platfrom'),
10 => array('name' => 'Android 2.3.3 / 2.3.4 Platfroms'),
11 => array('name' => 'Android 3.0 Platfroms'),
12 => array('name' => 'Android 3.1 Platfroms'),
13 => array('name' => 'Android 3.2 Platfroms'),
14 => array('name' => 'Android 4.0 Platfroms'),
14 => array('name' => 'Android 4.0.3 Platfroms'),
);
public function __construct($apiLevel)
{
$this->level = $apiLevel;
$this->platform = $this->getPlatform();
}
/**
* @return array
*/
public function getPlatform()
{
if(!isset(self::$platforms[$this->level]))
throw new Exception("Unknown api level.");
$platform = self::$platforms[$this->level];
$platform['level'] = $this->level;
return $platform;
}
}

View File

@ -0,0 +1,117 @@
<?php
include_once dirname(__FILE__) . "/ApkStream.php";
/**
* Customized ZipArchive for .apk files.
* @author Tufan Baris YILDIRIM
* @TODO Add ->getResource('file_name'), or getIcon() directly.
* @todo Override the // extractTo() method. Rewrite all of XML files converted from Binary Xml to text based XML!
*/
class ApkArchive extends ZipArchive
{
/**
* @var string
*/
private $filePath;
/**
* @var string
*/
private $fileName;
public function __construct($file = false)
{
if($file && is_file($file))
{
$this->open($file);
$this->fileName = basename($this->filePath = $file);
}
else
throw new Exception($file . " not a regular file");
}
/**
* Get a file from apk Archive by name.
*
* @param string $name
* @param int $length
* @param int $flags
* @return mixed
*/
public function getFromName($name,$length = NULL,$flags = NULL)
{
if(strtolower(substr($name,-4)) == '.xml')
{
$xmlParser = new ApkXmlParser(new ApkStream($this->getStream($name)));
return $xmlParser->getXmlString();
}
else
return parent::getFromName($name,$length,$flags);
}
/**
* Returns an ApkStream whick contains AndroidManifest.xml
* @return ApkStream
*/
public function getManifestStream()
{
return new ApkStream($this->getStream('AndroidManifest.xml'));
}
/**
* Apk file path.
* @return string
*/
public function getApkPath()
{
return $this->filePath;
}
/**
* Apk file name
* @return string
*/
public function getApkName()
{
return $this->fileName;
}
public function extractTo($destination,$entries = NULL)
{
if($extResult = parent::extractTo($destination,$entries))
{
//TODO: ApkXmlParser can not parse the main.xml and others! only AndroidManifest.xml
//return $extResult;
$xmlFiles = $this->glob_recursive($destination . '/*.xml');
foreach($xmlFiles as $xmlFile)
{
// TODO : Remove this ifcheck , if ApkXml can parse! amk!
if($xmlFile == "AndroidManifest.xml")
ApkXmlParser::decompressFile($xmlFile);
}
}
return $extResult;
}
// Can Move to the Utils(???) class.
private function glob_recursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir)
{
$files = array_merge($files, $this->glob_recursive($dir.'/'.basename($pattern), $flags));
}
return $files;
}
}

View File

@ -0,0 +1,269 @@
<?php
include_once dirname(__FILE__). '/ApkXml.php';
include_once dirname(__FILE__). '/ApkManifestXmlElement.php';
include_once dirname(__FILE__). '/ApkAndroidPlatform.php';
/**
* ApkManifest
* -- description is coming.
*
* @todo Add getPackageName();
* @todo Add getVersion();
* @todo Add getUsesSdk();
* @todo Add getMinSdk();
*/
class ApkManifest extends ApkXml
{
/**
* @var ApkXmlParser
*/
private $xmlParser;
private $attrs = null;
public function __construct(ApkXmlParser $xmlParser)
{
$this->xmlParser = $xmlParser;
}
/**
* Returns ManifestXml as a String.
* @return string
*/
public function getXmlString()
{
return $this->xmlParser->getXmlString();
}
/**
* Get Application Permissions
* @return array
*/
public function getPermissions()
{
return $this->getXmlObject()->getPermissions();
}
/**
* Android Package Name
* @return string
*/
public function getPackageName()
{
return $this->getAttribute('package');
}
/**
* Application Version Name
* @return string
*/
public function getVersionName()
{
return $this->getAttribute('versionName');
}
/**
* Application Version Code
* @return mixed
*/
public function getVersionCode()
{
return hexdec( $this->getAttribute('versionCode') );
}
/**
* @return bool
*/
public function isDebuggable()
{
return (bool)$this->getAttribute('debuggable');
}
/**
* The minimum API Level required for the application to run.
* @return int
*/
public function getMinSdkLevel()
{
$xmlObj = $this->getXmlObject();
$usesSdk = get_object_vars($xmlObj->{'uses-sdk'});
return hexdec($usesSdk['@attributes']['minSdkVersion']);
}
private function getAttribute($attributeName)
{
if($this->attrs === NULL)
{
$xmlObj = $this->getXmlObject();
$vars = get_object_vars($xmlObj->attributes());
$this->attrs = $vars['@attributes'];
}
if(!isset($this->attrs[$attributeName]))
throw new Exception("Attribute not found : " . $attributeName);
return $this->attrs[$attributeName];
}
/**
* More Information About The minimum API Level required for the application to run.
* @return ApkAndroidPlatform
*/
public function getMinSdk()
{
return new ApkAndroidPlatform($this->getMinSdkLevel());
}
/**
* get SimleXmlElement created from AndroidManifest.xml
*
* @param mixed $className
* @return ApkManifestXmlElement
*/
public function getXmlObject($className = 'ApkManifestXmlElement')
{
return $this->xmlParser->getXmlObject($className);
}
/**
* Basicly string casting method.
*/
public function __toString()
{
return $this->getXmlString();
}
/**
* Android Permissions list
* @see http://developer.android.com/reference/android/Manifest.permission.html
*
* @todo: Move to {lang}_perms.php file, for easly translations.
* @var mixed
*/
public static $permissions = array(
'ACCESS_CHECKIN_PROPERTIES' => 'Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.',
'ACCESS_COARSE_LOCATION' => 'Allows an application to access coarse (e.g., Cell-ID, WiFi) location',
'ACCESS_FINE_LOCATION' => 'Allows an application to access fine (e.g., GPS) location',
'ACCESS_LOCATION_EXTRA_COMMANDS' => 'Allows an application to access extra location provider commands',
'ACCESS_MOCK_LOCATION' => 'Allows an application to create mock location providers for testing',
'ACCESS_NETWORK_STATE' => 'Allows applications to access information about networks',
'ACCESS_SURFACE_FLINGER' => 'Allows an application to use SurfaceFlinger\'s low level features',
'ACCESS_WIFI_STATE' => 'Allows applications to access information about Wi-Fi networks',
'ACCOUNT_MANAGER' => 'Allows applications to call into AccountAuthenticators.',
'ADD_VOICEMAIL' => 'Allows an application to add voicemails into the system.',
'AUTHENTICATE_ACCOUNTS' => 'Allows an application to act as an AccountAuthenticator for the AccountManager',
'BATTERY_STATS' => 'Allows an application to collect battery statistics',
'BIND_APPWIDGET' => 'Allows an application to tell the AppWidget service which application can access AppWidget\'s data.',
'BIND_DEVICE_ADMIN' => 'Must be required by device administration receiver, to ensure that only the system can interact with it.',
'BIND_INPUT_METHOD' => 'Must be required by an InputMethodService, to ensure that only the system can bind to it.',
'BIND_REMOTEVIEWS' => 'Must be required by a RemoteViewsService, to ensure that only the system can bind to it.',
'BIND_TEXT_SERVICE' => 'Must be required by a TextService (e.g.',
'BIND_VPN_SERVICE' => 'Must be required by an VpnService, to ensure that only the system can bind to it.',
'BIND_WALLPAPER' => 'Must be required by a WallpaperService, to ensure that only the system can bind to it.',
'BLUETOOTH' => 'Allows applications to connect to paired bluetooth devices',
'BLUETOOTH_ADMIN' => 'Allows applications to discover and pair bluetooth devices',
'BRICK' => 'Required to be able to disable the device (very dangerous!).',
'BROADCAST_PACKAGE_REMOVED' => 'Allows an application to broadcast a notification that an application package has been removed.',
'BROADCAST_SMS' => 'Allows an application to broadcast an SMS receipt notification',
'BROADCAST_STICKY' => 'Allows an application to broadcast sticky intents.',
'BROADCAST_WAP_PUSH' => 'Allows an application to broadcast a WAP PUSH receipt notification',
'CALL_PHONE' => 'Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.',
'CALL_PRIVILEGED' => 'Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.',
'CAMERA' => 'Required to be able to access the camera device.',
'CHANGE_COMPONENT_ENABLED_STATE' => 'Allows an application to change whether an application component (other than its own) is enabled or not.',
'CHANGE_CONFIGURATION' => 'Allows an application to modify the current configuration, such as locale.',
'CHANGE_NETWORK_STATE' => 'Allows applications to change network connectivity state',
'CHANGE_WIFI_MULTICAST_STATE' => 'Allows applications to enter Wi-Fi Multicast mode',
'CHANGE_WIFI_STATE' => 'Allows applications to change Wi-Fi connectivity state',
'CLEAR_APP_CACHE' => 'Allows an application to clear the caches of all installed applications on the device.',
'CLEAR_APP_USER_DATA' => 'Allows an application to clear user data',
'CONTROL_LOCATION_UPDATES' => 'Allows enabling/disabling location update notifications from the radio.',
'DELETE_CACHE_FILES' => 'Allows an application to delete cache files.',
'DELETE_PACKAGES' => 'Allows an application to delete packages.',
'DEVICE_POWER' => 'Allows low-level access to power management',
'DIAGNOSTIC' => 'Allows applications to RW to diagnostic resources.',
'DISABLE_KEYGUARD' => 'Allows applications to disable the keyguard',
'DUMP' => 'Allows an application to retrieve state dump information from system services.',
'EXPAND_STATUS_BAR' => 'Allows an application to expand or collapse the status bar.',
'FACTORY_TEST' => 'Run as a manufacturer test application, running as the root user.',
'FLASHLIGHT' => 'Allows access to the flashlight',
'FORCE_BACK' => 'Allows an application to force a BACK operation on whatever is the top activity.',
'GET_ACCOUNTS' => 'Allows access to the list of accounts in the Accounts Service',
'GET_PACKAGE_SIZE' => 'Allows an application to find out the space used by any package.',
'GET_TASKS' => 'Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.',
'GLOBAL_SEARCH' => 'This permission can be used on content providers to allow the global search system to access their data.',
'HARDWARE_TEST' => 'Allows access to hardware peripherals.',
'INJECT_EVENTS' => 'Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.',
'INSTALL_LOCATION_PROVIDER' => 'Allows an application to install a location provider into the Location Manager',
'INSTALL_PACKAGES' => 'Allows an application to install packages.',
'INTERNAL_SYSTEM_WINDOW' => 'Allows an application to open windows that are for use by parts of the system user interface.',
'INTERNET' => 'Allows applications to open network sockets.',
'KILL_BACKGROUND_PROCESSES' => 'Allows an application to call killBackgroundProcesses(String).',
'MANAGE_ACCOUNTS' => 'Allows an application to manage the list of accounts in the AccountManager',
'MANAGE_APP_TOKENS' => 'Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.',
'MASTER_CLEAR' => '',
'MODIFY_AUDIO_SETTINGS' => 'Allows an application to modify global audio settings',
'MODIFY_PHONE_STATE' => 'Allows modification of the telephony state - power on, mmi, etc.',
'MOUNT_FORMAT_FILESYSTEMS' => 'Allows formatting file systems for removable storage.',
'MOUNT_UNMOUNT_FILESYSTEMS' => 'Allows mounting and unmounting file systems for removable storage.',
'NFC' => 'Allows applications to perform I/O operations over NFC',
'PERSISTENT_ACTIVITY' => 'This constant is deprecated. This functionality will be removed in the future; please do not use. Allow an application to make its activities persistent.',
'PROCESS_OUTGOING_CALLS' => 'Allows an application to monitor, modify, or abort outgoing calls.',
'READ_CALENDAR' => 'Allows an application to read the user\'s calendar data.',
'READ_CONTACTS' => 'Allows an application to read the user\'s contacts data.',
'READ_FRAME_BUFFER' => 'Allows an application to take screen shots and more generally get access to the frame buffer data',
'READ_HISTORY_BOOKMARKS' => 'Allows an application to read (but not write) the user\'s browsing history and bookmarks.',
'READ_INPUT_STATE' => 'Allows an application to retrieve the current state of keys and switches.',
'READ_LOGS' => 'Allows an application to read the low-level system log files.',
'READ_PHONE_STATE' => 'Allows read only access to phone state.',
'READ_PROFILE' => 'Allows an application to read the user\'s personal profile data.',
'READ_SMS' => 'Allows an application to read SMS messages.',
'READ_SOCIAL_STREAM' => 'Allows an application to read from the user\'s social stream.',
'READ_SYNC_SETTINGS' => 'Allows applications to read the sync settings',
'READ_SYNC_STATS' => 'Allows applications to read the sync stats',
'REBOOT' => 'Required to be able to reboot the device.',
'RECEIVE_BOOT_COMPLETED' => 'Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.',
'RECEIVE_MMS' => 'Allows an application to monitor incoming MMS messages, to record or perform processing on them.',
'RECEIVE_SMS' => 'Allows an application to monitor incoming SMS messages, to record or perform processing on them.',
'RECEIVE_WAP_PUSH' => 'Allows an application to monitor incoming WAP push messages.',
'RECORD_AUDIO' => 'Allows an application to record audio',
'REORDER_TASKS' => 'Allows an application to change the Z-order of tasks',
'RESTART_PACKAGES' => 'This constant is deprecated. The restartPackage(String) API is no longer supported.',
'SEND_SMS' => 'Allows an application to send SMS messages.',
'SET_ACTIVITY_WATCHER' => 'Allows an application to watch and control how activities are started globally in the system.',
'SET_ALARM' => 'Allows an application to broadcast an Intent to set an alarm for the user.',
'SET_ALWAYS_FINISH' => 'Allows an application to control whether activities are immediately finished when put in the background.',
'SET_ANIMATION_SCALE' => 'Modify the global animation scaling factor.',
'SET_DEBUG_APP' => 'Configure an application for debugging.',
'SET_ORIENTATION' => 'Allows low-level access to setting the orientation (actually rotation) of the screen.',
'SET_POINTER_SPEED' => 'Allows low-level access to setting the pointer speed.',
'SET_PREFERRED_APPLICATIONS' => 'This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.',
'SET_PROCESS_LIMIT' => 'Allows an application to set the maximum number of (not needed) application processes that can be running.',
'SET_TIME' => 'Allows applications to set the system time',
'SET_TIME_ZONE' => 'Allows applications to set the system time zone',
'SET_WALLPAPER' => 'Allows applications to set the wallpaper',
'SET_WALLPAPER_HINTS' => 'Allows applications to set the wallpaper hints',
'SIGNAL_PERSISTENT_PROCESSES' => 'Allow an application to request that a signal be sent to all persistent processes',
'STATUS_BAR' => 'Allows an application to open, close, or disable the status bar and its icons.',
'SUBSCRIBED_FEEDS_READ' => 'Allows an application to allow access the subscribed feeds ContentProvider.',
'SUBSCRIBED_FEEDS_WRITE' => '',
'SYSTEM_ALERT_WINDOW' => 'Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.',
'UPDATE_DEVICE_STATS' => 'Allows an application to update device statistics.',
'USE_CREDENTIALS' => 'Allows an application to request authtokens from the AccountManager',
'USE_SIP' => 'Allows an application to use SIP service',
'VIBRATE' => 'Allows access to the vibrator',
'WAKE_LOCK' => 'Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming',
'WRITE_APN_SETTINGS' => 'Allows applications to write the apn settings',
'WRITE_CALENDAR' => 'Allows an application to write (but not read) the user\'s calendar data.',
'WRITE_CONTACTS' => 'Allows an application to write (but not read) the user\'s contacts data.',
'WRITE_EXTERNAL_STORAGE' => 'Allows an application to write to external storage',
'WRITE_GSERVICES' => 'Allows an application to modify the Google service map.',
'WRITE_HISTORY_BOOKMARKS' => 'Allows an application to write (but not read) the user\'s browsing history and bookmarks.',
'WRITE_PROFILE' => 'Allows an application to write (but not read) the user\'s personal profile data.',
'WRITE_SECURE_SETTINGS' => 'Allows an application to read or write the secure system settings.',
'WRITE_SETTINGS' => 'Allows an application to read or write the system settings.',
'WRITE_SMS' => 'Allows an application to write SMS messages.',
'WRITE_SOCIAL_STREAM' => 'Allows an application to write (but not read) the user\'s social stream data.',
'WRITE_SYNC_SETTINGS' => 'Allows applications to write the sync settings'
);
}

View File

@ -0,0 +1,22 @@
<?php
class ApkManifestXmlElement extends SimpleXMLElement
{
public function getPermissions()
{
/**
* @var ApkManifestXmlElement
*/
$permsArray = $this->{'uses-permission'};
$perms = array();
foreach($permsArray as $perm)
{
$permAttr = get_object_vars($perm);
$objNotationArray = explode('.',$permAttr['@attributes']['name']);
$permName = trim(end($objNotationArray));
$perms[$permName] = ApkManifest::$permissions[$permName];
}
return $perms;
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* @author Tufan Baris YILDIRIM
* -- descrtiption is coming.
*/
class ApkStream
{
/**
* file strem, like "fopen"
*
* @var resource
*/
private $stream;
/**
* @param resource $stream File stream.
* @return ApkStream
*/
public function __construct($stream)
{
if(!is_resource($stream))
// TODO : the resource type must be a regular file stream resource.
throw new Exception( "Invalid stream" );
$this->stream = $stream;
}
/**
* Read the next character from stream.
*
* @param mixed $length
*/
public function read($length = 1)
{
return fread($this->stream,$length);
}
/**
* check if end of filestream
*/
public function feof()
{
return feof($this->stream);
}
/**
* Jump to the index!
* @param int $offset
*/
public function seek($offset)
{
fseek($this->stream,$offset);
}
/**
* Close the stream
*/
public function close()
{
fclose($this->stream);
}
/**
* Read the next byte
* @return byte
*/
public function readByte()
{
return ord($this->read());
}
/**
* fetch the remaining byte into an array
*
* @param mixed $count Byte length.
* @return array
*/
public function getByteArray($count = null)
{
$bytes = array();
while(!$this->feof() && ($count === null || count($bytes) < $count))
$bytes[] = $this->readByte();
return $bytes;
}
}

View File

@ -0,0 +1,5 @@
<?php
abstract class ApkXml
{
}

View File

@ -0,0 +1,181 @@
<?php
include_once dirname(__FILE__) . "/ApkStream.php";
class ApkXmlParser
{
const END_DOC_TAG = 0x00100101;
const START_TAG = 0x00100102;
const END_TAG = 0x00100103;
private $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n";
private $bytes = array();
private $ready = false;
public static $indent_spaces = " ";
/**
* Store the SimpleXmlElement object
* @var SimpleXmlElement
*/
private $xmlObject = NULL;
public function __construct(ApkStream $apkStream)
{
$this->bytes = $apkStream->getByteArray();
}
public static function decompressFile($file,$destination = NULL)
{
if(!is_file($file))
throw new Exception("{$file} is not a regular file");
$parser = new self(new ApkStream(fopen($file,'rd')));
//TODO : write a method in this class, ->saveToFile();
file_put_contents($destination === NULL ? $file : $destination,$parser->getXmlString());
}
public function decompress()
{
$numbStrings = $this->littleEndianWord($this->bytes, 4*4);
$sitOff = 0x24;
$stOff = $sitOff + $numbStrings * 4;
$this->bytesTagOff = $this->littleEndianWord($this->bytes, 3*4);
for ($ii = $this->bytesTagOff; $ii < count($this->bytes) - 4; $ii += 4):
if ($this->littleEndianWord($this->bytes, $ii) == self::START_TAG) :
$this->bytesTagOff = $ii;
break;
endif;
endfor;
$off = $this->bytesTagOff;
$indentCount = 0;
$startTagLineNo = -2;
while ($off < count($this->bytes))
{
$currentTag = $this->littleEndianWord($this->bytes, $off);
$lineNo = $this->littleEndianWord($this->bytes, $off + 2*4);
$nameNsSi = $this->littleEndianWord($this->bytes, $off + 4*4);
$nameSi = $this->littleEndianWord($this->bytes, $off + 5*4);
switch($currentTag)
{
case self::START_TAG:
{
$tagSix = $this->littleEndianWord($this->bytes, $off + 6*4);
$numbAttrs = $this->littleEndianWord($this->bytes, $off + 7*4);
$off += 9*4;
$tagName = $this->compXmlString($this->bytes, $sitOff, $stOff, $nameSi);
$startTagLineNo = $lineNo;
$attr_string = "";
for ($ii=0; $ii < $numbAttrs; $ii++)
{
$attrNameNsSi = $this->littleEndianWord($this->bytes, $off);
$attrNameSi = $this->littleEndianWord($this->bytes, $off + 1*4);
$attrValueSi = $this->littleEndianWord($this->bytes, $off + 2*4);
$attrFlags = $this->littleEndianWord($this->bytes, $off + 3*4);
$attrResId = $this->littleEndianWord($this->bytes, $off + 4*4);
$off += 5*4;
$attrName = $this->compXmlString($this->bytes, $sitOff, $stOff, $attrNameSi);
if($attrValueSi != 0xffffffff)
$attrValue = $this->compXmlString($this->bytes, $sitOff, $stOff, $attrValueSi);
else
$attrValue = "0x" . dechex($attrResId);
$attr_string .= " " . $attrName . "=\"" . $attrValue . "\"";
}
$this->appendXmlIndent($indentCount, "<". $tagName . $attr_string . ">");
$indentCount++;
}
break;
case self::END_TAG:
{
$indentCount--;
$off += 6*4;
$tagName = $this->compXmlString($this->bytes, $sitOff, $stOff, $nameSi);
$this->appendXmlIndent($indentCount, "</" . $tagName . ">");
}
break;
case self::END_DOC_TAG:
{
$this->ready = true;
break 2;
}
break;
default:
throw new Exception("Unrecognized tag code '" . dechex($currentTag) . "' at offset " . $off);
break;
}
}
}
public function compXmlString($xml, $sitOff, $stOff, $str_index)
{
if ($str_index < 0)
return null;
$strOff = $stOff + $this->littleEndianWord($xml, $sitOff + $str_index * 4);
return $this->compXmlStringAt($xml, $strOff);
}
public function appendXmlIndent($indent, $str)
{
$this->appendXml(substr(self::$indent_spaces,0, min($indent * 2, strlen(self::$indent_spaces))) . $str);
}
public function appendXml($str)
{
$this->xml .= $str ."\r\n";
}
public function compXmlStringAt($arr, $string_offset)
{
$strlen = $arr[$string_offset + 1] << 8 & 0xff00 | $arr[$string_offset] & 0xff;
$string = "";
for ($i=0; $i<$strlen; $i++)
$string .= chr($arr[$string_offset + 2 + $i * 2]);
return $string;
}
public function littleEndianWord($arr, $off)
{
return $arr[$off+3] << 24&0xff000000 | $arr[$off+2] << 16&0xff0000 | $arr[$off+1]<<8&0xff00 | $arr[$off]&0xFF;
}
public function output()
{
echo $this->getXmlString();
}
public function getXmlString()
{
if(!$this->ready)
$this->decompress();
return $this->xml;
}
public function getXmlObject($className = 'SimpleXmlElement')
{
if($this->xmlObject === NULL || !$this->xmlObject instanceof $className)
$this->xmlObject = simplexml_load_string($this->getXmlString(),$className);
return $this->xmlObject;
}
}

View File

@ -0,0 +1,164 @@
<?php
include_once ( "userscript.php" );
function iitcDesktopDownload ( $build )
{
$iitc_details = loadUserScriptHeader ( "$build/total-conversion-build.user.js" );
$iitc_version = preg_replace ( '/^(\d+\.\d+\.\d+)\.(\d{8}\.\d{1,6})/', '\1<small class="muted">.\2</small>', $iitc_details['@version'] );
print "<p>IITC version $iitc_version</p>\n";
print "<a class=\"btn btn-large btn-primary\" onclick=\"if(track){track('desktop','iitc','$build');}\" href=\"$build/total-conversion-build.user.js\" target=\"_blank\">Download</a>\n";
}
function loadPopularity()
{
$popularity = Array();
$pop_file = getcwd() . "/popularity.txt";
if ( file_exists($pop_file) )
{
foreach ( file($pop_file,FILE_IGNORE_NEW_LINES) as $line )
{
$items = explode ( ' ', $line );
$popularity[$items[0]] = (int)$items[1];
}
}
return $popularity;
}
function popularity_cmp ( $a, $b )
{
if ( @$a['popularity'] == @$b['popularity'] )
return 0;
// sort from highest to lowest
return ($a['popularity'] > $b['popularity']) ? -1 : 1;
}
function iitcDesktopPluginDownloadTable ( $build )
{
$categories = Array (
'Portal Info' => "Enhanced information on the selected portal",
'Info' => "Display additional information",
'Keys' => "Manual key management",
'Controls' => "Map controls/widgets",
'Highlighter' => "Portal highlighters",
'Layer' => "Additional map layers",
'Map Tiles' => "Alternative map layers",
'Tweaks' => "Adjust IITC settings",
'Misc' => "Unclassified plugins",
'Obsolete' => "Plugins that are no longer recommended, due to being superceeded by others or similar",
'Deleted' => "Deleted plugins - listed here for reference only. No download available"
);
$popularity = loadPopularity();
$plugins = Array();
foreach ( glob ( "$build/plugins/*.user.js" ) as $path )
{
$basename = basename ( $path, ".user.js" );
$details = loadUserScriptHeader ( $path );
if ( array_key_exists('total-conversion-build',$popularity) && array_key_exists('plugins/'.$basename, $popularity) )
{
$details['popularity'] = $popularity['plugins/'.$basename] / $popularity['total-conversion-build'];
}
$plugins[$basename] = $details;
$category = array_key_exists('@category',$details) ? $details['@category'] : 'Misc';
if ( !array_key_exists($category,$categories) )
{
# add missing categories
$categories[$category] = '';
}
}
ksort ( $plugins );
uasort ( $plugins, 'popularity_cmp' );
?>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Popularity</th>
<th>ID</th>
<th>Version</th>
<!--
<th>Description</th>
-->
<th>Download</th>
</tr>
</thead>
<tbody>
<?php
foreach ( $categories as $category => $category_desc )
{
print "<tr class=\"category_header\"><th colspan=\"1\">Category: $category</th><td colspan=\"4\">$category_desc</td></tr>\n";
$empty = True;
foreach ( $plugins as $basename => $details )
{
$path = "$build/plugins/$basename.user.js";
$this_category = array_key_exists('@category',$details) ? $details['@category'] : 'Misc';
if ( $category != $this_category )
continue;
$empty = False;
print "<tr id=\"plugin-$basename\">\n";
# remove 'IITC Plugin: ' prefix if it's there, for neatness
$name = preg_replace ( '/^IITC plugin: /i', '', $details['@name'] );
# format extended version info in less prominant font
$version = preg_replace ( '/^(\d+\.\d+\.\d+)\.(\d{8}\.\d{1,6})/', '\1<br><small class="muted">.\2</small>', $details['@version'] );
# remove unneeded prefix from description
$description = preg_replace ( '/^\[[^]]*\] */', '', $details['@description'] );
$plugin_users = "-";
if ( array_key_exists('popularity',$details) )
{
$plugin_users = (int)($details['popularity']*1000)/10 . "%";
}
print "<td class=\"name\">$name</td>";
print "<td class=\"popularity\">$plugin_users</td>";
print "<td class=\"id\">$basename</td>";
print "<td class=\"version\" rowspan=\"2\">$version</td>";
if ( $category != "Deleted" )
{
print "<td class=\"download\" rowspan=\"2\"><a onclick=\"if(track){track('desktop','iitc-plugin-$basename','$build');}\" href=\"$path\" target=\"_blank\" class=\"btn btn-small btn-primary\" title=\"Download\"><i class=\"icon-download icon-white\"></i></a></td>";
}
print "</tr>\n";
print "<tr><td class=\"description\" colspan=\"3\" style=\"border-top: none; padding-top: 0; padding-bottom: 0.5em\">$description</td></tr>\n";
}
if ( $empty )
print "<tr><td class=\"empty\" colspan=\"4\">(no plugins in this category)</td></tr>\n";
}
?>
</tbody>
</table>
<?php
}
?>

View File

@ -0,0 +1,74 @@
<?php
include_once ( "apk/ApkParser.php" );
include_once ( "url/url_to_absolute.php" );
include_once ( "userscript.php" );
function getMobileVersion ( $apkfile )
{
$result = Array();
$apkinfo = new ApkParser ( $apkfile );
$manifest = $apkinfo->getManifest();
$result['apk_version'] = $manifest->getVersionName();
$archive = $apkinfo->getApkArchive();
$stream = $archive->getStream ( "assets/total-conversion-build.user.js" );
if ( ! $stream )
$stream = $archive->getStream ( "assets/iitc.js" );
if ( $stream )
{
$header = loadUserScriptHeader ( $stream );
$result['iitc_version'] = $header['@version'];
}
else
{
$result['iitc_version'] = 'unknown';
}
return $result;
}
function iitcMobileDownload ( $apkfile )
{
$version = getMobileVersion ( $apkfile );
$apk_version = $version['apk_version'];
$iitc_version = preg_replace ( '/^(\d+\.\d+\.\d+)\.(\d{8}\.\d{1,6})/', '\1<small class="muted">.\2</small>', $version['iitc_version'] );
# we need an absolute link for the QR Code
# get the URL of this page itself
$pageurl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? "https" : "http")."://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$apkurl = url_to_absolute ( $pageurl, $apkfile );
?>
<div>
<img style="float: right; margin: 10px;" src="https://chart.googleapis.com/chart?cht=qr&chs=120x120&chld=L|2&chl=<?php print urlencode($apkurl); ?>" alt="QR Code for download">
<p>
IITC Mobile version <?php print $apk_version; ?>, with IITC version <?php print $iitc_version; ?>
</p>
<p>
<a style="margin-right: 1em;" onclick="if(track){track('mobile','download','<?php print $apkfile; ?>');}" class="btn btn-large btn-primary" href="<?php print $apkfile; ?>">Download</a> or scan the QR Code
</p>
</div>
<div style="clear: both"></div>
<?php
}
?>

View File

@ -0,0 +1,24 @@
Changelog
---------
absoluteurl v1.6, March 12, 2010
---------------------------------
- added encode_url function to convert an absolute url to its percentage
encoded equivalent, according to RFC 3986
absoluteurl v1.5, March 11, 2010
---------------------------------
- fixed to allow spaces in the path of url
absoluteurl v1.4, October 2, 2009
----------------------------------------------
- Percentage encoding of the absolute url disabled.
absoluteurl v1.2, 2009-02-27
-----------------------------------------------
- Minor bug fix
absoluteurl v1.0, 2009-08-28
----------------------------------------
- Initial release

View File

@ -0,0 +1,26 @@
absoluteurl
---------------------
This script converts the relative url to absolute url, provided a base url.
For more, look here: http://publicmind.in/blog/urltoabsolute
Usage:
----------
Extract the script (url_to_absolute.php) into your web directory, include it into your current php file using:
require(path-to-file);
then, you can convert the relative url to absolute url by calling:
url_to_absolute( $baseUrl, $relativeUrl);
It return false on failure, otherwise returns the absolute url. If the $relativeUrl is a valid absolute url, it is returned without any modification.
Author/credits
-----------
1) Original author: David R. Nadeau, NadeauSoftware.com
2) Edited and maintained by: Nitin Kr, Gupta, publicmind.in

View File

@ -0,0 +1,485 @@
<?php
/**
* Edited by Nitin Kr. Gupta, publicmind.in
*/
/**
* Copyright (c) 2008, David R. Nadeau, NadeauSoftware.com.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the names of David R. Nadeau or NadeauSoftware.com, nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
/*
* This is a BSD License approved by the Open Source Initiative (OSI).
* See: http://www.opensource.org/licenses/bsd-license.php
*/
/**
* Combine a base URL and a relative URL to produce a new
* absolute URL. The base URL is often the URL of a page,
* and the relative URL is a URL embedded on that page.
*
* This function implements the "absolutize" algorithm from
* the RFC3986 specification for URLs.
*
* This function supports multi-byte characters with the UTF-8 encoding,
* per the URL specification.
*
* Parameters:
* baseUrl the absolute base URL.
*
* url the relative URL to convert.
*
* Return values:
* An absolute URL that combines parts of the base and relative
* URLs, or FALSE if the base URL is not absolute or if either
* URL cannot be parsed.
*/
function url_to_absolute( $baseUrl, $relativeUrl )
{
// If relative URL has a scheme, clean path and return.
$r = split_url( $relativeUrl );
if ( $r === FALSE )
return FALSE;
if ( !empty( $r['scheme'] ) )
{
if ( !empty( $r['path'] ) && $r['path'][0] == '/' )
$r['path'] = url_remove_dot_segments( $r['path'] );
return join_url( $r );
}
// Make sure the base URL is absolute.
$b = split_url( $baseUrl );
if ( $b === FALSE || empty( $b['scheme'] ) || empty( $b['host'] ) )
return FALSE;
$r['scheme'] = $b['scheme'];
// If relative URL has an authority, clean path and return.
if ( isset( $r['host'] ) )
{
if ( !empty( $r['path'] ) )
$r['path'] = url_remove_dot_segments( $r['path'] );
return join_url( $r );
}
unset( $r['port'] );
unset( $r['user'] );
unset( $r['pass'] );
// Copy base authority.
$r['host'] = $b['host'];
if ( isset( $b['port'] ) ) $r['port'] = $b['port'];
if ( isset( $b['user'] ) ) $r['user'] = $b['user'];
if ( isset( $b['pass'] ) ) $r['pass'] = $b['pass'];
// If relative URL has no path, use base path
if ( empty( $r['path'] ) )
{
if ( !empty( $b['path'] ) )
$r['path'] = $b['path'];
if ( !isset( $r['query'] ) && isset( $b['query'] ) )
$r['query'] = $b['query'];
return join_url( $r );
}
// If relative URL path doesn't start with /, merge with base path
if ( $r['path'][0] != '/' )
{
$base = mb_strrchr( $b['path'], '/', TRUE, 'UTF-8' );
if ( $base === FALSE ) $base = '';
$r['path'] = $base . '/' . $r['path'];
}
$r['path'] = url_remove_dot_segments( $r['path'] );
return join_url( $r );
}
/**
* Filter out "." and ".." segments from a URL's path and return
* the result.
*
* This function implements the "remove_dot_segments" algorithm from
* the RFC3986 specification for URLs.
*
* This function supports multi-byte characters with the UTF-8 encoding,
* per the URL specification.
*
* Parameters:
* path the path to filter
*
* Return values:
* The filtered path with "." and ".." removed.
*/
function url_remove_dot_segments( $path )
{
// multi-byte character explode
$inSegs = preg_split( '!/!u', $path );
$outSegs = array( );
foreach ( $inSegs as $seg )
{
if ( $seg == '' || $seg == '.')
continue;
if ( $seg == '..' )
array_pop( $outSegs );
else
array_push( $outSegs, $seg );
}
$outPath = implode( '/', $outSegs );
if ( $path[0] == '/' )
$outPath = '/' . $outPath;
// compare last multi-byte character against '/'
if ( $outPath != '/' &&
(mb_strlen($path)-1) == mb_strrpos( $path, '/', 'UTF-8' ) )
$outPath .= '/';
return $outPath;
}
/**
* This function parses an absolute or relative URL and splits it
* into individual components.
*
* RFC3986 specifies the components of a Uniform Resource Identifier (URI).
* A portion of the ABNFs are repeated here:
*
* URI-reference = URI
* / relative-ref
*
* URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
*
* relative-ref = relative-part [ "?" query ] [ "#" fragment ]
*
* hier-part = "//" authority path-abempty
* / path-absolute
* / path-rootless
* / path-empty
*
* relative-part = "//" authority path-abempty
* / path-absolute
* / path-noscheme
* / path-empty
*
* authority = [ userinfo "@" ] host [ ":" port ]
*
* So, a URL has the following major components:
*
* scheme
* The name of a method used to interpret the rest of
* the URL. Examples: "http", "https", "mailto", "file'.
*
* authority
* The name of the authority governing the URL's name
* space. Examples: "example.com", "user@example.com",
* "example.com:80", "user:password@example.com:80".
*
* The authority may include a host name, port number,
* user name, and password.
*
* The host may be a name, an IPv4 numeric address, or
* an IPv6 numeric address.
*
* path
* The hierarchical path to the URL's resource.
* Examples: "/index.htm", "/scripts/page.php".
*
* query
* The data for a query. Examples: "?search=google.com".
*
* fragment
* The name of a secondary resource relative to that named
* by the path. Examples: "#section1", "#header".
*
* An "absolute" URL must include a scheme and path. The authority, query,
* and fragment components are optional.
*
* A "relative" URL does not include a scheme and must include a path. The
* authority, query, and fragment components are optional.
*
* This function splits the $url argument into the following components
* and returns them in an associative array. Keys to that array include:
*
* "scheme" The scheme, such as "http".
* "host" The host name, IPv4, or IPv6 address.
* "port" The port number.
* "user" The user name.
* "pass" The user password.
* "path" The path, such as a file path for "http".
* "query" The query.
* "fragment" The fragment.
*
* One or more of these may not be present, depending upon the URL.
*
* Optionally, the "user", "pass", "host" (if a name, not an IP address),
* "path", "query", and "fragment" may have percent-encoded characters
* decoded. The "scheme" and "port" cannot include percent-encoded
* characters and are never decoded. Decoding occurs after the URL has
* been parsed.
*
* Parameters:
* url the URL to parse.
*
* decode an optional boolean flag selecting whether
* to decode percent encoding or not. Default = TRUE.
*
* Return values:
* the associative array of URL parts, or FALSE if the URL is
* too malformed to recognize any parts.
*/
function split_url( $url, $decode=FALSE)
{
// Character sets from RFC3986.
$xunressub = 'a-zA-Z\d\-._~\!$&\'()*+,;=';
$xpchar = $xunressub . ':@% ';
// Scheme from RFC3986.
$xscheme = '([a-zA-Z][a-zA-Z\d+-.]*)';
// User info (user + password) from RFC3986.
$xuserinfo = '(([' . $xunressub . '%]*)' .
'(:([' . $xunressub . ':%]*))?)';
// IPv4 from RFC3986 (without digit constraints).
$xipv4 = '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})';
// IPv6 from RFC2732 (without digit and grouping constraints).
$xipv6 = '(\[([a-fA-F\d.:]+)\])';
// Host name from RFC1035. Technically, must start with a letter.
// Relax that restriction to better parse URL structure, then
// leave host name validation to application.
$xhost_name = '([a-zA-Z\d-.%]+)';
// Authority from RFC3986. Skip IP future.
$xhost = '(' . $xhost_name . '|' . $xipv4 . '|' . $xipv6 . ')';
$xport = '(\d*)';
$xauthority = '((' . $xuserinfo . '@)?' . $xhost .
'?(:' . $xport . ')?)';
// Path from RFC3986. Blend absolute & relative for efficiency.
$xslash_seg = '(/[' . $xpchar . ']*)';
$xpath_authabs = '((//' . $xauthority . ')((/[' . $xpchar . ']*)*))';
$xpath_rel = '([' . $xpchar . ']+' . $xslash_seg . '*)';
$xpath_abs = '(/(' . $xpath_rel . ')?)';
$xapath = '(' . $xpath_authabs . '|' . $xpath_abs .
'|' . $xpath_rel . ')';
// Query and fragment from RFC3986.
$xqueryfrag = '([' . $xpchar . '/?' . ']*)';
// URL.
$xurl = '^(' . $xscheme . ':)?' . $xapath . '?' .
'(\?' . $xqueryfrag . ')?(#' . $xqueryfrag . ')?$';
// Split the URL into components.
if ( !preg_match( '!' . $xurl . '!', $url, $m ) )
return FALSE;
if ( !empty($m[2]) ) $parts['scheme'] = strtolower($m[2]);
if ( !empty($m[7]) ) {
if ( isset( $m[9] ) ) $parts['user'] = $m[9];
else $parts['user'] = '';
}
if ( !empty($m[10]) ) $parts['pass'] = $m[11];
if ( !empty($m[13]) ) $h=$parts['host'] = $m[13];
else if ( !empty($m[14]) ) $parts['host'] = $m[14];
else if ( !empty($m[16]) ) $parts['host'] = $m[16];
else if ( !empty( $m[5] ) ) $parts['host'] = '';
if ( !empty($m[17]) ) $parts['port'] = $m[18];
if ( !empty($m[19]) ) $parts['path'] = $m[19];
else if ( !empty($m[21]) ) $parts['path'] = $m[21];
else if ( !empty($m[25]) ) $parts['path'] = $m[25];
if ( !empty($m[27]) ) $parts['query'] = $m[28];
if ( !empty($m[29]) ) $parts['fragment']= $m[30];
if ( !$decode )
return $parts;
if ( !empty($parts['user']) )
$parts['user'] = rawurldecode( $parts['user'] );
if ( !empty($parts['pass']) )
$parts['pass'] = rawurldecode( $parts['pass'] );
if ( !empty($parts['path']) )
$parts['path'] = rawurldecode( $parts['path'] );
if ( isset($h) )
$parts['host'] = rawurldecode( $parts['host'] );
if ( !empty($parts['query']) )
$parts['query'] = rawurldecode( $parts['query'] );
if ( !empty($parts['fragment']) )
$parts['fragment'] = rawurldecode( $parts['fragment'] );
return $parts;
}
/**
* This function joins together URL components to form a complete URL.
*
* RFC3986 specifies the components of a Uniform Resource Identifier (URI).
* This function implements the specification's "component recomposition"
* algorithm for combining URI components into a full URI string.
*
* The $parts argument is an associative array containing zero or
* more of the following:
*
* "scheme" The scheme, such as "http".
* "host" The host name, IPv4, or IPv6 address.
* "port" The port number.
* "user" The user name.
* "pass" The user password.
* "path" The path, such as a file path for "http".
* "query" The query.
* "fragment" The fragment.
*
* The "port", "user", and "pass" values are only used when a "host"
* is present.
*
* The optional $encode argument indicates if appropriate URL components
* should be percent-encoded as they are assembled into the URL. Encoding
* is only applied to the "user", "pass", "host" (if a host name, not an
* IP address), "path", "query", and "fragment" components. The "scheme"
* and "port" are never encoded. When a "scheme" and "host" are both
* present, the "path" is presumed to be hierarchical and encoding
* processes each segment of the hierarchy separately (i.e., the slashes
* are left alone).
*
* The assembled URL string is returned.
*
* Parameters:
* parts an associative array of strings containing the
* individual parts of a URL.
*
* encode an optional boolean flag selecting whether
* to do percent encoding or not. Default = true.
*
* Return values:
* Returns the assembled URL string. The string is an absolute
* URL if a scheme is supplied, and a relative URL if not. An
* empty string is returned if the $parts array does not contain
* any of the needed values.
*/
function join_url( $parts, $encode=FALSE)
{
if ( $encode )
{
if ( isset( $parts['user'] ) )
$parts['user'] = rawurlencode( $parts['user'] );
if ( isset( $parts['pass'] ) )
$parts['pass'] = rawurlencode( $parts['pass'] );
if ( isset( $parts['host'] ) &&
!preg_match( '!^(\[[\da-f.:]+\]])|([\da-f.:]+)$!ui', $parts['host'] ) )
$parts['host'] = rawurlencode( $parts['host'] );
if ( !empty( $parts['path'] ) )
$parts['path'] = preg_replace( '!%2F!ui', '/',
rawurlencode( $parts['path'] ) );
if ( isset( $parts['query'] ) )
$parts['query'] = rawurlencode( $parts['query'] );
if ( isset( $parts['fragment'] ) )
$parts['fragment'] = rawurlencode( $parts['fragment'] );
}
$url = '';
if ( !empty( $parts['scheme'] ) )
$url .= $parts['scheme'] . ':';
if ( isset( $parts['host'] ) )
{
$url .= '//';
if ( isset( $parts['user'] ) )
{
$url .= $parts['user'];
if ( isset( $parts['pass'] ) )
$url .= ':' . $parts['pass'];
$url .= '@';
}
if ( preg_match( '!^[\da-f]*:[\da-f.:]+$!ui', $parts['host'] ) )
$url .= '[' . $parts['host'] . ']'; // IPv6
else
$url .= $parts['host']; // IPv4 or name
if ( isset( $parts['port'] ) )
$url .= ':' . $parts['port'];
if ( !empty( $parts['path'] ) && $parts['path'][0] != '/' )
$url .= '/';
}
if ( !empty( $parts['path'] ) )
$url .= $parts['path'];
if ( isset( $parts['query'] ) )
$url .= '?' . $parts['query'];
if ( isset( $parts['fragment'] ) )
$url .= '#' . $parts['fragment'];
return $url;
}
/**
* This function encodes URL to form a URL which is properly
* percent encoded to replace disallowed characters.
*
* RFC3986 specifies the allowed characters in the URL as well as
* reserved characters in the URL. This function replaces all the
* disallowed characters in the URL with their repective percent
* encodings. Already encoded characters are not encoded again,
* such as '%20' is not encoded to '%2520'.
*
* Parameters:
* url the url to encode.
*
* Return values:
* Returns the encoded URL string.
*/
function encode_url($url) {
$reserved = array(
":" => '!%3A!ui',
"/" => '!%2F!ui',
"?" => '!%3F!ui',
"#" => '!%23!ui',
"[" => '!%5B!ui',
"]" => '!%5D!ui',
"@" => '!%40!ui',
"!" => '!%21!ui',
"$" => '!%24!ui',
"&" => '!%26!ui',
"'" => '!%27!ui',
"(" => '!%28!ui',
")" => '!%29!ui',
"*" => '!%2A!ui',
"+" => '!%2B!ui',
"," => '!%2C!ui',
";" => '!%3B!ui',
"=" => '!%3D!ui',
"%" => '!%25!ui',
);
$url = rawurlencode($url);
$url = preg_replace(array_values($reserved), array_keys($reserved), $url);
return $url;
}
?>

View File

@ -0,0 +1,35 @@
<?php
function loadUserScriptHeader($file)
{
$result = Array();
if ( is_string($file) )
$file = fopen ( $file, "rt" );
# else assume it's already a readable stream
while ( ( $line = fgets ( $file ) ) !== FALSE )
{
if ( preg_match ( '#//[ \\t]*==/UserScript==#', $line ) )
break;
$matches = Array();
if ( preg_match ( '#^//[ \\t]*(@[a-zA-Z0-9]+)[ \\t]+(.*)$#', $line, $matches ) )
{
$name = $matches[1];
$value = $matches[2];
if ( ! array_key_exists ( $name, $result ) )
{
$result[$name] = $value;
}
}
}
fclose ( $file );
return $result;
}
?>