From 8e0b068b1d6929af82ef348a47b235dd89196c11 Mon Sep 17 00:00:00 2001 From: sostler Date: Sat, 27 Feb 2010 23:22:29 -0500 Subject: Fixed CLRF --- webcam/com/adobe/net/DynamicURLLoader.as | 114 +- webcam/com/adobe/net/IURIResolver.as | 156 +- webcam/com/adobe/net/URI.as | 4936 ++++++++++++------------- webcam/com/adobe/net/URIEncodingBitmap.as | 282 +- webcam/com/adobe/net/proxies/RFC2817Socket.as | 406 +- 5 files changed, 2947 insertions(+), 2947 deletions(-) (limited to 'webcam/com/adobe/net') diff --git a/webcam/com/adobe/net/DynamicURLLoader.as b/webcam/com/adobe/net/DynamicURLLoader.as index ae69f22..9b16a61 100644 --- a/webcam/com/adobe/net/DynamicURLLoader.as +++ b/webcam/com/adobe/net/DynamicURLLoader.as @@ -1,58 +1,58 @@ -/* - Adobe Systems Incorporated(r) Source Code License Agreement - Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. - - Please read this Source Code License Agreement carefully before using - the source code. - - Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, - no-charge, royalty-free, irrevocable copyright license, to reproduce, - prepare derivative works of, publicly display, publicly perform, and - distribute this source code and such derivative works in source or - object code form without any attribution requirements. - - The name "Adobe Systems Incorporated" must not be used to endorse or promote products - derived from the source code without prior written permission. - - You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and - against any loss, damage, claims or lawsuits, including attorney's - fees that arise or result from your use or distribution of the source - code. - - THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT - ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, - BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF - NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA - OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package com.adobe.net -{ - import flash.net.URLLoader; - - /** - * Class that provides a dynamic implimentation of the URLLoader class. - * - * This class provides no API implimentations. However, since the class is - * declared as dynamic, it can be used in place of URLLoader, and allow - * you to dynamically attach properties to it (which URLLoader does not allow). - * - * @langversion ActionScript 3.0 - * @playerversion Flash 9.0 - * @tiptext - */ - public dynamic class DynamicURLLoader extends URLLoader - { - public function DynamicURLLoader() - { - super(); - } - } +/* + Adobe Systems Incorporated(r) Source Code License Agreement + Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. + + Please read this Source Code License Agreement carefully before using + the source code. + + Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable copyright license, to reproduce, + prepare derivative works of, publicly display, publicly perform, and + distribute this source code and such derivative works in source or + object code form without any attribution requirements. + + The name "Adobe Systems Incorporated" must not be used to endorse or promote products + derived from the source code without prior written permission. + + You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and + against any loss, damage, claims or lawsuits, including attorney's + fees that arise or result from your use or distribution of the source + code. + + THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT + ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF + NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA + OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.adobe.net +{ + import flash.net.URLLoader; + + /** + * Class that provides a dynamic implimentation of the URLLoader class. + * + * This class provides no API implimentations. However, since the class is + * declared as dynamic, it can be used in place of URLLoader, and allow + * you to dynamically attach properties to it (which URLLoader does not allow). + * + * @langversion ActionScript 3.0 + * @playerversion Flash 9.0 + * @tiptext + */ + public dynamic class DynamicURLLoader extends URLLoader + { + public function DynamicURLLoader() + { + super(); + } + } } \ No newline at end of file diff --git a/webcam/com/adobe/net/IURIResolver.as b/webcam/com/adobe/net/IURIResolver.as index ed8acd8..658a95a 100644 --- a/webcam/com/adobe/net/IURIResolver.as +++ b/webcam/com/adobe/net/IURIResolver.as @@ -1,79 +1,79 @@ -/* - Adobe Systems Incorporated(r) Source Code License Agreement - Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. - - Please read this Source Code License Agreement carefully before using - the source code. - - Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, - no-charge, royalty-free, irrevocable copyright license, to reproduce, - prepare derivative works of, publicly display, publicly perform, and - distribute this source code and such derivative works in source or - object code form without any attribution requirements. - - The name "Adobe Systems Incorporated" must not be used to endorse or promote products - derived from the source code without prior written permission. - - You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and - against any loss, damage, claims or lawsuits, including attorney's - fees that arise or result from your use or distribution of the source - code. - - THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT - ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, - BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF - NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA - OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package com.adobe.net -{ - /** - * The URI class cannot know about DNS aliases, virtual hosts, or - * symbolic links that may be involved. The application can provide - * an implementation of this interface to resolve the URI before the - * URI class makes any comparisons. For example, a web host has - * two aliases: - * - *

- * http://www.site.com/ - * http://www.site.net/ - *

- * - *

The application can provide an implementation that automatically - * resolves site.net to site.com before URI compares two URI objects. - * Only the application can know and understand the context in which - * the URI's are being used.

- * - *

Use the URI.resolver accessor to assign a custom resolver to - * the URI class. Any resolver specified is global to all instances - * of URI.

- * - *

URI will call this before performing URI comparisons in the - * URI.getRelation() and URI.getCommonParent() functions. - * - * @see URI.getRelation - * @see URI.getCommonParent - * - * @langversion ActionScript 3.0 - * @playerversion Flash 9.0 - */ - public interface IURIResolver - { - /** - * Implement this method to provide custom URI resolution for - * your application. - * - * @langversion ActionScript 3.0 - * @playerversion Flash 9.0 - */ - function resolve(uri:URI) : URI; - } +/* + Adobe Systems Incorporated(r) Source Code License Agreement + Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. + + Please read this Source Code License Agreement carefully before using + the source code. + + Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable copyright license, to reproduce, + prepare derivative works of, publicly display, publicly perform, and + distribute this source code and such derivative works in source or + object code form without any attribution requirements. + + The name "Adobe Systems Incorporated" must not be used to endorse or promote products + derived from the source code without prior written permission. + + You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and + against any loss, damage, claims or lawsuits, including attorney's + fees that arise or result from your use or distribution of the source + code. + + THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT + ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF + NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA + OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.adobe.net +{ + /** + * The URI class cannot know about DNS aliases, virtual hosts, or + * symbolic links that may be involved. The application can provide + * an implementation of this interface to resolve the URI before the + * URI class makes any comparisons. For example, a web host has + * two aliases: + * + *

+ * http://www.site.com/ + * http://www.site.net/ + *

+ * + *

The application can provide an implementation that automatically + * resolves site.net to site.com before URI compares two URI objects. + * Only the application can know and understand the context in which + * the URI's are being used.

+ * + *

Use the URI.resolver accessor to assign a custom resolver to + * the URI class. Any resolver specified is global to all instances + * of URI.

+ * + *

URI will call this before performing URI comparisons in the + * URI.getRelation() and URI.getCommonParent() functions. + * + * @see URI.getRelation + * @see URI.getCommonParent + * + * @langversion ActionScript 3.0 + * @playerversion Flash 9.0 + */ + public interface IURIResolver + { + /** + * Implement this method to provide custom URI resolution for + * your application. + * + * @langversion ActionScript 3.0 + * @playerversion Flash 9.0 + */ + function resolve(uri:URI) : URI; + } } \ No newline at end of file diff --git a/webcam/com/adobe/net/URI.as b/webcam/com/adobe/net/URI.as index 8ec9acd..40e1cbc 100644 --- a/webcam/com/adobe/net/URI.as +++ b/webcam/com/adobe/net/URI.as @@ -1,2469 +1,2469 @@ -/* -Adobe Systems Incorporated(r) Source Code License Agreement -Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. - -Please read this Source Code License Agreement carefully before using -the source code. - -Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable copyright license, to reproduce, -prepare derivative works of, publicly display, publicly perform, and -distribute this source code and such derivative works in source or -object code form without any attribution requirements. - -The name "Adobe Systems Incorporated" must not be used to endorse or promote products -derived from the source code without prior written permission. - -You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and -against any loss, damage, claims or lawsuits, including attorney's -fees that arise or result from your use or distribution of the source -code. - -THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT -ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF -NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA -OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package com.adobe.net -{ - import flash.utils.ByteArray; - - /** - * This class implements functions and utilities for working with URI's - * (Universal Resource Identifiers). For technical description of the - * URI syntax, please see RFC 3986 at http://www.ietf.org/rfc/rfc3986.txt - * or do a web search for "rfc 3986". - * - *

The most important aspect of URI's to understand is that URI's - * and URL's are not strings. URI's are complex data structures that - * encapsulate many pieces of information. The string version of a - * URI is the serialized representation of that data structure. This - * string serialization is used to provide a human readable - * representation and a means to transport the data over the network - * where it can then be parsed back into its' component parts.

- * - *

URI's fall into one of three categories: - *

- * - *

The query and fragment parts are optional.

- * - *

This class supports both non-hierarchical and hierarchical URI's

- * - *

This class is intended to be used "as-is" for the vast majority - * of common URI's. However, if your application requires a custom - * URI syntax (e.g. custom query syntax or special handling of - * non-hierarchical URI's), this class can be fully subclassed. If you - * intended to subclass URI, please see the source code for complete - * documation on protected members and protected fuctions.

- * - * @langversion ActionScript 3.0 - * @playerversion Flash 9.0 - */ - public class URI - { - // Here we define which characters must be escaped for each - // URI part. The characters that must be escaped for each - // part differ depending on what would cause ambiguous parsing. - // RFC 3986 sec. 2.4 states that characters should only be - // encoded when they would conflict with subcomponent delimiters. - // We don't want to over-do the escaping. We only want to escape - // the minimum needed to prevent parsing problems. - - // space and % must be escaped in all cases. '%' is the delimiter - // for escaped characters. - public static const URImustEscape:String = " %"; - - // Baseline of what characters must be escaped - public static const URIbaselineEscape:String = URImustEscape + ":?#/@"; - - // Characters that must be escaped in the part part. - public static const URIpathEscape:String = URImustEscape + "?#"; - - // Characters that must be escaped in the query part, if setting - // the query as a whole string. If the query is set by - // name/value, URIqueryPartEscape is used instead. - public static const URIqueryEscape:String = URImustEscape + "#"; - - // This is what each name/value pair must escape "&=" as well - // so they don't conflict with the "param=value¶m2=value2" - // syntax. - public static const URIqueryPartEscape:String = URImustEscape + "#&="; - - // Non-hierarchical URI's can have query and fragment parts, but - // we also want to prevent '/' otherwise it might end up looking - // like a hierarchical URI to the parser. - public static const URInonHierEscape:String = URImustEscape + "?#/"; - - // Baseline uninitialized setting for the URI scheme. - public static const UNKNOWN_SCHEME:String = "unknown"; - - // The following bitmaps are used for performance enhanced - // character escaping. - - // Baseline characters that need to be escaped. Many parts use - // this. - protected static const URIbaselineExcludedBitmap:URIEncodingBitmap = - new URIEncodingBitmap(URIbaselineEscape); - - // Scheme escaping bitmap - protected static const URIschemeExcludedBitmap:URIEncodingBitmap = - URIbaselineExcludedBitmap; - - // User/pass escaping bitmap - protected static const URIuserpassExcludedBitmap:URIEncodingBitmap = - URIbaselineExcludedBitmap; - - // Authority escaping bitmap - protected static const URIauthorityExcludedBitmap:URIEncodingBitmap = - URIbaselineExcludedBitmap; - - // Port escaping bitmap - protected static const URIportExludedBitmap:URIEncodingBitmap = - URIbaselineExcludedBitmap; - - // Path escaping bitmap - protected static const URIpathExcludedBitmap:URIEncodingBitmap = - new URIEncodingBitmap(URIpathEscape); - - // Query (whole) escaping bitmap - protected static const URIqueryExcludedBitmap:URIEncodingBitmap = - new URIEncodingBitmap(URIqueryEscape); - - // Query (individual parts) escaping bitmap - protected static const URIqueryPartExcludedBitmap:URIEncodingBitmap = - new URIEncodingBitmap(URIqueryPartEscape); - - // Fragments are the last part in the URI. They only need to - // escape space, '#', and '%'. Turns out that is what query - // uses too. - protected static const URIfragmentExcludedBitmap:URIEncodingBitmap = - URIqueryExcludedBitmap; - - // Characters that need to be escaped in the non-hierarchical part - protected static const URInonHierexcludedBitmap:URIEncodingBitmap = - new URIEncodingBitmap(URInonHierEscape); - - // Values used by getRelation() - public static const NOT_RELATED:int = 0; - public static const CHILD:int = 1; - public static const EQUAL:int = 2; - public static const PARENT:int = 3; - - //------------------------------------------------------------------- - // protected class members - //------------------------------------------------------------------- - protected var _valid:Boolean = false; - protected var _relative:Boolean = false; - protected var _scheme:String = ""; - protected var _authority:String = ""; - protected var _username:String = ""; - protected var _password:String = ""; - protected var _port:String = ""; - protected var _path:String = ""; - protected var _query:String = ""; - protected var _fragment:String = ""; - protected var _nonHierarchical:String = ""; - protected static var _resolver:IURIResolver = null; - - - /** - * URI Constructor. If no string is given, this will initialize - * this URI object to a blank URI. - */ - public function URI(uri:String = null) : void - { - if (uri == null) - initialize(); - else - constructURI(uri); - } - - - /** - * @private - * Method that loads the URI from the given string. - */ - protected function constructURI(uri:String) : Boolean - { - if (!parseURI(uri)) - _valid = false; - - return isValid(); - } - - - /** - * @private Private initializiation. - */ - protected function initialize() : void - { - _valid = false; - _relative = false; - - _scheme = UNKNOWN_SCHEME; - _authority = ""; - _username = ""; - _password = ""; - _port = ""; - _path = ""; - _query = ""; - _fragment = ""; - - _nonHierarchical = ""; - } - - /** - * @private Accessor to explicitly set/get the hierarchical - * state of the URI. - */ - protected function set hierState(state:Boolean) : void - { - if (state) - { - // Clear the non-hierarchical data - _nonHierarchical = ""; - - // Also set the state vars while we are at it - if (_scheme == "" || _scheme == UNKNOWN_SCHEME) - _relative = true; - else - _relative = false; - - if (_authority.length == 0 && _path.length == 0) - _valid = false; - else - _valid = true; - } - else - { - // Clear the hierarchical data - _authority = ""; - _username = ""; - _password = ""; - _port = ""; - _path = ""; - - _relative = false; - - if (_scheme == "" || _scheme == UNKNOWN_SCHEME) - _valid = false; - else - _valid = true; - } - } - protected function get hierState() : Boolean - { - return (_nonHierarchical.length == 0); - } - - - /** - * @private Functions that performs some basic consistency validation. - */ - protected function validateURI() : Boolean - { - // Check the scheme - if (isAbsolute()) - { - if (_scheme.length <= 1 || _scheme == UNKNOWN_SCHEME) - { - // we probably parsed a C:\ type path or no scheme - return false; - } - else if (verifyAlpha(_scheme) == false) - return false; // Scheme contains bad characters - } - - if (hierState) - { - if (_path.search('\\') != -1) - return false; // local path - else if (isRelative() == false && _scheme == UNKNOWN_SCHEME) - return false; // It's an absolute URI, but it has a bad scheme - } - else - { - if (_nonHierarchical.search('\\') != -1) - return false; // some kind of local path - } - - // Looks like it's ok. - return true; - } - - - /** - * @private - * - * Given a URI in string format, parse that sucker into its basic - * components and assign them to this object. A URI is of the form: - * :?# - * - * For simplicity, we parse the URI in the following order: - * - * 1. Fragment (anchors) - * 2. Query (CGI stuff) - * 3. Scheme ("http") - * 4. Authority (host name) - * 5. Username/Password (if any) - * 6. Port (server port if any) - * 7. Path (/homepages/mypage.html) - * - * The reason for this order is to minimize any parsing ambiguities. - * Fragments and queries can contain almost anything (they are parts - * that can contain custom data with their own syntax). Parsing - * them out first removes a large chance of parsing errors. This - * method expects well formed URI's, but performing the parse in - * this order makes us a little more tolerant of user error. - * - * REGEXP - * Why doesn't this use regular expressions to parse the URI? We - * have found that in a real world scenario, URI's are not always - * well formed. Sometimes characters that should have been escaped - * are not, and those situations would break a regexp pattern. This - * function attempts to be smart about what it is parsing based on - * location of characters relative to eachother. This function has - * been proven through real-world use to parse the vast majority - * of URI's correctly. - * - * NOTE - * It is assumed that the string in URI form is escaped. This function - * does not escape anything. If you constructed the URI string by - * hand, and used this to parse in the URI and still need it escaped, - * call forceEscape() on your URI object. - * - * Parsing Assumptions - * This routine assumes that the URI being passed is well formed. - * Passing things like local paths, malformed URI's, and the such - * will result in parsing errors. This function can handle - * - absolute hierarchical (e.g. "http://something.com/index.html), - * - relative hierarchical (e.g. "../images/flower.gif"), or - * - non-hierarchical URIs (e.g. "mailto:jsmith@fungoo.com"). - * - * Anything else will probably result in a parsing error, or a bogus - * URI object. - * - * Note that non-hierarchical URIs *MUST* have a scheme, otherwise - * they will be mistaken for relative URI's. - * - * If you are not sure what is being passed to you (like manually - * entered text from UI), you can construct a blank URI object and - * call unknownToURI() passing in the unknown string. - * - * @return true if successful, false if there was some kind of - * parsing error - */ - protected function parseURI(uri:String) : Boolean - { - var baseURI:String = uri; - var index:int, index2:int; - - // Make sure this object is clean before we start. If it was used - // before and we are now parsing a new URI, we don't want any stale - // info lying around. - initialize(); - - // Remove any fragments (anchors) from the URI - index = baseURI.indexOf("#"); - if (index != -1) - { - // Store the fragment piece if any - if (baseURI.length > (index + 1)) // +1 is to skip the '#' - _fragment = baseURI.substr(index + 1, baseURI.length - (index + 1)); - - // Trim off the fragment - baseURI = baseURI.substr(0, index); - } - - // We need to strip off any CGI parameters (eg '?param=bob') - index = baseURI.indexOf("?"); - if (index != -1) - { - if (baseURI.length > (index + 1)) - _query = baseURI.substr(index + 1, baseURI.length - (index + 1)); // +1 is to skip the '?' - - // Trim off the query - baseURI = baseURI.substr(0, index); - } - - // Now try to find the scheme part - index = baseURI.search(':'); - index2 = baseURI.search('/'); - - var containsColon:Boolean = (index != -1); - var containsSlash:Boolean = (index2 != -1); - - // This value is indeterminate if "containsColon" is false. - // (if there is no colon, does the slash come before or - // after said non-existing colon?) - var colonBeforeSlash:Boolean = (!containsSlash || index < index2); - - // If it has a colon and it's before the first slash, we will treat - // it as a scheme. If a slash is before a colon, there must be a - // stray colon in a path or something. In which case, the colon is - // not the separator for the scheme. Technically, we could consider - // this an error, but since this is not an ambiguous state (we know - // 100% that this has no scheme), we will keep going. - if (containsColon && colonBeforeSlash) - { - // We found a scheme - _scheme = baseURI.substr(0, index); - - // Normalize the scheme - _scheme = _scheme.toLowerCase(); - - baseURI = baseURI.substr(index + 1); - - if (baseURI.substr(0, 2) == "//") - { - // This is a hierarchical URI - _nonHierarchical = ""; - - // Trim off the "//" - baseURI = baseURI.substr(2, baseURI.length - 2); - } - else - { - // This is a non-hierarchical URI like "mailto:bob@mail.com" - _nonHierarchical = baseURI; - - if ((_valid = validateURI()) == false) - initialize(); // Bad URI. Clear it. - - // No more parsing to do for this case - return isValid(); - } - } - else - { - // No scheme. We will consider this a relative URI - _scheme = ""; - _relative = true; - _nonHierarchical = ""; - } - - // Ok, what we have left is everything after the :// - - // Now that we have stripped off any query and fragment parts, we - // need to split the authority from the path - - if (isRelative()) - { - // Don't bother looking for the authority. It's a relative URI - _authority = ""; - _port = ""; - _path = baseURI; - } - else - { - // Check for malformed UNC style file://///server/type/path/ - // By the time we get here, we have already trimmed the "file://" - // so baseURI will be ///server/type/path. If baseURI only - // has one slash, we leave it alone because that is valid (that - // is the case of "file:///path/to/file.txt" where there is no - // server - implicit "localhost"). - if (baseURI.substr(0, 2) == "//") - { - // Trim all leading slashes - while(baseURI.charAt(0) == "/") - baseURI = baseURI.substr(1, baseURI.length - 1); - } - - index = baseURI.search('/'); - if (index == -1) - { - // No path. We must have passed something like "http://something.com" - _authority = baseURI; - _path = ""; - } - else - { - _authority = baseURI.substr(0, index); - _path = baseURI.substr(index, baseURI.length - index); - } - - // Check to see if the URI has any username or password information. - // For example: ftp://username:password@server.com - index = _authority.search('@'); - if (index != -1) - { - // We have a username and possibly a password - _username = _authority.substr(0, index); - - // Remove the username/password from the authority - _authority = _authority.substr(index + 1); // Skip the '@' - - // Now check to see if the username also has a password - index = _username.search(':'); - if (index != -1) - { - _password = _username.substring(index + 1, _username.length); - _username = _username.substr(0, index); - } - else - _password = ""; - } - else - { - _username = ""; - _password = ""; - } - - // Lastly, check to see if the authorty has a port number. - // This is parsed after the username/password to avoid conflicting - // with the ':' in the 'username:password' if one exists. - index = _authority.search(':'); - if (index != -1) - { - _port = _authority.substring(index + 1, _authority.length); // skip the ':' - _authority = _authority.substr(0, index); - } - else - { - _port = ""; - } - - // Lastly, normalize the authority. Domain names - // are case insensitive. - _authority = _authority.toLowerCase(); - } - - if ((_valid = validateURI()) == false) - initialize(); // Bad URI. Clear it - - return isValid(); - } - - - /******************************************************************** - * Copy function. - */ - public function copyURI(uri:URI) : void - { - this._scheme = uri._scheme; - this._authority = uri._authority; - this._username = uri._username; - this._password = uri._password; - this._port = uri._port; - this._path = uri._path; - this._query = uri._query; - this._fragment = uri._fragment; - this._nonHierarchical = uri._nonHierarchical; - - this._valid = uri._valid; - this._relative = uri._relative; - } - - - /** - * @private - * Checks if the given string only contains a-z or A-Z. - */ - protected function verifyAlpha(str:String) : Boolean - { - var pattern:RegExp = /[^a-z]/; - var index:int; - - str = str.toLowerCase(); - index = str.search(pattern); - - if (index == -1) - return true; - else - return false; - } - - /** - * Is this a valid URI? - * - * @return true if this object represents a valid URI, false - * otherwise. - */ - public function isValid() : Boolean - { - return this._valid; - } - - - /** - * Is this URI an absolute URI? An absolute URI is a complete, fully - * qualified reference to a resource. e.g. http://site.com/index.htm - * Non-hierarchical URI's are always absolute. - */ - public function isAbsolute() : Boolean - { - return !this._relative; - } - - - /** - * Is this URI a relative URI? Relative URI's do not have a scheme - * and only contain a relative path with optional anchor and query - * parts. e.g. "../reports/index.htm". Non-hierarchical URI's - * will never be relative. - */ - public function isRelative() : Boolean - { - return this._relative; - } - - - /** - * Does this URI point to a resource that is a directory/folder? - * The URI specification dictates that any path that ends in a slash - * is a directory. This is needed to be able to perform correct path - * logic when combining relative URI's with absolute URI's to - * obtain the correct absolute URI to a resource. - * - * @see URI.chdir - * - * @return true if this URI represents a directory resource, false - * if this URI represents a file resource. - */ - public function isDirectory() : Boolean - { - if (_path.length == 0) - return false; - - return (_path.charAt(path.length - 1) == '/'); - } - - - /** - * Is this URI a hierarchical URI? URI's can be - */ - public function isHierarchical() : Boolean - { - return hierState; - } - - - /** - * The scheme of the URI. - */ - public function get scheme() : String - { - return URI.unescapeChars(_scheme); - } - public function set scheme(schemeStr:String) : void - { - // Normalize the scheme - var normalized:String = schemeStr.toLowerCase(); - _scheme = URI.fastEscapeChars(normalized, URI.URIschemeExcludedBitmap); - } - - - /** - * The authority (host) of the URI. Only valid for - * hierarchical URI's. If the URI is relative, this will - * be an empty string. When setting this value, the string - * given is assumed to be unescaped. When retrieving this - * value, the resulting string is unescaped. - */ - public function get authority() : String - { - return URI.unescapeChars(_authority); - } - public function set authority(authorityStr:String) : void - { - // Normalize the authority - authorityStr = authorityStr.toLowerCase(); - - _authority = URI.fastEscapeChars(authorityStr, - URI.URIauthorityExcludedBitmap); - - // Only hierarchical URI's can have an authority, make - // sure this URI is of the proper format. - this.hierState = true; - } - - - /** - * The username of the URI. Only valid for hierarchical - * URI's. If the URI is relative, this will be an empty - * string. - * - *

The URI specification allows for authentication - * credentials to be embedded in the URI as such:

- * - *

http://user:passwd@host/path/to/file.htm

- * - *

When setting this value, the string - * given is assumed to be unescaped. When retrieving this - * value, the resulting string is unescaped.

- */ - public function get username() : String - { - return URI.unescapeChars(_username); - } - public function set username(usernameStr:String) : void - { - _username = URI.fastEscapeChars(usernameStr, URI.URIuserpassExcludedBitmap); - - // Only hierarchical URI's can have a username. - this.hierState = true; - } - - - /** - * The password of the URI. Similar to username. - * @see URI.username - */ - public function get password() : String - { - return URI.unescapeChars(_password); - } - public function set password(passwordStr:String) : void - { - _password = URI.fastEscapeChars(passwordStr, - URI.URIuserpassExcludedBitmap); - - // Only hierarchical URI's can have a password. - this.hierState = true; - } - - - /** - * The host port number. Only valid for hierarchical URI's. If - * the URI is relative, this will be an empty string. URI's can - * contain the port number of the remote host: - * - *

http://site.com:8080/index.htm

- */ - public function get port() : String - { - return URI.unescapeChars(_port); - } - public function set port(portStr:String) : void - { - _port = URI.escapeChars(portStr); - - // Only hierarchical URI's can have a port. - this.hierState = true; - } - - - /** - * The path portion of the URI. Only valid for hierarchical - * URI's. When setting this value, the string - * given is assumed to be unescaped. When retrieving this - * value, the resulting string is unescaped. - * - *

The path portion can be in one of two formats. 1) an absolute - * path, or 2) a relative path. An absolute path starts with a - * slash ('/'), a relative path does not.

- * - *

An absolute path may look like:

- * /full/path/to/my/file.htm - * - *

A relative path may look like:

- * - * path/to/my/file.htm - * ../images/logo.gif - * ../../reports/index.htm - * - * - *

Paths can be absolute or relative. Note that this not the same as - * an absolute or relative URI. An absolute URI can only have absolute - * paths. For example:

- * - * http:/site.com/path/to/file.htm - * - *

This absolute URI has an absolute path of "/path/to/file.htm".

- * - *

Relative URI's can have either absolute paths or relative paths. - * All of the following relative URI's are valid:

- * - * - * /absolute/path/to/file.htm - * path/to/file.htm - * ../path/to/file.htm - * - */ - public function get path() : String - { - return URI.unescapeChars(_path); - } - public function set path(pathStr:String) : void - { - this._path = URI.fastEscapeChars(pathStr, URI.URIpathExcludedBitmap); - - if (this._scheme == UNKNOWN_SCHEME) - { - // We set the path. This is a valid URI now. - this._scheme = ""; - } - - // Only hierarchical URI's can have a path. - hierState = true; - } - - - /** - * The query (CGI) portion of the URI. This part is valid for - * both hierarchical and non-hierarchical URI's. - * - *

This accessor should only be used if a custom query syntax - * is used. This URI class supports the common "param=value" - * style query syntax via the get/setQueryValue() and - * get/setQueryByMap() functions. Those functions should be used - * instead if the common syntax is being used. - * - *

The URI RFC does not specify any particular - * syntax for the query part of a URI. It is intended to allow - * any format that can be agreed upon by the two communicating hosts. - * However, most systems have standardized on the typical CGI - * format:

- * - * http://site.com/script.php?param1=value1¶m2=value2 - * - *

This class has specific support for this query syntax

- * - *

This common query format is an array of name/value - * pairs with its own syntax that is different from the overall URI - * syntax. The query has its own escaping logic. For a query part - * to be properly escaped and unescaped, it must be split into its - * component parts. This accessor escapes/unescapes the entire query - * part without regard for it's component parts. This has the - * possibliity of leaving the query string in an ambiguious state in - * regards to its syntax. If the contents of the query part are - * important, it is recommended that get/setQueryValue() or - * get/setQueryByMap() are used instead.

- * - * If a different query syntax is being used, a subclass of URI - * can be created to handle that specific syntax. - * - * @see URI.getQueryValue, URI.getQueryByMap - */ - public function get query() : String - { - return URI.unescapeChars(_query); - } - public function set query(queryStr:String) : void - { - _query = URI.fastEscapeChars(queryStr, URI.URIqueryExcludedBitmap); - - // both hierarchical and non-hierarchical URI's can - // have a query. Do not set the hierState. - } - - /** - * Accessor to the raw query data. If you are using a custom query - * syntax, this accessor can be used to get and set the query part - * directly with no escaping/unescaping. This should ONLY be used - * if your application logic is handling custom query logic and - * handling the proper escaping of the query part. - */ - public function get queryRaw() : String - { - return _query; - } - public function set queryRaw(queryStr:String) : void - { - _query = queryStr; - } - - - /** - * The fragment (anchor) portion of the URI. This is valid for - * both hierarchical and non-hierarchical URI's. - */ - public function get fragment() : String - { - return URI.unescapeChars(_fragment); - } - public function set fragment(fragmentStr:String) : void - { - _fragment = URI.fastEscapeChars(fragmentStr, URIfragmentExcludedBitmap); - - // both hierarchical and non-hierarchical URI's can - // have a fragment. Do not set the hierState. - } - - - /** - * The non-hierarchical part of the URI. For example, if - * this URI object represents "mailto:somebody@company.com", - * this will contain "somebody@company.com". This is valid only - * for non-hierarchical URI's. - */ - public function get nonHierarchical() : String - { - return URI.unescapeChars(_nonHierarchical); - } - public function set nonHierarchical(nonHier:String) : void - { - _nonHierarchical = URI.fastEscapeChars(nonHier, URInonHierexcludedBitmap); - - // This is a non-hierarchical URI. - this.hierState = false; - } - - - /** - * Quick shorthand accessor to set the parts of this URI. - * The given parts are assumed to be in unescaped form. If - * the URI is non-hierarchical (e.g. mailto:) you will need - * to call SetScheme() and SetNonHierarchical(). - */ - public function setParts(schemeStr:String, authorityStr:String, - portStr:String, pathStr:String, queryStr:String, - fragmentStr:String) : void - { - this.scheme = schemeStr; - this.authority = authorityStr; - this.port = portStr; - this.path = pathStr; - this.query = queryStr; - this.fragment = fragmentStr; - - hierState = true; - } - - - /** - * URI escapes the given character string. This is similar in function - * to the global encodeURIComponent() function in ActionScript, but is - * slightly different in regards to which characters get escaped. This - * escapes the characters specified in the URIbaselineExluded set (see class - * static members). This is needed for this class to work properly. - * - *

If a different set of characters need to be used for the escaping, - * you may use fastEscapeChars() and specify a custom URIEncodingBitmap - * that contains the characters your application needs escaped.

- * - *

Never pass a full URI to this function. A URI can only be properly - * escaped/unescaped when split into its component parts (see RFC 3986 - * section 2.4). This is due to the fact that the URI component separators - * could be characters that would normally need to be escaped.

- * - * @param unescaped character string to be escaped. - * - * @return escaped character string - * - * @see encodeURIComponent - * @see fastEscapeChars - */ - static public function escapeChars(unescaped:String) : String - { - // This uses the excluded set by default. - return fastEscapeChars(unescaped, URI.URIbaselineExcludedBitmap); - } - - - /** - * Unescape any URI escaped characters in the given character - * string. - * - *

Never pass a full URI to this function. A URI can only be properly - * escaped/unescaped when split into its component parts (see RFC 3986 - * section 2.4). This is due to the fact that the URI component separators - * could be characters that would normally need to be escaped.

- * - * @param escaped the escaped string to be unescaped. - * - * @return unescaped string. - */ - static public function unescapeChars(escaped:String /*, onlyHighASCII:Boolean = false*/) : String - { - // We can just use the default AS function. It seems to - // decode everything correctly - var unescaped:String; - unescaped = decodeURIComponent(escaped); - return unescaped; - } - - /** - * Performance focused function that escapes the given character - * string using the given URIEncodingBitmap as the rule for what - * characters need to be escaped. This function is used by this - * class and can be used externally to this class to perform - * escaping on custom character sets. - * - *

Never pass a full URI to this function. A URI can only be properly - * escaped/unescaped when split into its component parts (see RFC 3986 - * section 2.4). This is due to the fact that the URI component separators - * could be characters that would normally need to be escaped.

- * - * @param unescaped the unescaped string to be escaped - * @param bitmap the set of characters that need to be escaped - * - * @return the escaped string. - */ - static public function fastEscapeChars(unescaped:String, bitmap:URIEncodingBitmap) : String - { - var escaped:String = ""; - var c:String; - var x:int, i:int; - - for (i = 0; i < unescaped.length; i++) - { - c = unescaped.charAt(i); - - x = bitmap.ShouldEscape(c); - if (x) - { - c = x.toString(16); - if (c.length == 1) - c = "0" + c; - - c = "%" + c; - c = c.toUpperCase(); - } - - escaped += c; - } - - return escaped; - } - - - /** - * Is this URI of a particular scheme type? For example, - * passing "http" to a URI object that represents the URI - * "http://site.com/" would return true. - * - * @param scheme scheme to check for - * - * @return true if this URI object is of the given type, false - * otherwise. - */ - public function isOfType(scheme:String) : Boolean - { - // Schemes are never case sensitive. Ignore case. - scheme = scheme.toLowerCase(); - return (this._scheme == scheme); - } - - - /** - * Get the value for the specified named in the query part. This - * assumes the query part of the URI is in the common - * "name1=value1&name2=value2" syntax. Do not call this function - * if you are using a custom query syntax. - * - * @param name name of the query value to get. - * - * @return the value of the query name, empty string if the - * query name does not exist. - */ - public function getQueryValue(name:String) : String - { - var map:Object; - var item:String; - var value:String; - - map = getQueryByMap(); - - for (item in map) - { - if (item == name) - { - value = map[item]; - return value; - } - } - - // Didn't find the specified key - return new String(""); - } - - - /** - * Set the given value on the given query name. If the given name - * does not exist, it will automatically add this name/value pair - * to the query. If null is passed as the value, it will remove - * the given item from the query. - * - *

This automatically escapes any characters that may conflict with - * the query syntax so that they are "safe" within the query. The - * strings passed are assumed to be literal unescaped name and value.

- * - * @param name name of the query value to set - * @param value value of the query item to set. If null, this will - * force the removal of this item from the query. - */ - public function setQueryValue(name:String, value:String) : void - { - var map:Object; - - map = getQueryByMap(); - - // If the key doesn't exist yet, this will create a new pair in - // the map. If it does exist, this will overwrite the previous - // value, which is what we want. - map[name] = value; - - setQueryByMap(map); - } - - - /** - * Get the query of the URI in an Object class that allows for easy - * access to the query data via Object accessors. For example: - * - * - * var query:Object = uri.getQueryByMap(); - * var value:String = query["param"]; // get a value - * query["param2"] = "foo"; // set a new value - * - * - * @return Object that contains the name/value pairs of the query. - * - * @see #setQueryByMap - * @see #getQueryValue - * @see #setQueryValue - */ - public function getQueryByMap() : Object - { - var queryStr:String; - var pair:String; - var pairs:Array; - var item:Array; - var name:String, value:String; - var index:int; - var map:Object = new Object(); - - - // We need the raw query string, no unescaping. - queryStr = this._query; - - pairs = queryStr.split('&'); - for each (pair in pairs) - { - if (pair.length == 0) - continue; - - item = pair.split('='); - - if (item.length > 0) - name = item[0]; - else - continue; // empty array - - if (item.length > 1) - value = item[1]; - else - value = ""; - - name = queryPartUnescape(name); - value = queryPartUnescape(value); - - map[name] = value; - } - - return map; - } - - - /** - * Set the query part of this URI using the given object as the - * content source. Any member of the object that has a value of - * null will not be in the resulting query. - * - * @param map object that contains the name/value pairs as - * members of that object. - * - * @see #getQueryByMap - * @see #getQueryValue - * @see #setQueryValue - */ - public function setQueryByMap(map:Object) : void - { - var item:String; - var name:String, value:String; - var queryStr:String = ""; - var tmpPair:String; - var foo:String; - - for (item in map) - { - name = item; - value = map[item]; - - if (value == null) - value = ""; - - // Need to escape the name/value pair so that they - // don't conflict with the query syntax (specifically - // '=', '&', and ). - name = queryPartEscape(name); - value = queryPartEscape(value); - - tmpPair = name; - - if (value.length > 0) - { - tmpPair += "="; - tmpPair += value; - } - - if (queryStr.length != 0) - queryStr += '&'; // Add the separator - - queryStr += tmpPair; - } - - // We don't want to escape. We already escaped the - // individual name/value pairs. If we escaped the - // query string again by assigning it to "query", - // we would have double escaping. - _query = queryStr; - } - - - /** - * Similar to Escape(), except this also escapes characters that - * would conflict with the name/value pair query syntax. This is - * intended to be called on each individual "name" and "value" - * in the query making sure that nothing in the name or value - * strings contain characters that would conflict with the query - * syntax (e.g. '=' and '&'). - * - * @param unescaped unescaped string that is to be escaped. - * - * @return escaped string. - * - * @see #queryUnescape - */ - static public function queryPartEscape(unescaped:String) : String - { - var escaped:String = unescaped; - escaped = URI.fastEscapeChars(unescaped, URI.URIqueryPartExcludedBitmap); - return escaped; - } - - - /** - * Unescape the individual name/value string pairs. - * - * @param escaped escaped string to be unescaped - * - * @return unescaped string - * - * @see #queryEscape - */ - static public function queryPartUnescape(escaped:String) : String - { - var unescaped:String = escaped; - unescaped = unescapeChars(unescaped); - return unescaped; - } - - /** - * Output this URI as a string. The resulting string is properly - * escaped and well formed for machine processing. - */ - public function toString() : String - { - if (this == null) - return ""; - else - return toStringInternal(false); - } - - /** - * Output the URI as a string that is easily readable by a human. - * This outputs the URI with all escape sequences unescaped to - * their character representation. This makes the URI easier for - * a human to read, but the URI could be completely invalid - * because some unescaped characters may now cause ambiguous parsing. - * This function should only be used if you want to display a URI to - * a user. This function should never be used outside that specific - * case. - * - * @return the URI in string format with all escape sequences - * unescaped. - * - * @see #toString - */ - public function toDisplayString() : String - { - return toStringInternal(true); - } - - - /** - * @private - * - * The guts of toString() - */ - protected function toStringInternal(forDisplay:Boolean) : String - { - var uri:String = ""; - var part:String = ""; - - if (isHierarchical() == false) - { - // non-hierarchical URI - - uri += (forDisplay ? this.scheme : _scheme); - uri += ":"; - uri += (forDisplay ? this.nonHierarchical : _nonHierarchical); - } - else - { - // Hierarchical URI - - if (isRelative() == false) - { - // If it is not a relative URI, then we want the scheme and - // authority parts in the string. If it is relative, we - // do NOT want this stuff. - - if (_scheme.length != 0) - { - part = (forDisplay ? this.scheme : _scheme); - uri += part + ":"; - } - - if (_authority.length != 0 || isOfType("file")) - { - uri += "//"; - - // Add on any username/password associated with this - // authority - if (_username.length != 0) - { - part = (forDisplay ? this.username : _username); - uri += part; - - if (_password.length != 0) - { - part = (forDisplay ? this.password : _password); - uri += ":" + part; - } - - uri += "@"; - } - - // add the authority - part = (forDisplay ? this.authority : _authority); - uri += part; - - // Tack on the port number, if any - if (port.length != 0) - uri += ":" + port; - } - } - - // Tack on the path - part = (forDisplay ? this.path : _path); - uri += part; - - } // end hierarchical part - - // Both non-hier and hierarchical have query and fragment parts - - // Add on the query and fragment parts - if (_query.length != 0) - { - part = (forDisplay ? this.query : _query); - uri += "?" + part; - } - - if (fragment.length != 0) - { - part = (forDisplay ? this.fragment : _fragment); - uri += "#" + part; - } - - return uri; - } - - /** - * Forcefully ensure that this URI is properly escaped. - * - *

Sometimes URI's are constructed by hand using strings outside - * this class. In those cases, it is unlikely the URI has been - * properly escaped. This function forcefully escapes this URI - * by unescaping each part and then re-escaping it. If the URI - * did not have any escaping, the first unescape will do nothing - * and then the re-escape will properly escape everything. If - * the URI was already escaped, the unescape and re-escape will - * essentally be a no-op. This provides a safe way to make sure - * a URI is in the proper escaped form.

- */ - public function forceEscape() : void - { - // The accessors for each of the members will unescape - // and then re-escape as we get and assign them. - - // Handle the parts that are common for both hierarchical - // and non-hierarchical URI's - this.scheme = this.scheme; - this.setQueryByMap(this.getQueryByMap()); - this.fragment = this.fragment; - - if (isHierarchical()) - { - this.authority = this.authority; - this.path = this.path; - this.port = this.port; - this.username = this.username; - this.password = this.password; - } - else - { - this.nonHierarchical = this.nonHierarchical; - } - } - - - /** - * Does this URI point to a resource of the given file type? - * Given a file extension (or just a file name, this will strip the - * extension), check to see if this URI points to a file of that - * type. - * - * @param extension string that contains a file extension with or - * without a dot ("html" and ".html" are both valid), or a file - * name with an extension (e.g. "index.html"). - * - * @return true if this URI points to a resource with the same file - * file extension as the extension provided, false otherwise. - */ - public function isOfFileType(extension:String) : Boolean - { - var thisExtension:String; - var index:int; - - index = extension.lastIndexOf("."); - if (index != -1) - { - // Strip the extension - extension = extension.substr(index + 1); - } - else - { - // The caller passed something without a dot in it. We - // will assume that it is just a plain extension (e.g. "html"). - // What they passed is exactly what we want - } - - thisExtension = getExtension(true); - - if (thisExtension == "") - return false; - - // Compare the extensions ignoring case - if (compareStr(thisExtension, extension, false) == 0) - return true; - else - return false; - } - - - /** - * Get the ".xyz" file extension from the filename in the URI. - * For example, if we have the following URI: - * - * http://something.com/path/to/my/page.html?form=yes&name=bob#anchor - * - *

This will return ".html".

- * - * @param minusDot If true, this will strip the dot from the extension. - * If true, the above example would have returned "html". - * - * @return the file extension - */ - public function getExtension(minusDot:Boolean = false) : String - { - var filename:String = getFilename(); - var extension:String; - var index:int; - - if (filename == "") - return String(""); - - index = filename.lastIndexOf("."); - - // If it doesn't have an extension, or if it is a "hidden" file, - // it doesn't have an extension. Hidden files on unix start with - // a dot (e.g. ".login"). - if (index == -1 || index == 0) - return String(""); - - extension = filename.substr(index); - - // If the caller does not want the dot, remove it. - if (minusDot && extension.charAt(0) == ".") - extension = extension.substr(1); - - return extension; - } - - /** - * Quick function to retrieve the file name off the end of a URI. - * - *

For example, if the URI is:

- * http://something.com/some/path/to/my/file.html - *

this function will return "file.html".

- * - * @param minusExtension true if the file extension should be stripped - * - * @return the file name. If this URI is a directory, the return - * value will be empty string. - */ - public function getFilename(minusExtension:Boolean = false) : String - { - if (isDirectory()) - return String(""); - - var pathStr:String = this.path; - var filename:String; - var index:int; - - // Find the last path separator. - index = pathStr.lastIndexOf("/"); - - if (index != -1) - filename = pathStr.substr(index + 1); - else - filename = pathStr; - - if (minusExtension) - { - // The caller has requested that the extension be removed - index = filename.lastIndexOf("."); - - if (index != -1) - filename = filename.substr(0, index); - } - - return filename; - } - - - /** - * @private - * Helper function to compare strings. - * - * @return true if the two strings are identical, false otherwise. - */ - static protected function compareStr(str1:String, str2:String, - sensitive:Boolean = true) : Boolean - { - if (sensitive == false) - { - str1 = str1.toLowerCase(); - str2 = str2.toLowerCase(); - } - - return (str1 == str2) - } - - /** - * Based on the type of this URI (http, ftp, etc.) get - * the default port used for that protocol. This is - * just intended to be a helper function for the most - * common cases. - */ - public function getDefaultPort() : String - { - if (_scheme == "http") - return String("80"); - else if (_scheme == "ftp") - return String("21"); - else if (_scheme == "file") - return String(""); - else if (_scheme == "sftp") - return String("22"); // ssh standard port - else - { - // Don't know the port for this URI type - return String(""); - } - } - - /** - * @private - * - * This resolves the given URI if the application has a - * resolver interface defined. This function does not - * modify the passed in URI and returns a new URI. - */ - static protected function resolve(uri:URI) : URI - { - var copy:URI = new URI(); - copy.copyURI(uri); - - if (_resolver != null) - { - // A resolver class has been registered. Call it. - return _resolver.resolve(copy); - } - else - { - // No resolver. Nothing to do, but we don't - // want to reuse the one passed in. - return copy; - } - } - - /** - * Accessor to set and get the resolver object used by all URI - * objects to dynamically resolve URI's before comparison. - */ - static public function set resolver(resolver:IURIResolver) : void - { - _resolver = resolver; - } - static public function get resolver() : IURIResolver - { - return _resolver; - } - - /** - * Given another URI, return this URI object's relation to the one given. - * URI's can have 1 of 4 possible relationships. They can be unrelated, - * equal, parent, or a child of the given URI. - * - * @param uri URI to compare this URI object to. - * @param caseSensitive true if the URI comparison should be done - * taking case into account, false if the comparison should be - * performed case insensitive. - * - * @return URI.NOT_RELATED, URI.CHILD, URI.PARENT, or URI.EQUAL - */ - public function getRelation(uri:URI, caseSensitive:Boolean = true) : int - { - // Give the app a chance to resolve these URI's before we compare them. - var thisURI:URI = URI.resolve(this); - var thatURI:URI = URI.resolve(uri); - - if (thisURI.isRelative() || thatURI.isRelative()) - { - // You cannot compare relative URI's due to their lack of context. - // You could have two relative URI's that look like: - // ../../images/ - // ../../images/marketing/logo.gif - // These may appear related, but you have no overall context - // from which to make the comparison. The first URI could be - // from one site and the other URI could be from another site. - return URI.NOT_RELATED; - } - else if (thisURI.isHierarchical() == false || thatURI.isHierarchical() == false) - { - // One or both of the URI's are non-hierarchical. - if (((thisURI.isHierarchical() == false) && (thatURI.isHierarchical() == true)) || - ((thisURI.isHierarchical() == true) && (thatURI.isHierarchical() == false))) - { - // XOR. One is hierarchical and the other is - // non-hierarchical. They cannot be compared. - return URI.NOT_RELATED; - } - else - { - // They are both non-hierarchical - if (thisURI.scheme != thatURI.scheme) - return URI.NOT_RELATED; - - if (thisURI.nonHierarchical != thatURI.nonHierarchical) - return URI.NOT_RELATED; - - // The two non-hierarcical URI's are equal. - return URI.EQUAL; - } - } - - // Ok, this URI and the one we are being compared to are both - // absolute hierarchical URI's. - - if (thisURI.scheme != thatURI.scheme) - return URI.NOT_RELATED; - - if (thisURI.authority != thatURI.authority) - return URI.NOT_RELATED; - - var thisPort:String = thisURI.port; - var thatPort:String = thatURI.port; - - // Different ports are considered completely different servers. - if (thisPort == "") - thisPort = thisURI.getDefaultPort(); - if (thatPort == "") - thatPort = thatURI.getDefaultPort(); - - // Check to see if the port is the default port. - if (thisPort != thatPort) - return URI.NOT_RELATED; - - if (compareStr(thisURI.path, thatURI.path, caseSensitive)) - return URI.EQUAL; - - // Special case check. If we are here, the scheme, authority, - // and port match, and it is not a relative path, but the - // paths did not match. There is a special case where we - // could have: - // http://something.com/ - // http://something.com - // Technically, these are equal. So lets, check for this case. - var thisPath:String = thisURI.path; - var thatPath:String = thatURI.path; - - if ( (thisPath == "/" || thatPath == "/") && - (thisPath == "" || thatPath == "") ) - { - // We hit the special case. These two are equal. - return URI.EQUAL; - } - - // Ok, the paths do not match, but one path may be a parent/child - // of the other. For example, we may have: - // http://something.com/path/to/homepage/ - // http://something.com/path/to/homepage/images/logo.gif - // In this case, the first is a parent of the second (or the second - // is a child of the first, depending on which you compare to the - // other). To make this comparison, we must split the path into - // its component parts (split the string on the '/' path delimiter). - // We then compare the - var thisParts:Array, thatParts:Array; - var thisPart:String, thatPart:String; - var i:int; - - thisParts = thisPath.split("/"); - thatParts = thatPath.split("/"); - - if (thisParts.length > thatParts.length) - { - thatPart = thatParts[thatParts.length - 1]; - if (thatPart.length > 0) - { - // if the last part is not empty, the passed URI is - // not a directory. There is no way the passed URI - // can be a parent. - return URI.NOT_RELATED; - } - else - { - // Remove the empty trailing part - thatParts.pop(); - } - - // This may be a child of the one passed in - for (i = 0; i < thatParts.length; i++) - { - thisPart = thisParts[i]; - thatPart = thatParts[i]; - - if (compareStr(thisPart, thatPart, caseSensitive) == false) - return URI.NOT_RELATED; - } - - return URI.CHILD; - } - else if (thisParts.length < thatParts.length) - { - thisPart = thisParts[thisParts.length - 1]; - if (thisPart.length > 0) - { - // if the last part is not empty, this URI is not a - // directory. There is no way this object can be - // a parent. - return URI.NOT_RELATED; - } - else - { - // Remove the empty trailing part - thisParts.pop(); - } - - // This may be the parent of the one passed in - for (i = 0; i < thisParts.length; i++) - { - thisPart = thisParts[i]; - thatPart = thatParts[i]; - - if (compareStr(thisPart, thatPart, caseSensitive) == false) - return URI.NOT_RELATED; - } - - return URI.PARENT; - } - else - { - // Both URI's have the same number of path components, but - // it failed the equivelence check above. This means that - // the two URI's are not related. - return URI.NOT_RELATED; - } - - // If we got here, the scheme and authority are the same, - // but the paths pointed to two different locations that - // were in different parts of the file system tree - return URI.NOT_RELATED; - } - - /** - * Given another URI, return the common parent between this one - * and the provided URI. - * - * @param uri the other URI from which to find a common parent - * @para caseSensitive true if this operation should be done - * with case sensitive comparisons. - * - * @return the parent URI if successful, null otherwise. - */ - public function getCommonParent(uri:URI, caseSensitive:Boolean = true) : URI - { - var thisURI:URI = URI.resolve(this); - var thatURI:URI = URI.resolve(uri); - - if(!thisURI.isAbsolute() || !thatURI.isAbsolute() || - thisURI.isHierarchical() == false || - thatURI.isHierarchical() == false) - { - // Both URI's must be absolute hierarchical for this to - // make sense. - return null; - } - - var relation:int = thisURI.getRelation(thatURI); - if (relation == URI.NOT_RELATED) - { - // The given URI is not related to this one. No - // common parent. - return null; - } - - thisURI.chdir("."); - thatURI.chdir("."); - - var strBefore:String, strAfter:String; - do - { - relation = thisURI.getRelation(thatURI, caseSensitive); - if(relation == URI.EQUAL || relation == URI.PARENT) - break; - - // If strBefore and strAfter end up being the same, - // we know we are at the root of the path because - // chdir("..") is doing nothing. - strBefore = thisURI.toString(); - thisURI.chdir(".."); - strAfter = thisURI.toString(); - } - while(strBefore != strAfter); - - return thisURI; - } - - - /** - * This function is used to move around in a URI in a way similar - * to the 'cd' or 'chdir' commands on Unix. These operations are - * completely string based, using the context of the URI to - * determine the position within the path. The heuristics used - * to determine the action are based off Appendix C in RFC 2396. - * - *

URI paths that end in '/' are considered paths that point to - * directories, while paths that do not end in '/' are files. For - * example, if you execute chdir("d") on the following URI's:
- * 1. http://something.com/a/b/c/ (directory)
- * 2. http://something.com/a/b/c (not directory)
- * you will get:
- * 1. http://something.com/a/b/c/d
- * 2. http://something.com/a/b/d

- * - *

See RFC 2396, Appendix C for more info.

- * - * @param reference the URI or path to "cd" to. - * @param escape true if the passed reference string should be URI - * escaped before using it. - * - * @return true if the chdir was successful, false otherwise. - */ - public function chdir(reference:String, escape:Boolean = false) : Boolean - { - var uriReference:URI; - var ref:String = reference; - - if (escape) - ref = URI.escapeChars(reference); - - if (ref == "") - { - // NOOP - return true; - } - else if (ref.substr(0, 2) == "//") - { - // Special case. This is an absolute URI but without the scheme. - // Take the scheme from this URI and tack it on. This is - // intended to make working with chdir() a little more - // tolerant. - var final:String = this.scheme + ":" + ref; - - return constructURI(final); - } - else if (ref.charAt(0) == "?") - { - // A relative URI that is just a query part is essentially - // a "./?query". We tack on the "./" here to make the rest - // of our logic work. - ref = "./" + ref; - } - - // Parse the reference passed in as a URI. This way we - // get any query and fragments parsed out as well. - uriReference = new URI(ref); - - if (uriReference.isAbsolute() || - uriReference.isHierarchical() == false) - { - // If the URI given is a full URI, it replaces this one. - copyURI(uriReference); - return true; - } - - - var thisPath:String, thatPath:String; - var thisParts:Array, thatParts:Array; - var thisIsDir:Boolean = false, thatIsDir:Boolean = false; - var thisIsAbs:Boolean = false, thatIsAbs:Boolean = false; - var lastIsDotOperation:Boolean = false; - var curDir:String; - var i:int; - - thisPath = this.path; - thatPath = uriReference.path; - - if (thisPath.length > 0) - thisParts = thisPath.split("/"); - else - thisParts = new Array(); - - if (thatPath.length > 0) - thatParts = thatPath.split("/"); - else - thatParts = new Array(); - - if (thisParts.length > 0 && thisParts[0] == "") - { - thisIsAbs = true; - thisParts.shift(); // pop the first one off the array - } - if (thisParts.length > 0 && thisParts[thisParts.length - 1] == "") - { - thisIsDir = true; - thisParts.pop(); // pop the last one off the array - } - - if (thatParts.length > 0 && thatParts[0] == "") - { - thatIsAbs = true; - thatParts.shift(); // pop the first one off the array - } - if (thatParts.length > 0 && thatParts[thatParts.length - 1] == "") - { - thatIsDir = true; - thatParts.pop(); // pop the last one off the array - } - - if (thatIsAbs) - { - // The reference is an absolute path (starts with a slash). - // It replaces this path wholesale. - this.path = uriReference.path; - - // And it inherits the query and fragment - this.queryRaw = uriReference.queryRaw; - this.fragment = uriReference.fragment; - - return true; - } - else if (thatParts.length == 0 && uriReference.query == "") - { - // The reference must have only been a fragment. Fragments just - // get appended to whatever the current path is. We don't want - // to overwrite any query that may already exist, so this case - // only takes on the new fragment. - this.fragment = uriReference.fragment; - return true; - } - else if (thisIsDir == false && thisParts.length > 0) - { - // This path ends in a file. It goes away no matter what. - thisParts.pop(); - } - - // By default, this assumes the query and fragment of the reference - this.queryRaw = uriReference.queryRaw; - this.fragment = uriReference.fragment; - - // Append the parts of the path from the passed in reference - // to this object's path. - thisParts = thisParts.concat(thatParts); - - for(i = 0; i < thisParts.length; i++) - { - curDir = thisParts[i]; - lastIsDotOperation = false; - - if (curDir == ".") - { - thisParts.splice(i, 1); - i = i - 1; // account for removing this item - lastIsDotOperation = true; - } - else if (curDir == "..") - { - if (i >= 1) - { - if (thisParts[i - 1] == "..") - { - // If the previous is a "..", we must have skipped - // it due to this URI being relative. We can't - // collapse leading ".."s in a relative URI, so - // do nothing. - } - else - { - thisParts.splice(i - 1, 2); - i = i - 2; // move back to account for the 2 we removed - } - } - else - { - // This is the first thing in the path. - - if (isRelative()) - { - // We can't collapse leading ".."s in a relative - // path. Do noting. - } - else - { - // This is an abnormal case. We have dot-dotted up - // past the base of our "file system". This is a - // case where we had a /path/like/this.htm and were - // given a path to chdir to like this: - // ../../../../../../mydir - // Obviously, it has too many ".." and will take us - // up beyond the top of the URI. However, according - // RFC 2396 Appendix C.2, we should try to handle - // these abnormal cases appropriately. In this case, - // we will do what UNIX command lines do if you are - // at the root (/) of the filesystem and execute: - // # cd ../../../../../bin - // Which will put you in /bin. Essentially, the extra - // ".."'s will just get eaten. - - thisParts.splice(i, 1); - i = i - 1; // account for the ".." we just removed - } - } - - lastIsDotOperation = true; - } - } - - var finalPath:String = ""; - - // If the last thing in the path was a "." or "..", then this thing is a - // directory. If the last thing isn't a dot-op, then we don't want to - // blow away any information about the directory (hence the "|=" binary - // assignment). - thatIsDir = thatIsDir || lastIsDotOperation; - - // Reconstruct the path with the abs/dir info we have - finalPath = joinPath(thisParts, thisIsAbs, thatIsDir); - - // Set the path (automatically escaping it) - this.path = finalPath; - - return true; - } - - /** - * @private - * Join an array of path parts back into a URI style path string. - * This is used by the various path logic functions to recombine - * a path. This is different than the standard Array.join() - * function because we need to take into account the starting and - * ending path delimiters if this is an absolute path or a - * directory. - * - * @param parts the Array that contains strings of each path part. - * @param isAbs true if the given path is absolute - * @param isDir true if the given path is a directory - * - * @return the combined path string. - */ - protected function joinPath(parts:Array, isAbs:Boolean, isDir:Boolean) : String - { - var pathStr:String = ""; - var i:int; - - for (i = 0; i < parts.length; i++) - { - if (pathStr.length > 0) - pathStr += "/"; - - pathStr += parts[i]; - } - - // If this path is a directory, tack on the directory delimiter, - // but only if the path contains something. Adding this to an - // empty path would make it "/", which is an absolute path that - // starts at the root. - if (isDir && pathStr.length > 0) - pathStr += "/"; - - if (isAbs) - pathStr = "/" + pathStr; - - return pathStr; - } - - /** - * Given an absolute URI, make this relative URI absolute using - * the given URI as a base. This URI instance must be relative - * and the base_uri must be absolute. - * - * @param base_uri URI to use as the base from which to make - * this relative URI into an absolute URI. - * - * @return true if successful, false otherwise. - */ - public function makeAbsoluteURI(base_uri:URI) : Boolean - { - if (isAbsolute() || base_uri.isRelative()) - { - // This URI needs to be relative, and the base needs to be - // absolute otherwise we won't know what to do! - return false; - } - - // Make a copy of the base URI. We don't want to modify - // the passed URI. - var base:URI = new URI(); - base.copyURI(base_uri); - - // ChDir on the base URI. This will preserve any query - // and fragment we have. - if (base.chdir(toString()) == false) - return false; - - // It worked, so copy the base into this one - copyURI(base); - - return true; - } - - - /** - * Given a URI to use as a base from which this object should be - * relative to, convert this object into a relative URI. For example, - * if you have: - * - * - * var uri1:URI = new URI("http://something.com/path/to/some/file.html"); - * var uri2:URI = new URI("http://something.com/path/to/another/file.html"); - * - * uri1.MakeRelativePath(uri2); - * - *

uri1 will have a final value of "../some/file.html"

- * - *

Note! This function is brute force. If you have two URI's - * that are completely unrelated, this will still attempt to make - * the relative URI. In that case, you will most likely get a - * relative path that looks something like:

- * - *

../../../../../../some/path/to/my/file.html

- * - * @param base_uri the URI from which to make this URI relative - * - * @return true if successful, false if the base_uri and this URI - * are not related, of if error. - */ - public function makeRelativeURI(base_uri:URI, caseSensitive:Boolean = true) : Boolean - { - var base:URI = new URI(); - base.copyURI(base_uri); - - var thisParts:Array, thatParts:Array; - var finalParts:Array = new Array(); - var thisPart:String, thatPart:String, finalPath:String; - var pathStr:String = this.path; - var queryStr:String = this.queryRaw; - var fragmentStr:String = this.fragment; - var i:int; - var diff:Boolean = false; - var isDir:Boolean = false; - - if (isRelative()) - { - // We're already relative. - return true; - } - - if (base.isRelative()) - { - // The base is relative. A relative base doesn't make sense. - return false; - } - - - if ( (isOfType(base_uri.scheme) == false) || - (this.authority != base_uri.authority) ) - { - // The schemes and/or authorities are different. We can't - // make a relative path to something that is completely - // unrelated. - return false; - } - - // Record the state of this URI - isDir = isDirectory(); - - // We are based of the directory of the given URI. We need to - // make sure the URI is pointing to a directory. Changing - // directory to "." will remove any file name if the base is - // not a directory. - base.chdir("."); - - thisParts = pathStr.split("/"); - thatParts = base.path.split("/"); - - if (thisParts.length > 0 && thisParts[0] == "") - thisParts.shift(); - - if (thisParts.length > 0 && thisParts[thisParts.length - 1] == "") - { - isDir = true; - thisParts.pop(); - } - - if (thatParts.length > 0 && thatParts[0] == "") - thatParts.shift(); - if (thatParts.length > 0 && thatParts[thatParts.length - 1] == "") - thatParts.pop(); - - - // Now that we have the paths split into an array of directories, - // we can compare the two paths. We start from the left of side - // of the path and start comparing. When we either run out of - // directories (one path is longer than the other), or we find - // a directory that is different, we stop. The remaining parts - // of each path is then used to determine the relative path. For - // example, lets say we have: - // path we want to make relative: /a/b/c/d/e.txt - // path to use as base for relative: /a/b/f/ - // - // This loop will start at the left, and remove directories - // until we get a mismatch or run off the end of one of them. - // In this example, the result will be: - // c/d/e.txt - // f - // - // For every part left over in the base path, we prepend a ".." - // to the relative to get the final path: - // ../c/d/e.txt - while(thatParts.length > 0) - { - if (thisParts.length == 0) - { - // we matched all there is to match, we are done. - // This is the case where "this" object is a parent - // path of the given URI. eg: - // this.path = /a/b/ (thisParts) - // base.path = /a/b/c/d/e/ (thatParts) - break; - } - - thisPart = thisParts[0]; - thatPart = thatParts[0]; - - if (compareStr(thisPart, thatPart, caseSensitive)) - { - thisParts.shift(); - thatParts.shift(); - } - else - break; - } - - // If there are any path info left from the base URI, that means - // **this** object is above the given URI in the file tree. For - // each part left over in the given URI, we need to move up one - // directory to get where we are. - var dotdot:String = ".."; - for (i = 0; i < thatParts.length; i++) - { - finalParts.push(dotdot); - } - - // Append the parts of this URI to any dot-dot's we have - finalParts = finalParts.concat(thisParts); - - // Join the parts back into a path - finalPath = joinPath(finalParts, false /* not absolute */, isDir); - - if (finalPath.length == 0) - { - // The two URI's are exactly the same. The proper relative - // path is: - finalPath = "./"; - } - - // Set the parts of the URI, preserving the original query and - // fragment parts. - setParts("", "", "", finalPath, queryStr, fragmentStr); - - return true; - } - - /** - * Given a string, convert it to a URI. The string could be a - * full URI that is improperly escaped, a malformed URI (e.g. - * missing a protocol like "www.something.com"), a relative URI, - * or any variation there of. - * - *

The intention of this function is to take anything that a - * user might manually enter as a URI/URL and try to determine what - * they mean. This function differs from the URI constructor in - * that it makes some assumptions to make it easy to import user - * entered URI data.

- * - *

This function is intended to be a helper function. - * It is not all-knowning and will probably make mistakes - * when attempting to parse a string of unknown origin. If - * your applicaiton is receiving input from the user, your - * application should already have a good idea what the user - * should be entering, and your application should be - * pre-processing the user's input to make sure it is well formed - * before passing it to this function.

- * - *

It is assumed that the string given to this function is - * something the user may have manually entered. Given this, - * the URI string is probably unescaped or improperly escaped. - * This function will attempt to properly escape the URI by - * using forceEscape(). The result is that a toString() call - * on a URI that was created from unknownToURI() may not match - * the input string due to the difference in escaping.

- * - * @param unknown a potental URI string that should be parsed - * and loaded into this object. - * @param defaultScheme if it is determined that the passed string - * looks like a URI, but it is missing the scheme part, this - * string will be used as the missing scheme. - * - * @return true if the given string was successfully parsed into - * a valid URI object, false otherwise. - */ - public function unknownToURI(unknown:String, defaultScheme:String = "http") : Boolean - { - var temp:String; - - if (unknown.length == 0) - { - this.initialize(); - return false; - } - - // Some users love the backslash key. Fix it. - unknown = unknown.replace(/\\/g, "/"); - - // Check for any obviously missing scheme. - if (unknown.length >= 2) - { - temp = unknown.substr(0, 2); - if (temp == "//") - unknown = defaultScheme + ":" + unknown; - } - - if (unknown.length >= 3) - { - temp = unknown.substr(0, 3); - if (temp == "://") - unknown = defaultScheme + unknown; - } - - // Try parsing it as a normal URI - var uri:URI = new URI(unknown); - - if (uri.isHierarchical() == false) - { - if (uri.scheme == UNKNOWN_SCHEME) - { - this.initialize(); - return false; - } - - // It's a non-hierarchical URI - copyURI(uri); - forceEscape(); - return true; - } - else if ((uri.scheme != UNKNOWN_SCHEME) && - (uri.scheme.length > 0)) - { - if ( (uri.authority.length > 0) || - (uri.scheme == "file") ) - { - // file://... URI - copyURI(uri); - forceEscape(); // ensure proper escaping - return true; - } - else if (uri.authority.length == 0 && uri.path.length == 0) - { - // It's is an incomplete URI (eg "http://") - - setParts(uri.scheme, "", "", "", "", ""); - return false; - } - } - else - { - // Possible relative URI. We can only detect relative URI's - // that start with "." or "..". If it starts with something - // else, the parsing is ambiguous. - var path:String = uri.path; - - if (path == ".." || path == "." || - (path.length >= 3 && path.substr(0, 3) == "../") || - (path.length >= 2 && path.substr(0, 2) == "./") ) - { - // This is a relative URI. - copyURI(uri); - forceEscape(); - return true; - } - } - - // Ok, it looks like we are just a normal URI missing the scheme. Tack - // on the scheme. - uri = new URI(defaultScheme + "://" + unknown); - - // Check to see if we are good now - if (uri.scheme.length > 0 && uri.authority.length > 0) - { - // It was just missing the scheme. - copyURI(uri); - forceEscape(); // Make sure we are properly encoded. - return true; - } - - // don't know what this is - this.initialize(); - return false; - } - - } // end URI class +/* +Adobe Systems Incorporated(r) Source Code License Agreement +Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. + +Please read this Source Code License Agreement carefully before using +the source code. + +Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license, to reproduce, +prepare derivative works of, publicly display, publicly perform, and +distribute this source code and such derivative works in source or +object code form without any attribution requirements. + +The name "Adobe Systems Incorporated" must not be used to endorse or promote products +derived from the source code without prior written permission. + +You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and +against any loss, damage, claims or lawsuits, including attorney's +fees that arise or result from your use or distribution of the source +code. + +THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT +ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF +NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA +OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.adobe.net +{ + import flash.utils.ByteArray; + + /** + * This class implements functions and utilities for working with URI's + * (Universal Resource Identifiers). For technical description of the + * URI syntax, please see RFC 3986 at http://www.ietf.org/rfc/rfc3986.txt + * or do a web search for "rfc 3986". + * + *

The most important aspect of URI's to understand is that URI's + * and URL's are not strings. URI's are complex data structures that + * encapsulate many pieces of information. The string version of a + * URI is the serialized representation of that data structure. This + * string serialization is used to provide a human readable + * representation and a means to transport the data over the network + * where it can then be parsed back into its' component parts.

+ * + *

URI's fall into one of three categories: + *

    + *
  • <scheme>:<scheme-specific-part>#<fragment> (non-hierarchical)
  • + *
  • <scheme>: + *
  • <path>?<query>#<fragment> (relative hierarchical)
  • + *

+ * + *

The query and fragment parts are optional.

+ * + *

This class supports both non-hierarchical and hierarchical URI's

+ * + *

This class is intended to be used "as-is" for the vast majority + * of common URI's. However, if your application requires a custom + * URI syntax (e.g. custom query syntax or special handling of + * non-hierarchical URI's), this class can be fully subclassed. If you + * intended to subclass URI, please see the source code for complete + * documation on protected members and protected fuctions.

+ * + * @langversion ActionScript 3.0 + * @playerversion Flash 9.0 + */ + public class URI + { + // Here we define which characters must be escaped for each + // URI part. The characters that must be escaped for each + // part differ depending on what would cause ambiguous parsing. + // RFC 3986 sec. 2.4 states that characters should only be + // encoded when they would conflict with subcomponent delimiters. + // We don't want to over-do the escaping. We only want to escape + // the minimum needed to prevent parsing problems. + + // space and % must be escaped in all cases. '%' is the delimiter + // for escaped characters. + public static const URImustEscape:String = " %"; + + // Baseline of what characters must be escaped + public static const URIbaselineEscape:String = URImustEscape + ":?#/@"; + + // Characters that must be escaped in the part part. + public static const URIpathEscape:String = URImustEscape + "?#"; + + // Characters that must be escaped in the query part, if setting + // the query as a whole string. If the query is set by + // name/value, URIqueryPartEscape is used instead. + public static const URIqueryEscape:String = URImustEscape + "#"; + + // This is what each name/value pair must escape "&=" as well + // so they don't conflict with the "param=value¶m2=value2" + // syntax. + public static const URIqueryPartEscape:String = URImustEscape + "#&="; + + // Non-hierarchical URI's can have query and fragment parts, but + // we also want to prevent '/' otherwise it might end up looking + // like a hierarchical URI to the parser. + public static const URInonHierEscape:String = URImustEscape + "?#/"; + + // Baseline uninitialized setting for the URI scheme. + public static const UNKNOWN_SCHEME:String = "unknown"; + + // The following bitmaps are used for performance enhanced + // character escaping. + + // Baseline characters that need to be escaped. Many parts use + // this. + protected static const URIbaselineExcludedBitmap:URIEncodingBitmap = + new URIEncodingBitmap(URIbaselineEscape); + + // Scheme escaping bitmap + protected static const URIschemeExcludedBitmap:URIEncodingBitmap = + URIbaselineExcludedBitmap; + + // User/pass escaping bitmap + protected static const URIuserpassExcludedBitmap:URIEncodingBitmap = + URIbaselineExcludedBitmap; + + // Authority escaping bitmap + protected static const URIauthorityExcludedBitmap:URIEncodingBitmap = + URIbaselineExcludedBitmap; + + // Port escaping bitmap + protected static const URIportExludedBitmap:URIEncodingBitmap = + URIbaselineExcludedBitmap; + + // Path escaping bitmap + protected static const URIpathExcludedBitmap:URIEncodingBitmap = + new URIEncodingBitmap(URIpathEscape); + + // Query (whole) escaping bitmap + protected static const URIqueryExcludedBitmap:URIEncodingBitmap = + new URIEncodingBitmap(URIqueryEscape); + + // Query (individual parts) escaping bitmap + protected static const URIqueryPartExcludedBitmap:URIEncodingBitmap = + new URIEncodingBitmap(URIqueryPartEscape); + + // Fragments are the last part in the URI. They only need to + // escape space, '#', and '%'. Turns out that is what query + // uses too. + protected static const URIfragmentExcludedBitmap:URIEncodingBitmap = + URIqueryExcludedBitmap; + + // Characters that need to be escaped in the non-hierarchical part + protected static const URInonHierexcludedBitmap:URIEncodingBitmap = + new URIEncodingBitmap(URInonHierEscape); + + // Values used by getRelation() + public static const NOT_RELATED:int = 0; + public static const CHILD:int = 1; + public static const EQUAL:int = 2; + public static const PARENT:int = 3; + + //------------------------------------------------------------------- + // protected class members + //------------------------------------------------------------------- + protected var _valid:Boolean = false; + protected var _relative:Boolean = false; + protected var _scheme:String = ""; + protected var _authority:String = ""; + protected var _username:String = ""; + protected var _password:String = ""; + protected var _port:String = ""; + protected var _path:String = ""; + protected var _query:String = ""; + protected var _fragment:String = ""; + protected var _nonHierarchical:String = ""; + protected static var _resolver:IURIResolver = null; + + + /** + * URI Constructor. If no string is given, this will initialize + * this URI object to a blank URI. + */ + public function URI(uri:String = null) : void + { + if (uri == null) + initialize(); + else + constructURI(uri); + } + + + /** + * @private + * Method that loads the URI from the given string. + */ + protected function constructURI(uri:String) : Boolean + { + if (!parseURI(uri)) + _valid = false; + + return isValid(); + } + + + /** + * @private Private initializiation. + */ + protected function initialize() : void + { + _valid = false; + _relative = false; + + _scheme = UNKNOWN_SCHEME; + _authority = ""; + _username = ""; + _password = ""; + _port = ""; + _path = ""; + _query = ""; + _fragment = ""; + + _nonHierarchical = ""; + } + + /** + * @private Accessor to explicitly set/get the hierarchical + * state of the URI. + */ + protected function set hierState(state:Boolean) : void + { + if (state) + { + // Clear the non-hierarchical data + _nonHierarchical = ""; + + // Also set the state vars while we are at it + if (_scheme == "" || _scheme == UNKNOWN_SCHEME) + _relative = true; + else + _relative = false; + + if (_authority.length == 0 && _path.length == 0) + _valid = false; + else + _valid = true; + } + else + { + // Clear the hierarchical data + _authority = ""; + _username = ""; + _password = ""; + _port = ""; + _path = ""; + + _relative = false; + + if (_scheme == "" || _scheme == UNKNOWN_SCHEME) + _valid = false; + else + _valid = true; + } + } + protected function get hierState() : Boolean + { + return (_nonHierarchical.length == 0); + } + + + /** + * @private Functions that performs some basic consistency validation. + */ + protected function validateURI() : Boolean + { + // Check the scheme + if (isAbsolute()) + { + if (_scheme.length <= 1 || _scheme == UNKNOWN_SCHEME) + { + // we probably parsed a C:\ type path or no scheme + return false; + } + else if (verifyAlpha(_scheme) == false) + return false; // Scheme contains bad characters + } + + if (hierState) + { + if (_path.search('\\') != -1) + return false; // local path + else if (isRelative() == false && _scheme == UNKNOWN_SCHEME) + return false; // It's an absolute URI, but it has a bad scheme + } + else + { + if (_nonHierarchical.search('\\') != -1) + return false; // some kind of local path + } + + // Looks like it's ok. + return true; + } + + + /** + * @private + * + * Given a URI in string format, parse that sucker into its basic + * components and assign them to this object. A URI is of the form: + * :?# + * + * For simplicity, we parse the URI in the following order: + * + * 1. Fragment (anchors) + * 2. Query (CGI stuff) + * 3. Scheme ("http") + * 4. Authority (host name) + * 5. Username/Password (if any) + * 6. Port (server port if any) + * 7. Path (/homepages/mypage.html) + * + * The reason for this order is to minimize any parsing ambiguities. + * Fragments and queries can contain almost anything (they are parts + * that can contain custom data with their own syntax). Parsing + * them out first removes a large chance of parsing errors. This + * method expects well formed URI's, but performing the parse in + * this order makes us a little more tolerant of user error. + * + * REGEXP + * Why doesn't this use regular expressions to parse the URI? We + * have found that in a real world scenario, URI's are not always + * well formed. Sometimes characters that should have been escaped + * are not, and those situations would break a regexp pattern. This + * function attempts to be smart about what it is parsing based on + * location of characters relative to eachother. This function has + * been proven through real-world use to parse the vast majority + * of URI's correctly. + * + * NOTE + * It is assumed that the string in URI form is escaped. This function + * does not escape anything. If you constructed the URI string by + * hand, and used this to parse in the URI and still need it escaped, + * call forceEscape() on your URI object. + * + * Parsing Assumptions + * This routine assumes that the URI being passed is well formed. + * Passing things like local paths, malformed URI's, and the such + * will result in parsing errors. This function can handle + * - absolute hierarchical (e.g. "http://something.com/index.html), + * - relative hierarchical (e.g. "../images/flower.gif"), or + * - non-hierarchical URIs (e.g. "mailto:jsmith@fungoo.com"). + * + * Anything else will probably result in a parsing error, or a bogus + * URI object. + * + * Note that non-hierarchical URIs *MUST* have a scheme, otherwise + * they will be mistaken for relative URI's. + * + * If you are not sure what is being passed to you (like manually + * entered text from UI), you can construct a blank URI object and + * call unknownToURI() passing in the unknown string. + * + * @return true if successful, false if there was some kind of + * parsing error + */ + protected function parseURI(uri:String) : Boolean + { + var baseURI:String = uri; + var index:int, index2:int; + + // Make sure this object is clean before we start. If it was used + // before and we are now parsing a new URI, we don't want any stale + // info lying around. + initialize(); + + // Remove any fragments (anchors) from the URI + index = baseURI.indexOf("#"); + if (index != -1) + { + // Store the fragment piece if any + if (baseURI.length > (index + 1)) // +1 is to skip the '#' + _fragment = baseURI.substr(index + 1, baseURI.length - (index + 1)); + + // Trim off the fragment + baseURI = baseURI.substr(0, index); + } + + // We need to strip off any CGI parameters (eg '?param=bob') + index = baseURI.indexOf("?"); + if (index != -1) + { + if (baseURI.length > (index + 1)) + _query = baseURI.substr(index + 1, baseURI.length - (index + 1)); // +1 is to skip the '?' + + // Trim off the query + baseURI = baseURI.substr(0, index); + } + + // Now try to find the scheme part + index = baseURI.search(':'); + index2 = baseURI.search('/'); + + var containsColon:Boolean = (index != -1); + var containsSlash:Boolean = (index2 != -1); + + // This value is indeterminate if "containsColon" is false. + // (if there is no colon, does the slash come before or + // after said non-existing colon?) + var colonBeforeSlash:Boolean = (!containsSlash || index < index2); + + // If it has a colon and it's before the first slash, we will treat + // it as a scheme. If a slash is before a colon, there must be a + // stray colon in a path or something. In which case, the colon is + // not the separator for the scheme. Technically, we could consider + // this an error, but since this is not an ambiguous state (we know + // 100% that this has no scheme), we will keep going. + if (containsColon && colonBeforeSlash) + { + // We found a scheme + _scheme = baseURI.substr(0, index); + + // Normalize the scheme + _scheme = _scheme.toLowerCase(); + + baseURI = baseURI.substr(index + 1); + + if (baseURI.substr(0, 2) == "//") + { + // This is a hierarchical URI + _nonHierarchical = ""; + + // Trim off the "//" + baseURI = baseURI.substr(2, baseURI.length - 2); + } + else + { + // This is a non-hierarchical URI like "mailto:bob@mail.com" + _nonHierarchical = baseURI; + + if ((_valid = validateURI()) == false) + initialize(); // Bad URI. Clear it. + + // No more parsing to do for this case + return isValid(); + } + } + else + { + // No scheme. We will consider this a relative URI + _scheme = ""; + _relative = true; + _nonHierarchical = ""; + } + + // Ok, what we have left is everything after the :// + + // Now that we have stripped off any query and fragment parts, we + // need to split the authority from the path + + if (isRelative()) + { + // Don't bother looking for the authority. It's a relative URI + _authority = ""; + _port = ""; + _path = baseURI; + } + else + { + // Check for malformed UNC style file://///server/type/path/ + // By the time we get here, we have already trimmed the "file://" + // so baseURI will be ///server/type/path. If baseURI only + // has one slash, we leave it alone because that is valid (that + // is the case of "file:///path/to/file.txt" where there is no + // server - implicit "localhost"). + if (baseURI.substr(0, 2) == "//") + { + // Trim all leading slashes + while(baseURI.charAt(0) == "/") + baseURI = baseURI.substr(1, baseURI.length - 1); + } + + index = baseURI.search('/'); + if (index == -1) + { + // No path. We must have passed something like "http://something.com" + _authority = baseURI; + _path = ""; + } + else + { + _authority = baseURI.substr(0, index); + _path = baseURI.substr(index, baseURI.length - index); + } + + // Check to see if the URI has any username or password information. + // For example: ftp://username:password@server.com + index = _authority.search('@'); + if (index != -1) + { + // We have a username and possibly a password + _username = _authority.substr(0, index); + + // Remove the username/password from the authority + _authority = _authority.substr(index + 1); // Skip the '@' + + // Now check to see if the username also has a password + index = _username.search(':'); + if (index != -1) + { + _password = _username.substring(index + 1, _username.length); + _username = _username.substr(0, index); + } + else + _password = ""; + } + else + { + _username = ""; + _password = ""; + } + + // Lastly, check to see if the authorty has a port number. + // This is parsed after the username/password to avoid conflicting + // with the ':' in the 'username:password' if one exists. + index = _authority.search(':'); + if (index != -1) + { + _port = _authority.substring(index + 1, _authority.length); // skip the ':' + _authority = _authority.substr(0, index); + } + else + { + _port = ""; + } + + // Lastly, normalize the authority. Domain names + // are case insensitive. + _authority = _authority.toLowerCase(); + } + + if ((_valid = validateURI()) == false) + initialize(); // Bad URI. Clear it + + return isValid(); + } + + + /******************************************************************** + * Copy function. + */ + public function copyURI(uri:URI) : void + { + this._scheme = uri._scheme; + this._authority = uri._authority; + this._username = uri._username; + this._password = uri._password; + this._port = uri._port; + this._path = uri._path; + this._query = uri._query; + this._fragment = uri._fragment; + this._nonHierarchical = uri._nonHierarchical; + + this._valid = uri._valid; + this._relative = uri._relative; + } + + + /** + * @private + * Checks if the given string only contains a-z or A-Z. + */ + protected function verifyAlpha(str:String) : Boolean + { + var pattern:RegExp = /[^a-z]/; + var index:int; + + str = str.toLowerCase(); + index = str.search(pattern); + + if (index == -1) + return true; + else + return false; + } + + /** + * Is this a valid URI? + * + * @return true if this object represents a valid URI, false + * otherwise. + */ + public function isValid() : Boolean + { + return this._valid; + } + + + /** + * Is this URI an absolute URI? An absolute URI is a complete, fully + * qualified reference to a resource. e.g. http://site.com/index.htm + * Non-hierarchical URI's are always absolute. + */ + public function isAbsolute() : Boolean + { + return !this._relative; + } + + + /** + * Is this URI a relative URI? Relative URI's do not have a scheme + * and only contain a relative path with optional anchor and query + * parts. e.g. "../reports/index.htm". Non-hierarchical URI's + * will never be relative. + */ + public function isRelative() : Boolean + { + return this._relative; + } + + + /** + * Does this URI point to a resource that is a directory/folder? + * The URI specification dictates that any path that ends in a slash + * is a directory. This is needed to be able to perform correct path + * logic when combining relative URI's with absolute URI's to + * obtain the correct absolute URI to a resource. + * + * @see URI.chdir + * + * @return true if this URI represents a directory resource, false + * if this URI represents a file resource. + */ + public function isDirectory() : Boolean + { + if (_path.length == 0) + return false; + + return (_path.charAt(path.length - 1) == '/'); + } + + + /** + * Is this URI a hierarchical URI? URI's can be + */ + public function isHierarchical() : Boolean + { + return hierState; + } + + + /** + * The scheme of the URI. + */ + public function get scheme() : String + { + return URI.unescapeChars(_scheme); + } + public function set scheme(schemeStr:String) : void + { + // Normalize the scheme + var normalized:String = schemeStr.toLowerCase(); + _scheme = URI.fastEscapeChars(normalized, URI.URIschemeExcludedBitmap); + } + + + /** + * The authority (host) of the URI. Only valid for + * hierarchical URI's. If the URI is relative, this will + * be an empty string. When setting this value, the string + * given is assumed to be unescaped. When retrieving this + * value, the resulting string is unescaped. + */ + public function get authority() : String + { + return URI.unescapeChars(_authority); + } + public function set authority(authorityStr:String) : void + { + // Normalize the authority + authorityStr = authorityStr.toLowerCase(); + + _authority = URI.fastEscapeChars(authorityStr, + URI.URIauthorityExcludedBitmap); + + // Only hierarchical URI's can have an authority, make + // sure this URI is of the proper format. + this.hierState = true; + } + + + /** + * The username of the URI. Only valid for hierarchical + * URI's. If the URI is relative, this will be an empty + * string. + * + *

The URI specification allows for authentication + * credentials to be embedded in the URI as such:

+ * + *

http://user:passwd@host/path/to/file.htm

+ * + *

When setting this value, the string + * given is assumed to be unescaped. When retrieving this + * value, the resulting string is unescaped.

+ */ + public function get username() : String + { + return URI.unescapeChars(_username); + } + public function set username(usernameStr:String) : void + { + _username = URI.fastEscapeChars(usernameStr, URI.URIuserpassExcludedBitmap); + + // Only hierarchical URI's can have a username. + this.hierState = true; + } + + + /** + * The password of the URI. Similar to username. + * @see URI.username + */ + public function get password() : String + { + return URI.unescapeChars(_password); + } + public function set password(passwordStr:String) : void + { + _password = URI.fastEscapeChars(passwordStr, + URI.URIuserpassExcludedBitmap); + + // Only hierarchical URI's can have a password. + this.hierState = true; + } + + + /** + * The host port number. Only valid for hierarchical URI's. If + * the URI is relative, this will be an empty string. URI's can + * contain the port number of the remote host: + * + *

http://site.com:8080/index.htm

+ */ + public function get port() : String + { + return URI.unescapeChars(_port); + } + public function set port(portStr:String) : void + { + _port = URI.escapeChars(portStr); + + // Only hierarchical URI's can have a port. + this.hierState = true; + } + + + /** + * The path portion of the URI. Only valid for hierarchical + * URI's. When setting this value, the string + * given is assumed to be unescaped. When retrieving this + * value, the resulting string is unescaped. + * + *

The path portion can be in one of two formats. 1) an absolute + * path, or 2) a relative path. An absolute path starts with a + * slash ('/'), a relative path does not.

+ * + *

An absolute path may look like:

+ * /full/path/to/my/file.htm + * + *

A relative path may look like:

+ * + * path/to/my/file.htm + * ../images/logo.gif + * ../../reports/index.htm + * + * + *

Paths can be absolute or relative. Note that this not the same as + * an absolute or relative URI. An absolute URI can only have absolute + * paths. For example:

+ * + * http:/site.com/path/to/file.htm + * + *

This absolute URI has an absolute path of "/path/to/file.htm".

+ * + *

Relative URI's can have either absolute paths or relative paths. + * All of the following relative URI's are valid:

+ * + * + * /absolute/path/to/file.htm + * path/to/file.htm + * ../path/to/file.htm + * + */ + public function get path() : String + { + return URI.unescapeChars(_path); + } + public function set path(pathStr:String) : void + { + this._path = URI.fastEscapeChars(pathStr, URI.URIpathExcludedBitmap); + + if (this._scheme == UNKNOWN_SCHEME) + { + // We set the path. This is a valid URI now. + this._scheme = ""; + } + + // Only hierarchical URI's can have a path. + hierState = true; + } + + + /** + * The query (CGI) portion of the URI. This part is valid for + * both hierarchical and non-hierarchical URI's. + * + *

This accessor should only be used if a custom query syntax + * is used. This URI class supports the common "param=value" + * style query syntax via the get/setQueryValue() and + * get/setQueryByMap() functions. Those functions should be used + * instead if the common syntax is being used. + * + *

The URI RFC does not specify any particular + * syntax for the query part of a URI. It is intended to allow + * any format that can be agreed upon by the two communicating hosts. + * However, most systems have standardized on the typical CGI + * format:

+ * + * http://site.com/script.php?param1=value1¶m2=value2 + * + *

This class has specific support for this query syntax

+ * + *

This common query format is an array of name/value + * pairs with its own syntax that is different from the overall URI + * syntax. The query has its own escaping logic. For a query part + * to be properly escaped and unescaped, it must be split into its + * component parts. This accessor escapes/unescapes the entire query + * part without regard for it's component parts. This has the + * possibliity of leaving the query string in an ambiguious state in + * regards to its syntax. If the contents of the query part are + * important, it is recommended that get/setQueryValue() or + * get/setQueryByMap() are used instead.

+ * + * If a different query syntax is being used, a subclass of URI + * can be created to handle that specific syntax. + * + * @see URI.getQueryValue, URI.getQueryByMap + */ + public function get query() : String + { + return URI.unescapeChars(_query); + } + public function set query(queryStr:String) : void + { + _query = URI.fastEscapeChars(queryStr, URI.URIqueryExcludedBitmap); + + // both hierarchical and non-hierarchical URI's can + // have a query. Do not set the hierState. + } + + /** + * Accessor to the raw query data. If you are using a custom query + * syntax, this accessor can be used to get and set the query part + * directly with no escaping/unescaping. This should ONLY be used + * if your application logic is handling custom query logic and + * handling the proper escaping of the query part. + */ + public function get queryRaw() : String + { + return _query; + } + public function set queryRaw(queryStr:String) : void + { + _query = queryStr; + } + + + /** + * The fragment (anchor) portion of the URI. This is valid for + * both hierarchical and non-hierarchical URI's. + */ + public function get fragment() : String + { + return URI.unescapeChars(_fragment); + } + public function set fragment(fragmentStr:String) : void + { + _fragment = URI.fastEscapeChars(fragmentStr, URIfragmentExcludedBitmap); + + // both hierarchical and non-hierarchical URI's can + // have a fragment. Do not set the hierState. + } + + + /** + * The non-hierarchical part of the URI. For example, if + * this URI object represents "mailto:somebody@company.com", + * this will contain "somebody@company.com". This is valid only + * for non-hierarchical URI's. + */ + public function get nonHierarchical() : String + { + return URI.unescapeChars(_nonHierarchical); + } + public function set nonHierarchical(nonHier:String) : void + { + _nonHierarchical = URI.fastEscapeChars(nonHier, URInonHierexcludedBitmap); + + // This is a non-hierarchical URI. + this.hierState = false; + } + + + /** + * Quick shorthand accessor to set the parts of this URI. + * The given parts are assumed to be in unescaped form. If + * the URI is non-hierarchical (e.g. mailto:) you will need + * to call SetScheme() and SetNonHierarchical(). + */ + public function setParts(schemeStr:String, authorityStr:String, + portStr:String, pathStr:String, queryStr:String, + fragmentStr:String) : void + { + this.scheme = schemeStr; + this.authority = authorityStr; + this.port = portStr; + this.path = pathStr; + this.query = queryStr; + this.fragment = fragmentStr; + + hierState = true; + } + + + /** + * URI escapes the given character string. This is similar in function + * to the global encodeURIComponent() function in ActionScript, but is + * slightly different in regards to which characters get escaped. This + * escapes the characters specified in the URIbaselineExluded set (see class + * static members). This is needed for this class to work properly. + * + *

If a different set of characters need to be used for the escaping, + * you may use fastEscapeChars() and specify a custom URIEncodingBitmap + * that contains the characters your application needs escaped.

+ * + *

Never pass a full URI to this function. A URI can only be properly + * escaped/unescaped when split into its component parts (see RFC 3986 + * section 2.4). This is due to the fact that the URI component separators + * could be characters that would normally need to be escaped.

+ * + * @param unescaped character string to be escaped. + * + * @return escaped character string + * + * @see encodeURIComponent + * @see fastEscapeChars + */ + static public function escapeChars(unescaped:String) : String + { + // This uses the excluded set by default. + return fastEscapeChars(unescaped, URI.URIbaselineExcludedBitmap); + } + + + /** + * Unescape any URI escaped characters in the given character + * string. + * + *

Never pass a full URI to this function. A URI can only be properly + * escaped/unescaped when split into its component parts (see RFC 3986 + * section 2.4). This is due to the fact that the URI component separators + * could be characters that would normally need to be escaped.

+ * + * @param escaped the escaped string to be unescaped. + * + * @return unescaped string. + */ + static public function unescapeChars(escaped:String /*, onlyHighASCII:Boolean = false*/) : String + { + // We can just use the default AS function. It seems to + // decode everything correctly + var unescaped:String; + unescaped = decodeURIComponent(escaped); + return unescaped; + } + + /** + * Performance focused function that escapes the given character + * string using the given URIEncodingBitmap as the rule for what + * characters need to be escaped. This function is used by this + * class and can be used externally to this class to perform + * escaping on custom character sets. + * + *

Never pass a full URI to this function. A URI can only be properly + * escaped/unescaped when split into its component parts (see RFC 3986 + * section 2.4). This is due to the fact that the URI component separators + * could be characters that would normally need to be escaped.

+ * + * @param unescaped the unescaped string to be escaped + * @param bitmap the set of characters that need to be escaped + * + * @return the escaped string. + */ + static public function fastEscapeChars(unescaped:String, bitmap:URIEncodingBitmap) : String + { + var escaped:String = ""; + var c:String; + var x:int, i:int; + + for (i = 0; i < unescaped.length; i++) + { + c = unescaped.charAt(i); + + x = bitmap.ShouldEscape(c); + if (x) + { + c = x.toString(16); + if (c.length == 1) + c = "0" + c; + + c = "%" + c; + c = c.toUpperCase(); + } + + escaped += c; + } + + return escaped; + } + + + /** + * Is this URI of a particular scheme type? For example, + * passing "http" to a URI object that represents the URI + * "http://site.com/" would return true. + * + * @param scheme scheme to check for + * + * @return true if this URI object is of the given type, false + * otherwise. + */ + public function isOfType(scheme:String) : Boolean + { + // Schemes are never case sensitive. Ignore case. + scheme = scheme.toLowerCase(); + return (this._scheme == scheme); + } + + + /** + * Get the value for the specified named in the query part. This + * assumes the query part of the URI is in the common + * "name1=value1&name2=value2" syntax. Do not call this function + * if you are using a custom query syntax. + * + * @param name name of the query value to get. + * + * @return the value of the query name, empty string if the + * query name does not exist. + */ + public function getQueryValue(name:String) : String + { + var map:Object; + var item:String; + var value:String; + + map = getQueryByMap(); + + for (item in map) + { + if (item == name) + { + value = map[item]; + return value; + } + } + + // Didn't find the specified key + return new String(""); + } + + + /** + * Set the given value on the given query name. If the given name + * does not exist, it will automatically add this name/value pair + * to the query. If null is passed as the value, it will remove + * the given item from the query. + * + *

This automatically escapes any characters that may conflict with + * the query syntax so that they are "safe" within the query. The + * strings passed are assumed to be literal unescaped name and value.

+ * + * @param name name of the query value to set + * @param value value of the query item to set. If null, this will + * force the removal of this item from the query. + */ + public function setQueryValue(name:String, value:String) : void + { + var map:Object; + + map = getQueryByMap(); + + // If the key doesn't exist yet, this will create a new pair in + // the map. If it does exist, this will overwrite the previous + // value, which is what we want. + map[name] = value; + + setQueryByMap(map); + } + + + /** + * Get the query of the URI in an Object class that allows for easy + * access to the query data via Object accessors. For example: + * + * + * var query:Object = uri.getQueryByMap(); + * var value:String = query["param"]; // get a value + * query["param2"] = "foo"; // set a new value + * + * + * @return Object that contains the name/value pairs of the query. + * + * @see #setQueryByMap + * @see #getQueryValue + * @see #setQueryValue + */ + public function getQueryByMap() : Object + { + var queryStr:String; + var pair:String; + var pairs:Array; + var item:Array; + var name:String, value:String; + var index:int; + var map:Object = new Object(); + + + // We need the raw query string, no unescaping. + queryStr = this._query; + + pairs = queryStr.split('&'); + for each (pair in pairs) + { + if (pair.length == 0) + continue; + + item = pair.split('='); + + if (item.length > 0) + name = item[0]; + else + continue; // empty array + + if (item.length > 1) + value = item[1]; + else + value = ""; + + name = queryPartUnescape(name); + value = queryPartUnescape(value); + + map[name] = value; + } + + return map; + } + + + /** + * Set the query part of this URI using the given object as the + * content source. Any member of the object that has a value of + * null will not be in the resulting query. + * + * @param map object that contains the name/value pairs as + * members of that object. + * + * @see #getQueryByMap + * @see #getQueryValue + * @see #setQueryValue + */ + public function setQueryByMap(map:Object) : void + { + var item:String; + var name:String, value:String; + var queryStr:String = ""; + var tmpPair:String; + var foo:String; + + for (item in map) + { + name = item; + value = map[item]; + + if (value == null) + value = ""; + + // Need to escape the name/value pair so that they + // don't conflict with the query syntax (specifically + // '=', '&', and ). + name = queryPartEscape(name); + value = queryPartEscape(value); + + tmpPair = name; + + if (value.length > 0) + { + tmpPair += "="; + tmpPair += value; + } + + if (queryStr.length != 0) + queryStr += '&'; // Add the separator + + queryStr += tmpPair; + } + + // We don't want to escape. We already escaped the + // individual name/value pairs. If we escaped the + // query string again by assigning it to "query", + // we would have double escaping. + _query = queryStr; + } + + + /** + * Similar to Escape(), except this also escapes characters that + * would conflict with the name/value pair query syntax. This is + * intended to be called on each individual "name" and "value" + * in the query making sure that nothing in the name or value + * strings contain characters that would conflict with the query + * syntax (e.g. '=' and '&'). + * + * @param unescaped unescaped string that is to be escaped. + * + * @return escaped string. + * + * @see #queryUnescape + */ + static public function queryPartEscape(unescaped:String) : String + { + var escaped:String = unescaped; + escaped = URI.fastEscapeChars(unescaped, URI.URIqueryPartExcludedBitmap); + return escaped; + } + + + /** + * Unescape the individual name/value string pairs. + * + * @param escaped escaped string to be unescaped + * + * @return unescaped string + * + * @see #queryEscape + */ + static public function queryPartUnescape(escaped:String) : String + { + var unescaped:String = escaped; + unescaped = unescapeChars(unescaped); + return unescaped; + } + + /** + * Output this URI as a string. The resulting string is properly + * escaped and well formed for machine processing. + */ + public function toString() : String + { + if (this == null) + return ""; + else + return toStringInternal(false); + } + + /** + * Output the URI as a string that is easily readable by a human. + * This outputs the URI with all escape sequences unescaped to + * their character representation. This makes the URI easier for + * a human to read, but the URI could be completely invalid + * because some unescaped characters may now cause ambiguous parsing. + * This function should only be used if you want to display a URI to + * a user. This function should never be used outside that specific + * case. + * + * @return the URI in string format with all escape sequences + * unescaped. + * + * @see #toString + */ + public function toDisplayString() : String + { + return toStringInternal(true); + } + + + /** + * @private + * + * The guts of toString() + */ + protected function toStringInternal(forDisplay:Boolean) : String + { + var uri:String = ""; + var part:String = ""; + + if (isHierarchical() == false) + { + // non-hierarchical URI + + uri += (forDisplay ? this.scheme : _scheme); + uri += ":"; + uri += (forDisplay ? this.nonHierarchical : _nonHierarchical); + } + else + { + // Hierarchical URI + + if (isRelative() == false) + { + // If it is not a relative URI, then we want the scheme and + // authority parts in the string. If it is relative, we + // do NOT want this stuff. + + if (_scheme.length != 0) + { + part = (forDisplay ? this.scheme : _scheme); + uri += part + ":"; + } + + if (_authority.length != 0 || isOfType("file")) + { + uri += "//"; + + // Add on any username/password associated with this + // authority + if (_username.length != 0) + { + part = (forDisplay ? this.username : _username); + uri += part; + + if (_password.length != 0) + { + part = (forDisplay ? this.password : _password); + uri += ":" + part; + } + + uri += "@"; + } + + // add the authority + part = (forDisplay ? this.authority : _authority); + uri += part; + + // Tack on the port number, if any + if (port.length != 0) + uri += ":" + port; + } + } + + // Tack on the path + part = (forDisplay ? this.path : _path); + uri += part; + + } // end hierarchical part + + // Both non-hier and hierarchical have query and fragment parts + + // Add on the query and fragment parts + if (_query.length != 0) + { + part = (forDisplay ? this.query : _query); + uri += "?" + part; + } + + if (fragment.length != 0) + { + part = (forDisplay ? this.fragment : _fragment); + uri += "#" + part; + } + + return uri; + } + + /** + * Forcefully ensure that this URI is properly escaped. + * + *

Sometimes URI's are constructed by hand using strings outside + * this class. In those cases, it is unlikely the URI has been + * properly escaped. This function forcefully escapes this URI + * by unescaping each part and then re-escaping it. If the URI + * did not have any escaping, the first unescape will do nothing + * and then the re-escape will properly escape everything. If + * the URI was already escaped, the unescape and re-escape will + * essentally be a no-op. This provides a safe way to make sure + * a URI is in the proper escaped form.

+ */ + public function forceEscape() : void + { + // The accessors for each of the members will unescape + // and then re-escape as we get and assign them. + + // Handle the parts that are common for both hierarchical + // and non-hierarchical URI's + this.scheme = this.scheme; + this.setQueryByMap(this.getQueryByMap()); + this.fragment = this.fragment; + + if (isHierarchical()) + { + this.authority = this.authority; + this.path = this.path; + this.port = this.port; + this.username = this.username; + this.password = this.password; + } + else + { + this.nonHierarchical = this.nonHierarchical; + } + } + + + /** + * Does this URI point to a resource of the given file type? + * Given a file extension (or just a file name, this will strip the + * extension), check to see if this URI points to a file of that + * type. + * + * @param extension string that contains a file extension with or + * without a dot ("html" and ".html" are both valid), or a file + * name with an extension (e.g. "index.html"). + * + * @return true if this URI points to a resource with the same file + * file extension as the extension provided, false otherwise. + */ + public function isOfFileType(extension:String) : Boolean + { + var thisExtension:String; + var index:int; + + index = extension.lastIndexOf("."); + if (index != -1) + { + // Strip the extension + extension = extension.substr(index + 1); + } + else + { + // The caller passed something without a dot in it. We + // will assume that it is just a plain extension (e.g. "html"). + // What they passed is exactly what we want + } + + thisExtension = getExtension(true); + + if (thisExtension == "") + return false; + + // Compare the extensions ignoring case + if (compareStr(thisExtension, extension, false) == 0) + return true; + else + return false; + } + + + /** + * Get the ".xyz" file extension from the filename in the URI. + * For example, if we have the following URI: + * + * http://something.com/path/to/my/page.html?form=yes&name=bob#anchor + * + *

This will return ".html".

+ * + * @param minusDot If true, this will strip the dot from the extension. + * If true, the above example would have returned "html". + * + * @return the file extension + */ + public function getExtension(minusDot:Boolean = false) : String + { + var filename:String = getFilename(); + var extension:String; + var index:int; + + if (filename == "") + return String(""); + + index = filename.lastIndexOf("."); + + // If it doesn't have an extension, or if it is a "hidden" file, + // it doesn't have an extension. Hidden files on unix start with + // a dot (e.g. ".login"). + if (index == -1 || index == 0) + return String(""); + + extension = filename.substr(index); + + // If the caller does not want the dot, remove it. + if (minusDot && extension.charAt(0) == ".") + extension = extension.substr(1); + + return extension; + } + + /** + * Quick function to retrieve the file name off the end of a URI. + * + *

For example, if the URI is:

+ * http://something.com/some/path/to/my/file.html + *

this function will return "file.html".

+ * + * @param minusExtension true if the file extension should be stripped + * + * @return the file name. If this URI is a directory, the return + * value will be empty string. + */ + public function getFilename(minusExtension:Boolean = false) : String + { + if (isDirectory()) + return String(""); + + var pathStr:String = this.path; + var filename:String; + var index:int; + + // Find the last path separator. + index = pathStr.lastIndexOf("/"); + + if (index != -1) + filename = pathStr.substr(index + 1); + else + filename = pathStr; + + if (minusExtension) + { + // The caller has requested that the extension be removed + index = filename.lastIndexOf("."); + + if (index != -1) + filename = filename.substr(0, index); + } + + return filename; + } + + + /** + * @private + * Helper function to compare strings. + * + * @return true if the two strings are identical, false otherwise. + */ + static protected function compareStr(str1:String, str2:String, + sensitive:Boolean = true) : Boolean + { + if (sensitive == false) + { + str1 = str1.toLowerCase(); + str2 = str2.toLowerCase(); + } + + return (str1 == str2) + } + + /** + * Based on the type of this URI (http, ftp, etc.) get + * the default port used for that protocol. This is + * just intended to be a helper function for the most + * common cases. + */ + public function getDefaultPort() : String + { + if (_scheme == "http") + return String("80"); + else if (_scheme == "ftp") + return String("21"); + else if (_scheme == "file") + return String(""); + else if (_scheme == "sftp") + return String("22"); // ssh standard port + else + { + // Don't know the port for this URI type + return String(""); + } + } + + /** + * @private + * + * This resolves the given URI if the application has a + * resolver interface defined. This function does not + * modify the passed in URI and returns a new URI. + */ + static protected function resolve(uri:URI) : URI + { + var copy:URI = new URI(); + copy.copyURI(uri); + + if (_resolver != null) + { + // A resolver class has been registered. Call it. + return _resolver.resolve(copy); + } + else + { + // No resolver. Nothing to do, but we don't + // want to reuse the one passed in. + return copy; + } + } + + /** + * Accessor to set and get the resolver object used by all URI + * objects to dynamically resolve URI's before comparison. + */ + static public function set resolver(resolver:IURIResolver) : void + { + _resolver = resolver; + } + static public function get resolver() : IURIResolver + { + return _resolver; + } + + /** + * Given another URI, return this URI object's relation to the one given. + * URI's can have 1 of 4 possible relationships. They can be unrelated, + * equal, parent, or a child of the given URI. + * + * @param uri URI to compare this URI object to. + * @param caseSensitive true if the URI comparison should be done + * taking case into account, false if the comparison should be + * performed case insensitive. + * + * @return URI.NOT_RELATED, URI.CHILD, URI.PARENT, or URI.EQUAL + */ + public function getRelation(uri:URI, caseSensitive:Boolean = true) : int + { + // Give the app a chance to resolve these URI's before we compare them. + var thisURI:URI = URI.resolve(this); + var thatURI:URI = URI.resolve(uri); + + if (thisURI.isRelative() || thatURI.isRelative()) + { + // You cannot compare relative URI's due to their lack of context. + // You could have two relative URI's that look like: + // ../../images/ + // ../../images/marketing/logo.gif + // These may appear related, but you have no overall context + // from which to make the comparison. The first URI could be + // from one site and the other URI could be from another site. + return URI.NOT_RELATED; + } + else if (thisURI.isHierarchical() == false || thatURI.isHierarchical() == false) + { + // One or both of the URI's are non-hierarchical. + if (((thisURI.isHierarchical() == false) && (thatURI.isHierarchical() == true)) || + ((thisURI.isHierarchical() == true) && (thatURI.isHierarchical() == false))) + { + // XOR. One is hierarchical and the other is + // non-hierarchical. They cannot be compared. + return URI.NOT_RELATED; + } + else + { + // They are both non-hierarchical + if (thisURI.scheme != thatURI.scheme) + return URI.NOT_RELATED; + + if (thisURI.nonHierarchical != thatURI.nonHierarchical) + return URI.NOT_RELATED; + + // The two non-hierarcical URI's are equal. + return URI.EQUAL; + } + } + + // Ok, this URI and the one we are being compared to are both + // absolute hierarchical URI's. + + if (thisURI.scheme != thatURI.scheme) + return URI.NOT_RELATED; + + if (thisURI.authority != thatURI.authority) + return URI.NOT_RELATED; + + var thisPort:String = thisURI.port; + var thatPort:String = thatURI.port; + + // Different ports are considered completely different servers. + if (thisPort == "") + thisPort = thisURI.getDefaultPort(); + if (thatPort == "") + thatPort = thatURI.getDefaultPort(); + + // Check to see if the port is the default port. + if (thisPort != thatPort) + return URI.NOT_RELATED; + + if (compareStr(thisURI.path, thatURI.path, caseSensitive)) + return URI.EQUAL; + + // Special case check. If we are here, the scheme, authority, + // and port match, and it is not a relative path, but the + // paths did not match. There is a special case where we + // could have: + // http://something.com/ + // http://something.com + // Technically, these are equal. So lets, check for this case. + var thisPath:String = thisURI.path; + var thatPath:String = thatURI.path; + + if ( (thisPath == "/" || thatPath == "/") && + (thisPath == "" || thatPath == "") ) + { + // We hit the special case. These two are equal. + return URI.EQUAL; + } + + // Ok, the paths do not match, but one path may be a parent/child + // of the other. For example, we may have: + // http://something.com/path/to/homepage/ + // http://something.com/path/to/homepage/images/logo.gif + // In this case, the first is a parent of the second (or the second + // is a child of the first, depending on which you compare to the + // other). To make this comparison, we must split the path into + // its component parts (split the string on the '/' path delimiter). + // We then compare the + var thisParts:Array, thatParts:Array; + var thisPart:String, thatPart:String; + var i:int; + + thisParts = thisPath.split("/"); + thatParts = thatPath.split("/"); + + if (thisParts.length > thatParts.length) + { + thatPart = thatParts[thatParts.length - 1]; + if (thatPart.length > 0) + { + // if the last part is not empty, the passed URI is + // not a directory. There is no way the passed URI + // can be a parent. + return URI.NOT_RELATED; + } + else + { + // Remove the empty trailing part + thatParts.pop(); + } + + // This may be a child of the one passed in + for (i = 0; i < thatParts.length; i++) + { + thisPart = thisParts[i]; + thatPart = thatParts[i]; + + if (compareStr(thisPart, thatPart, caseSensitive) == false) + return URI.NOT_RELATED; + } + + return URI.CHILD; + } + else if (thisParts.length < thatParts.length) + { + thisPart = thisParts[thisParts.length - 1]; + if (thisPart.length > 0) + { + // if the last part is not empty, this URI is not a + // directory. There is no way this object can be + // a parent. + return URI.NOT_RELATED; + } + else + { + // Remove the empty trailing part + thisParts.pop(); + } + + // This may be the parent of the one passed in + for (i = 0; i < thisParts.length; i++) + { + thisPart = thisParts[i]; + thatPart = thatParts[i]; + + if (compareStr(thisPart, thatPart, caseSensitive) == false) + return URI.NOT_RELATED; + } + + return URI.PARENT; + } + else + { + // Both URI's have the same number of path components, but + // it failed the equivelence check above. This means that + // the two URI's are not related. + return URI.NOT_RELATED; + } + + // If we got here, the scheme and authority are the same, + // but the paths pointed to two different locations that + // were in different parts of the file system tree + return URI.NOT_RELATED; + } + + /** + * Given another URI, return the common parent between this one + * and the provided URI. + * + * @param uri the other URI from which to find a common parent + * @para caseSensitive true if this operation should be done + * with case sensitive comparisons. + * + * @return the parent URI if successful, null otherwise. + */ + public function getCommonParent(uri:URI, caseSensitive:Boolean = true) : URI + { + var thisURI:URI = URI.resolve(this); + var thatURI:URI = URI.resolve(uri); + + if(!thisURI.isAbsolute() || !thatURI.isAbsolute() || + thisURI.isHierarchical() == false || + thatURI.isHierarchical() == false) + { + // Both URI's must be absolute hierarchical for this to + // make sense. + return null; + } + + var relation:int = thisURI.getRelation(thatURI); + if (relation == URI.NOT_RELATED) + { + // The given URI is not related to this one. No + // common parent. + return null; + } + + thisURI.chdir("."); + thatURI.chdir("."); + + var strBefore:String, strAfter:String; + do + { + relation = thisURI.getRelation(thatURI, caseSensitive); + if(relation == URI.EQUAL || relation == URI.PARENT) + break; + + // If strBefore and strAfter end up being the same, + // we know we are at the root of the path because + // chdir("..") is doing nothing. + strBefore = thisURI.toString(); + thisURI.chdir(".."); + strAfter = thisURI.toString(); + } + while(strBefore != strAfter); + + return thisURI; + } + + + /** + * This function is used to move around in a URI in a way similar + * to the 'cd' or 'chdir' commands on Unix. These operations are + * completely string based, using the context of the URI to + * determine the position within the path. The heuristics used + * to determine the action are based off Appendix C in RFC 2396. + * + *

URI paths that end in '/' are considered paths that point to + * directories, while paths that do not end in '/' are files. For + * example, if you execute chdir("d") on the following URI's:
+ * 1. http://something.com/a/b/c/ (directory)
+ * 2. http://something.com/a/b/c (not directory)
+ * you will get:
+ * 1. http://something.com/a/b/c/d
+ * 2. http://something.com/a/b/d

+ * + *

See RFC 2396, Appendix C for more info.

+ * + * @param reference the URI or path to "cd" to. + * @param escape true if the passed reference string should be URI + * escaped before using it. + * + * @return true if the chdir was successful, false otherwise. + */ + public function chdir(reference:String, escape:Boolean = false) : Boolean + { + var uriReference:URI; + var ref:String = reference; + + if (escape) + ref = URI.escapeChars(reference); + + if (ref == "") + { + // NOOP + return true; + } + else if (ref.substr(0, 2) == "//") + { + // Special case. This is an absolute URI but without the scheme. + // Take the scheme from this URI and tack it on. This is + // intended to make working with chdir() a little more + // tolerant. + var final:String = this.scheme + ":" + ref; + + return constructURI(final); + } + else if (ref.charAt(0) == "?") + { + // A relative URI that is just a query part is essentially + // a "./?query". We tack on the "./" here to make the rest + // of our logic work. + ref = "./" + ref; + } + + // Parse the reference passed in as a URI. This way we + // get any query and fragments parsed out as well. + uriReference = new URI(ref); + + if (uriReference.isAbsolute() || + uriReference.isHierarchical() == false) + { + // If the URI given is a full URI, it replaces this one. + copyURI(uriReference); + return true; + } + + + var thisPath:String, thatPath:String; + var thisParts:Array, thatParts:Array; + var thisIsDir:Boolean = false, thatIsDir:Boolean = false; + var thisIsAbs:Boolean = false, thatIsAbs:Boolean = false; + var lastIsDotOperation:Boolean = false; + var curDir:String; + var i:int; + + thisPath = this.path; + thatPath = uriReference.path; + + if (thisPath.length > 0) + thisParts = thisPath.split("/"); + else + thisParts = new Array(); + + if (thatPath.length > 0) + thatParts = thatPath.split("/"); + else + thatParts = new Array(); + + if (thisParts.length > 0 && thisParts[0] == "") + { + thisIsAbs = true; + thisParts.shift(); // pop the first one off the array + } + if (thisParts.length > 0 && thisParts[thisParts.length - 1] == "") + { + thisIsDir = true; + thisParts.pop(); // pop the last one off the array + } + + if (thatParts.length > 0 && thatParts[0] == "") + { + thatIsAbs = true; + thatParts.shift(); // pop the first one off the array + } + if (thatParts.length > 0 && thatParts[thatParts.length - 1] == "") + { + thatIsDir = true; + thatParts.pop(); // pop the last one off the array + } + + if (thatIsAbs) + { + // The reference is an absolute path (starts with a slash). + // It replaces this path wholesale. + this.path = uriReference.path; + + // And it inherits the query and fragment + this.queryRaw = uriReference.queryRaw; + this.fragment = uriReference.fragment; + + return true; + } + else if (thatParts.length == 0 && uriReference.query == "") + { + // The reference must have only been a fragment. Fragments just + // get appended to whatever the current path is. We don't want + // to overwrite any query that may already exist, so this case + // only takes on the new fragment. + this.fragment = uriReference.fragment; + return true; + } + else if (thisIsDir == false && thisParts.length > 0) + { + // This path ends in a file. It goes away no matter what. + thisParts.pop(); + } + + // By default, this assumes the query and fragment of the reference + this.queryRaw = uriReference.queryRaw; + this.fragment = uriReference.fragment; + + // Append the parts of the path from the passed in reference + // to this object's path. + thisParts = thisParts.concat(thatParts); + + for(i = 0; i < thisParts.length; i++) + { + curDir = thisParts[i]; + lastIsDotOperation = false; + + if (curDir == ".") + { + thisParts.splice(i, 1); + i = i - 1; // account for removing this item + lastIsDotOperation = true; + } + else if (curDir == "..") + { + if (i >= 1) + { + if (thisParts[i - 1] == "..") + { + // If the previous is a "..", we must have skipped + // it due to this URI being relative. We can't + // collapse leading ".."s in a relative URI, so + // do nothing. + } + else + { + thisParts.splice(i - 1, 2); + i = i - 2; // move back to account for the 2 we removed + } + } + else + { + // This is the first thing in the path. + + if (isRelative()) + { + // We can't collapse leading ".."s in a relative + // path. Do noting. + } + else + { + // This is an abnormal case. We have dot-dotted up + // past the base of our "file system". This is a + // case where we had a /path/like/this.htm and were + // given a path to chdir to like this: + // ../../../../../../mydir + // Obviously, it has too many ".." and will take us + // up beyond the top of the URI. However, according + // RFC 2396 Appendix C.2, we should try to handle + // these abnormal cases appropriately. In this case, + // we will do what UNIX command lines do if you are + // at the root (/) of the filesystem and execute: + // # cd ../../../../../bin + // Which will put you in /bin. Essentially, the extra + // ".."'s will just get eaten. + + thisParts.splice(i, 1); + i = i - 1; // account for the ".." we just removed + } + } + + lastIsDotOperation = true; + } + } + + var finalPath:String = ""; + + // If the last thing in the path was a "." or "..", then this thing is a + // directory. If the last thing isn't a dot-op, then we don't want to + // blow away any information about the directory (hence the "|=" binary + // assignment). + thatIsDir = thatIsDir || lastIsDotOperation; + + // Reconstruct the path with the abs/dir info we have + finalPath = joinPath(thisParts, thisIsAbs, thatIsDir); + + // Set the path (automatically escaping it) + this.path = finalPath; + + return true; + } + + /** + * @private + * Join an array of path parts back into a URI style path string. + * This is used by the various path logic functions to recombine + * a path. This is different than the standard Array.join() + * function because we need to take into account the starting and + * ending path delimiters if this is an absolute path or a + * directory. + * + * @param parts the Array that contains strings of each path part. + * @param isAbs true if the given path is absolute + * @param isDir true if the given path is a directory + * + * @return the combined path string. + */ + protected function joinPath(parts:Array, isAbs:Boolean, isDir:Boolean) : String + { + var pathStr:String = ""; + var i:int; + + for (i = 0; i < parts.length; i++) + { + if (pathStr.length > 0) + pathStr += "/"; + + pathStr += parts[i]; + } + + // If this path is a directory, tack on the directory delimiter, + // but only if the path contains something. Adding this to an + // empty path would make it "/", which is an absolute path that + // starts at the root. + if (isDir && pathStr.length > 0) + pathStr += "/"; + + if (isAbs) + pathStr = "/" + pathStr; + + return pathStr; + } + + /** + * Given an absolute URI, make this relative URI absolute using + * the given URI as a base. This URI instance must be relative + * and the base_uri must be absolute. + * + * @param base_uri URI to use as the base from which to make + * this relative URI into an absolute URI. + * + * @return true if successful, false otherwise. + */ + public function makeAbsoluteURI(base_uri:URI) : Boolean + { + if (isAbsolute() || base_uri.isRelative()) + { + // This URI needs to be relative, and the base needs to be + // absolute otherwise we won't know what to do! + return false; + } + + // Make a copy of the base URI. We don't want to modify + // the passed URI. + var base:URI = new URI(); + base.copyURI(base_uri); + + // ChDir on the base URI. This will preserve any query + // and fragment we have. + if (base.chdir(toString()) == false) + return false; + + // It worked, so copy the base into this one + copyURI(base); + + return true; + } + + + /** + * Given a URI to use as a base from which this object should be + * relative to, convert this object into a relative URI. For example, + * if you have: + * + * + * var uri1:URI = new URI("http://something.com/path/to/some/file.html"); + * var uri2:URI = new URI("http://something.com/path/to/another/file.html"); + * + * uri1.MakeRelativePath(uri2); + * + *

uri1 will have a final value of "../some/file.html"

+ * + *

Note! This function is brute force. If you have two URI's + * that are completely unrelated, this will still attempt to make + * the relative URI. In that case, you will most likely get a + * relative path that looks something like:

+ * + *

../../../../../../some/path/to/my/file.html

+ * + * @param base_uri the URI from which to make this URI relative + * + * @return true if successful, false if the base_uri and this URI + * are not related, of if error. + */ + public function makeRelativeURI(base_uri:URI, caseSensitive:Boolean = true) : Boolean + { + var base:URI = new URI(); + base.copyURI(base_uri); + + var thisParts:Array, thatParts:Array; + var finalParts:Array = new Array(); + var thisPart:String, thatPart:String, finalPath:String; + var pathStr:String = this.path; + var queryStr:String = this.queryRaw; + var fragmentStr:String = this.fragment; + var i:int; + var diff:Boolean = false; + var isDir:Boolean = false; + + if (isRelative()) + { + // We're already relative. + return true; + } + + if (base.isRelative()) + { + // The base is relative. A relative base doesn't make sense. + return false; + } + + + if ( (isOfType(base_uri.scheme) == false) || + (this.authority != base_uri.authority) ) + { + // The schemes and/or authorities are different. We can't + // make a relative path to something that is completely + // unrelated. + return false; + } + + // Record the state of this URI + isDir = isDirectory(); + + // We are based of the directory of the given URI. We need to + // make sure the URI is pointing to a directory. Changing + // directory to "." will remove any file name if the base is + // not a directory. + base.chdir("."); + + thisParts = pathStr.split("/"); + thatParts = base.path.split("/"); + + if (thisParts.length > 0 && thisParts[0] == "") + thisParts.shift(); + + if (thisParts.length > 0 && thisParts[thisParts.length - 1] == "") + { + isDir = true; + thisParts.pop(); + } + + if (thatParts.length > 0 && thatParts[0] == "") + thatParts.shift(); + if (thatParts.length > 0 && thatParts[thatParts.length - 1] == "") + thatParts.pop(); + + + // Now that we have the paths split into an array of directories, + // we can compare the two paths. We start from the left of side + // of the path and start comparing. When we either run out of + // directories (one path is longer than the other), or we find + // a directory that is different, we stop. The remaining parts + // of each path is then used to determine the relative path. For + // example, lets say we have: + // path we want to make relative: /a/b/c/d/e.txt + // path to use as base for relative: /a/b/f/ + // + // This loop will start at the left, and remove directories + // until we get a mismatch or run off the end of one of them. + // In this example, the result will be: + // c/d/e.txt + // f + // + // For every part left over in the base path, we prepend a ".." + // to the relative to get the final path: + // ../c/d/e.txt + while(thatParts.length > 0) + { + if (thisParts.length == 0) + { + // we matched all there is to match, we are done. + // This is the case where "this" object is a parent + // path of the given URI. eg: + // this.path = /a/b/ (thisParts) + // base.path = /a/b/c/d/e/ (thatParts) + break; + } + + thisPart = thisParts[0]; + thatPart = thatParts[0]; + + if (compareStr(thisPart, thatPart, caseSensitive)) + { + thisParts.shift(); + thatParts.shift(); + } + else + break; + } + + // If there are any path info left from the base URI, that means + // **this** object is above the given URI in the file tree. For + // each part left over in the given URI, we need to move up one + // directory to get where we are. + var dotdot:String = ".."; + for (i = 0; i < thatParts.length; i++) + { + finalParts.push(dotdot); + } + + // Append the parts of this URI to any dot-dot's we have + finalParts = finalParts.concat(thisParts); + + // Join the parts back into a path + finalPath = joinPath(finalParts, false /* not absolute */, isDir); + + if (finalPath.length == 0) + { + // The two URI's are exactly the same. The proper relative + // path is: + finalPath = "./"; + } + + // Set the parts of the URI, preserving the original query and + // fragment parts. + setParts("", "", "", finalPath, queryStr, fragmentStr); + + return true; + } + + /** + * Given a string, convert it to a URI. The string could be a + * full URI that is improperly escaped, a malformed URI (e.g. + * missing a protocol like "www.something.com"), a relative URI, + * or any variation there of. + * + *

The intention of this function is to take anything that a + * user might manually enter as a URI/URL and try to determine what + * they mean. This function differs from the URI constructor in + * that it makes some assumptions to make it easy to import user + * entered URI data.

+ * + *

This function is intended to be a helper function. + * It is not all-knowning and will probably make mistakes + * when attempting to parse a string of unknown origin. If + * your applicaiton is receiving input from the user, your + * application should already have a good idea what the user + * should be entering, and your application should be + * pre-processing the user's input to make sure it is well formed + * before passing it to this function.

+ * + *

It is assumed that the string given to this function is + * something the user may have manually entered. Given this, + * the URI string is probably unescaped or improperly escaped. + * This function will attempt to properly escape the URI by + * using forceEscape(). The result is that a toString() call + * on a URI that was created from unknownToURI() may not match + * the input string due to the difference in escaping.

+ * + * @param unknown a potental URI string that should be parsed + * and loaded into this object. + * @param defaultScheme if it is determined that the passed string + * looks like a URI, but it is missing the scheme part, this + * string will be used as the missing scheme. + * + * @return true if the given string was successfully parsed into + * a valid URI object, false otherwise. + */ + public function unknownToURI(unknown:String, defaultScheme:String = "http") : Boolean + { + var temp:String; + + if (unknown.length == 0) + { + this.initialize(); + return false; + } + + // Some users love the backslash key. Fix it. + unknown = unknown.replace(/\\/g, "/"); + + // Check for any obviously missing scheme. + if (unknown.length >= 2) + { + temp = unknown.substr(0, 2); + if (temp == "//") + unknown = defaultScheme + ":" + unknown; + } + + if (unknown.length >= 3) + { + temp = unknown.substr(0, 3); + if (temp == "://") + unknown = defaultScheme + unknown; + } + + // Try parsing it as a normal URI + var uri:URI = new URI(unknown); + + if (uri.isHierarchical() == false) + { + if (uri.scheme == UNKNOWN_SCHEME) + { + this.initialize(); + return false; + } + + // It's a non-hierarchical URI + copyURI(uri); + forceEscape(); + return true; + } + else if ((uri.scheme != UNKNOWN_SCHEME) && + (uri.scheme.length > 0)) + { + if ( (uri.authority.length > 0) || + (uri.scheme == "file") ) + { + // file://... URI + copyURI(uri); + forceEscape(); // ensure proper escaping + return true; + } + else if (uri.authority.length == 0 && uri.path.length == 0) + { + // It's is an incomplete URI (eg "http://") + + setParts(uri.scheme, "", "", "", "", ""); + return false; + } + } + else + { + // Possible relative URI. We can only detect relative URI's + // that start with "." or "..". If it starts with something + // else, the parsing is ambiguous. + var path:String = uri.path; + + if (path == ".." || path == "." || + (path.length >= 3 && path.substr(0, 3) == "../") || + (path.length >= 2 && path.substr(0, 2) == "./") ) + { + // This is a relative URI. + copyURI(uri); + forceEscape(); + return true; + } + } + + // Ok, it looks like we are just a normal URI missing the scheme. Tack + // on the scheme. + uri = new URI(defaultScheme + "://" + unknown); + + // Check to see if we are good now + if (uri.scheme.length > 0 && uri.authority.length > 0) + { + // It was just missing the scheme. + copyURI(uri); + forceEscape(); // Make sure we are properly encoded. + return true; + } + + // don't know what this is + this.initialize(); + return false; + } + + } // end URI class } // end package \ No newline at end of file diff --git a/webcam/com/adobe/net/URIEncodingBitmap.as b/webcam/com/adobe/net/URIEncodingBitmap.as index e85ac55..9c0239c 100644 --- a/webcam/com/adobe/net/URIEncodingBitmap.as +++ b/webcam/com/adobe/net/URIEncodingBitmap.as @@ -1,142 +1,142 @@ -/* - Adobe Systems Incorporated(r) Source Code License Agreement - Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. - - Please read this Source Code License Agreement carefully before using - the source code. - - Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, - no-charge, royalty-free, irrevocable copyright license, to reproduce, - prepare derivative works of, publicly display, publicly perform, and - distribute this source code and such derivative works in source or - object code form without any attribution requirements. - - The name "Adobe Systems Incorporated" must not be used to endorse or promote products - derived from the source code without prior written permission. - - You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and - against any loss, damage, claims or lawsuits, including attorney's - fees that arise or result from your use or distribution of the source - code. - - THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT - ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, - BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF - NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA - OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package com.adobe.net -{ - import flash.utils.ByteArray; - - /** - * This class implements an efficient lookup table for URI - * character escaping. This class is only needed if you - * create a derived class of URI to handle custom URI - * syntax. This class is used internally by URI. - * - * @langversion ActionScript 3.0 - * @playerversion Flash 9.0* - */ - public class URIEncodingBitmap extends ByteArray - { - /** - * Constructor. Creates an encoding bitmap using the given - * string of characters as the set of characters that need - * to be URI escaped. - * - * @langversion ActionScript 3.0 - * @playerversion Flash 9.0 - */ - public function URIEncodingBitmap(charsToEscape:String) : void - { - var i:int; - var data:ByteArray = new ByteArray(); - - // Initialize our 128 bits (16 bytes) to zero - for (i = 0; i < 16; i++) - this.writeByte(0); - - data.writeUTFBytes(charsToEscape); - data.position = 0; - - while (data.bytesAvailable) - { - var c:int = data.readByte(); - - if (c > 0x7f) - continue; // only escape low bytes - - var enc:int; - this.position = (c >> 3); - enc = this.readByte(); - enc |= 1 << (c & 0x7); - this.position = (c >> 3); - this.writeByte(enc); - } - } - - /** - * Based on the data table contained in this object, check - * if the given character should be escaped. - * - * @param char the character to be escaped. Only the first - * character in the string is used. Any other characters - * are ignored. - * - * @return the integer value of the raw UTF8 character. For - * example, if '%' is given, the return value is 37 (0x25). - * If the character given does not need to be escaped, the - * return value is zero. - * - * @langversion ActionScript 3.0 - * @playerversion Flash 9.0 - */ - public function ShouldEscape(char:String) : int - { - var data:ByteArray = new ByteArray(); - var c:int, mask:int; - - // write the character into a ByteArray so - // we can pull it out as a raw byte value. - data.writeUTFBytes(char); - data.position = 0; - c = data.readByte(); - - if (c & 0x80) - { - // don't escape high byte characters. It can make international - // URI's unreadable. We just want to escape characters that would - // make URI syntax ambiguous. - return 0; - } - else if ((c < 0x1f) || (c == 0x7f)) - { - // control characters must be escaped. - return c; - } - - this.position = (c >> 3); - mask = this.readByte(); - - if (mask & (1 << (c & 0x7))) - { - // we need to escape this, return the numeric value - // of the character - return c; - } - else - { - return 0; - } - } - } +/* + Adobe Systems Incorporated(r) Source Code License Agreement + Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. + + Please read this Source Code License Agreement carefully before using + the source code. + + Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable copyright license, to reproduce, + prepare derivative works of, publicly display, publicly perform, and + distribute this source code and such derivative works in source or + object code form without any attribution requirements. + + The name "Adobe Systems Incorporated" must not be used to endorse or promote products + derived from the source code without prior written permission. + + You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and + against any loss, damage, claims or lawsuits, including attorney's + fees that arise or result from your use or distribution of the source + code. + + THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT + ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF + NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA + OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.adobe.net +{ + import flash.utils.ByteArray; + + /** + * This class implements an efficient lookup table for URI + * character escaping. This class is only needed if you + * create a derived class of URI to handle custom URI + * syntax. This class is used internally by URI. + * + * @langversion ActionScript 3.0 + * @playerversion Flash 9.0* + */ + public class URIEncodingBitmap extends ByteArray + { + /** + * Constructor. Creates an encoding bitmap using the given + * string of characters as the set of characters that need + * to be URI escaped. + * + * @langversion ActionScript 3.0 + * @playerversion Flash 9.0 + */ + public function URIEncodingBitmap(charsToEscape:String) : void + { + var i:int; + var data:ByteArray = new ByteArray(); + + // Initialize our 128 bits (16 bytes) to zero + for (i = 0; i < 16; i++) + this.writeByte(0); + + data.writeUTFBytes(charsToEscape); + data.position = 0; + + while (data.bytesAvailable) + { + var c:int = data.readByte(); + + if (c > 0x7f) + continue; // only escape low bytes + + var enc:int; + this.position = (c >> 3); + enc = this.readByte(); + enc |= 1 << (c & 0x7); + this.position = (c >> 3); + this.writeByte(enc); + } + } + + /** + * Based on the data table contained in this object, check + * if the given character should be escaped. + * + * @param char the character to be escaped. Only the first + * character in the string is used. Any other characters + * are ignored. + * + * @return the integer value of the raw UTF8 character. For + * example, if '%' is given, the return value is 37 (0x25). + * If the character given does not need to be escaped, the + * return value is zero. + * + * @langversion ActionScript 3.0 + * @playerversion Flash 9.0 + */ + public function ShouldEscape(char:String) : int + { + var data:ByteArray = new ByteArray(); + var c:int, mask:int; + + // write the character into a ByteArray so + // we can pull it out as a raw byte value. + data.writeUTFBytes(char); + data.position = 0; + c = data.readByte(); + + if (c & 0x80) + { + // don't escape high byte characters. It can make international + // URI's unreadable. We just want to escape characters that would + // make URI syntax ambiguous. + return 0; + } + else if ((c < 0x1f) || (c == 0x7f)) + { + // control characters must be escaped. + return c; + } + + this.position = (c >> 3); + mask = this.readByte(); + + if (mask & (1 << (c & 0x7))) + { + // we need to escape this, return the numeric value + // of the character + return c; + } + else + { + return 0; + } + } + } } \ No newline at end of file diff --git a/webcam/com/adobe/net/proxies/RFC2817Socket.as b/webcam/com/adobe/net/proxies/RFC2817Socket.as index 3657da4..c52ee39 100644 --- a/webcam/com/adobe/net/proxies/RFC2817Socket.as +++ b/webcam/com/adobe/net/proxies/RFC2817Socket.as @@ -1,204 +1,204 @@ -/* - Adobe Systems Incorporated(r) Source Code License Agreement - Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. - - Please read this Source Code License Agreement carefully before using - the source code. - - Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, - no-charge, royalty-free, irrevocable copyright license, to reproduce, - prepare derivative works of, publicly display, publicly perform, and - distribute this source code and such derivative works in source or - object code form without any attribution requirements. - - The name "Adobe Systems Incorporated" must not be used to endorse or promote products - derived from the source code without prior written permission. - - You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and - against any loss, damage, claims or lawsuits, including attorney's - fees that arise or result from your use or distribution of the source - code. - - THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT - ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, - BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF - NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA - OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -package com.adobe.net.proxies -{ - import flash.events.Event; - import flash.events.IOErrorEvent; - import flash.events.ProgressEvent; - import flash.net.Socket; - - /** - * This class allows TCP socket connections through HTTP proxies in accordance with - * RFC 2817: - * - * ftp://ftp.rfc-editor.org/in-notes/rfc2817.txt - * - * It can also be used to make direct connections to a destination, as well. If you - * pass the host and port into the constructor, no proxy will be used. You can also - * call connect, passing in the host and the port, and if you didn't set the proxy - * info, a direct connection will be made. A proxy is only used after you have called - * the setProxyInfo function. - * - * The connection to and negotiation with the proxy is completely hidden. All the - * same events are thrown whether you are using a proxy or not, and the data you - * receive from the target server will look exact as it would if you were connected - * to it directly rather than through a proxy. - * - * @author Christian Cantrell - * - **/ - public class RFC2817Socket - extends Socket - { - private var proxyHost:String = null; - private var host:String = null; - private var proxyPort:int = 0; - private var port:int = 0; - private var deferredEventHandlers:Object = new Object(); - private var buffer:String = new String(); - - /** - * Construct a new RFC2817Socket object. If you pass in the host and the port, - * no proxy will be used. If you want to use a proxy, instantiate with no - * arguments, call setProxyInfo, then call connect. - **/ - public function RFC2817Socket(host:String = null, port:int = 0) - { - if (host != null && port != 0) - { - super(host, port); - } - } - - /** - * Set the proxy host and port number. Your connection will only proxied if - * this function has been called. - **/ - public function setProxyInfo(host:String, port:int):void - { - this.proxyHost = host; - this.proxyPort = port; - - var deferredSocketDataHandler:Object = this.deferredEventHandlers[ProgressEvent.SOCKET_DATA]; - var deferredConnectHandler:Object = this.deferredEventHandlers[Event.CONNECT]; - - if (deferredSocketDataHandler != null) - { - super.removeEventListener(ProgressEvent.SOCKET_DATA, deferredSocketDataHandler.listener, deferredSocketDataHandler.useCapture); - } - - if (deferredConnectHandler != null) - { - super.removeEventListener(Event.CONNECT, deferredConnectHandler.listener, deferredConnectHandler.useCapture); - } - } - - /** - * Connect to the specified host over the specified port. If you want your - * connection proxied, call the setProxyInfo function first. - **/ - public override function connect(host:String, port:int):void - { - if (this.proxyHost == null) - { - this.redirectConnectEvent(); - this.redirectSocketDataEvent(); - super.connect(host, port); - } - else - { - this.host = host; - this.port = port; - super.addEventListener(Event.CONNECT, this.onConnect); - super.addEventListener(ProgressEvent.SOCKET_DATA, this.onSocketData); - super.connect(this.proxyHost, this.proxyPort); - } - } - - private function onConnect(event:Event):void - { - this.writeUTFBytes("CONNECT "+this.host+":"+this.port+" HTTP/1.1\n\n"); - this.flush(); - this.redirectConnectEvent(); - } - - private function onSocketData(event:ProgressEvent):void - { - while (this.bytesAvailable != 0) - { - this.buffer += this.readUTFBytes(1); - if (this.buffer.search(/\r?\n\r?\n$/) != -1) - { - this.checkResponse(event); - break; - } - } - } - - private function checkResponse(event:ProgressEvent):void - { - var responseCode:String = this.buffer.substr(this.buffer.indexOf(" ")+1, 3); - - if (responseCode.search(/^2/) == -1) - { - var ioError:IOErrorEvent = new IOErrorEvent(IOErrorEvent.IO_ERROR); - ioError.text = "Error connecting to the proxy ["+this.proxyHost+"] on port ["+this.proxyPort+"]: " + this.buffer; - this.dispatchEvent(ioError); - } - else - { - this.redirectSocketDataEvent(); - this.dispatchEvent(new Event(Event.CONNECT)); - if (this.bytesAvailable > 0) - { - this.dispatchEvent(event); - } - } - this.buffer = null; - } - - private function redirectConnectEvent():void - { - super.removeEventListener(Event.CONNECT, onConnect); - var deferredEventHandler:Object = this.deferredEventHandlers[Event.CONNECT]; - if (deferredEventHandler != null) - { - super.addEventListener(Event.CONNECT, deferredEventHandler.listener, deferredEventHandler.useCapture, deferredEventHandler.priority, deferredEventHandler.useWeakReference); - } - } - - private function redirectSocketDataEvent():void - { - super.removeEventListener(ProgressEvent.SOCKET_DATA, onSocketData); - var deferredEventHandler:Object = this.deferredEventHandlers[ProgressEvent.SOCKET_DATA]; - if (deferredEventHandler != null) - { - super.addEventListener(ProgressEvent.SOCKET_DATA, deferredEventHandler.listener, deferredEventHandler.useCapture, deferredEventHandler.priority, deferredEventHandler.useWeakReference); - } - } - - public override function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int=0.0, useWeakReference:Boolean=false):void - { - if (type == Event.CONNECT || type == ProgressEvent.SOCKET_DATA) - { - this.deferredEventHandlers[type] = {listener:listener,useCapture:useCapture, priority:priority, useWeakReference:useWeakReference}; - } - else - { - super.addEventListener(type, listener, useCapture, priority, useWeakReference); - } - } - } +/* + Adobe Systems Incorporated(r) Source Code License Agreement + Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved. + + Please read this Source Code License Agreement carefully before using + the source code. + + Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable copyright license, to reproduce, + prepare derivative works of, publicly display, publicly perform, and + distribute this source code and such derivative works in source or + object code form without any attribution requirements. + + The name "Adobe Systems Incorporated" must not be used to endorse or promote products + derived from the source code without prior written permission. + + You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and + against any loss, damage, claims or lawsuits, including attorney's + fees that arise or result from your use or distribution of the source + code. + + THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT + ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF + NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA + OR ITS SUPPLIERS 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 SOURCE CODE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +package com.adobe.net.proxies +{ + import flash.events.Event; + import flash.events.IOErrorEvent; + import flash.events.ProgressEvent; + import flash.net.Socket; + + /** + * This class allows TCP socket connections through HTTP proxies in accordance with + * RFC 2817: + * + * ftp://ftp.rfc-editor.org/in-notes/rfc2817.txt + * + * It can also be used to make direct connections to a destination, as well. If you + * pass the host and port into the constructor, no proxy will be used. You can also + * call connect, passing in the host and the port, and if you didn't set the proxy + * info, a direct connection will be made. A proxy is only used after you have called + * the setProxyInfo function. + * + * The connection to and negotiation with the proxy is completely hidden. All the + * same events are thrown whether you are using a proxy or not, and the data you + * receive from the target server will look exact as it would if you were connected + * to it directly rather than through a proxy. + * + * @author Christian Cantrell + * + **/ + public class RFC2817Socket + extends Socket + { + private var proxyHost:String = null; + private var host:String = null; + private var proxyPort:int = 0; + private var port:int = 0; + private var deferredEventHandlers:Object = new Object(); + private var buffer:String = new String(); + + /** + * Construct a new RFC2817Socket object. If you pass in the host and the port, + * no proxy will be used. If you want to use a proxy, instantiate with no + * arguments, call setProxyInfo, then call connect. + **/ + public function RFC2817Socket(host:String = null, port:int = 0) + { + if (host != null && port != 0) + { + super(host, port); + } + } + + /** + * Set the proxy host and port number. Your connection will only proxied if + * this function has been called. + **/ + public function setProxyInfo(host:String, port:int):void + { + this.proxyHost = host; + this.proxyPort = port; + + var deferredSocketDataHandler:Object = this.deferredEventHandlers[ProgressEvent.SOCKET_DATA]; + var deferredConnectHandler:Object = this.deferredEventHandlers[Event.CONNECT]; + + if (deferredSocketDataHandler != null) + { + super.removeEventListener(ProgressEvent.SOCKET_DATA, deferredSocketDataHandler.listener, deferredSocketDataHandler.useCapture); + } + + if (deferredConnectHandler != null) + { + super.removeEventListener(Event.CONNECT, deferredConnectHandler.listener, deferredConnectHandler.useCapture); + } + } + + /** + * Connect to the specified host over the specified port. If you want your + * connection proxied, call the setProxyInfo function first. + **/ + public override function connect(host:String, port:int):void + { + if (this.proxyHost == null) + { + this.redirectConnectEvent(); + this.redirectSocketDataEvent(); + super.connect(host, port); + } + else + { + this.host = host; + this.port = port; + super.addEventListener(Event.CONNECT, this.onConnect); + super.addEventListener(ProgressEvent.SOCKET_DATA, this.onSocketData); + super.connect(this.proxyHost, this.proxyPort); + } + } + + private function onConnect(event:Event):void + { + this.writeUTFBytes("CONNECT "+this.host+":"+this.port+" HTTP/1.1\n\n"); + this.flush(); + this.redirectConnectEvent(); + } + + private function onSocketData(event:ProgressEvent):void + { + while (this.bytesAvailable != 0) + { + this.buffer += this.readUTFBytes(1); + if (this.buffer.search(/\r?\n\r?\n$/) != -1) + { + this.checkResponse(event); + break; + } + } + } + + private function checkResponse(event:ProgressEvent):void + { + var responseCode:String = this.buffer.substr(this.buffer.indexOf(" ")+1, 3); + + if (responseCode.search(/^2/) == -1) + { + var ioError:IOErrorEvent = new IOErrorEvent(IOErrorEvent.IO_ERROR); + ioError.text = "Error connecting to the proxy ["+this.proxyHost+"] on port ["+this.proxyPort+"]: " + this.buffer; + this.dispatchEvent(ioError); + } + else + { + this.redirectSocketDataEvent(); + this.dispatchEvent(new Event(Event.CONNECT)); + if (this.bytesAvailable > 0) + { + this.dispatchEvent(event); + } + } + this.buffer = null; + } + + private function redirectConnectEvent():void + { + super.removeEventListener(Event.CONNECT, onConnect); + var deferredEventHandler:Object = this.deferredEventHandlers[Event.CONNECT]; + if (deferredEventHandler != null) + { + super.addEventListener(Event.CONNECT, deferredEventHandler.listener, deferredEventHandler.useCapture, deferredEventHandler.priority, deferredEventHandler.useWeakReference); + } + } + + private function redirectSocketDataEvent():void + { + super.removeEventListener(ProgressEvent.SOCKET_DATA, onSocketData); + var deferredEventHandler:Object = this.deferredEventHandlers[ProgressEvent.SOCKET_DATA]; + if (deferredEventHandler != null) + { + super.addEventListener(ProgressEvent.SOCKET_DATA, deferredEventHandler.listener, deferredEventHandler.useCapture, deferredEventHandler.priority, deferredEventHandler.useWeakReference); + } + } + + public override function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int=0.0, useWeakReference:Boolean=false):void + { + if (type == Event.CONNECT || type == ProgressEvent.SOCKET_DATA) + { + this.deferredEventHandlers[type] = {listener:listener,useCapture:useCapture, priority:priority, useWeakReference:useWeakReference}; + } + else + { + super.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + } + } } \ No newline at end of file -- cgit v1.2.3-70-g09d2