#pike __REAL_VERSION__ |
|
|
|
|
|
|
#pragma strict_types |
|
|
string scheme; |
|
|
|
string authority; |
|
|
string path; |
|
|
string query; |
|
|
string fragment; |
|
|
string host, user, password; |
|
|
|
int port; |
|
|
this_program base_uri; |
|
|
|
|
|
|
string raw_uri; |
|
#ifdef STANDARDS_URI_DEBUG |
#define DEBUG(X, Y ...) werror("Standards.URI: "+X+"\n", Y) |
#else |
#define DEBUG(X, Y ...) |
#endif |
|
|
|
|
|
|
|
protected void parse_authority() |
{ |
string host_port = authority; |
|
if(sscanf(authority, "%[^@]@%s", string userinfo, host_port) == 2) |
{ |
|
sscanf(userinfo, "%[^:]:%s", user, password); |
DEBUG("parse_authority(): user=%O, password=%O", user, password); |
} |
if(scheme) |
port = Protocols.Ports.tcp[scheme]; |
|
if (has_prefix(host_port, "[")) { |
|
|
sscanf(host_port, "[%s]%*[:]%d", host, port); |
} else { |
|
|
sscanf(host_port, "%[^:]%*[:]%d", host, port); |
} |
DEBUG("parse_authority(): host=%O, port=%O", host, port); |
} |
|
|
protected void inherit_properties(this_program uri) |
{ |
sprintf_cache = ([]); |
authority = uri->authority; |
scheme = uri->scheme; |
user = uri->user; password = uri->password; |
host = uri->host; query = uri->query; |
port = uri->port; |
path = uri->path; fragment = uri->fragment; |
} |
|
|
|
|
int `==(mixed something) |
{ |
if( !objectp( something ) || object_program(something) < this_program ) |
return false; |
Standards.URI other = [object(Standards.URI)]something; |
|
|
|
return |
((host |
&& other->host |
&& lower_case(other->host) == lower_case(host) |
&& other->port == port |
&& other->user == user |
&& other->password == password) |
|| (other->authority == authority)) |
&& other->path == path |
&& other->query == query |
&& other->scheme == scheme |
&& other->fragment == fragment; |
} |
|
string combine_uri_path(string base, string rel) |
{ |
string buf = rel; |
array segments; |
|
|
|
|
|
|
|
|
|
|
if (!has_prefix(rel, "/")) { |
|
|
|
|
segments = base/"/"; |
buf = segments[..<1]*"/"+"/"; |
|
|
buf += rel; |
} |
segments = buf / "/"; |
|
|
|
for(int i=0; i<sizeof(segments)-1; i++) |
if(segments[i]==".") |
segments[i]=0; |
|
segments -= ({ 0 }); |
|
|
|
if(segments[-1]==".") |
segments=segments[..<1]+({""}); |
|
|
|
|
|
|
int found_pattern; |
do |
{ |
found_pattern=0; |
if(sizeof(segments)<3) |
continue; |
for(int i=0; i<sizeof(segments)-2; i++) |
{ |
if(segments[i]!=".." && segments[i]!="" && segments[i+1]=="..") |
{ |
segments = segments[..i-1]+segments[i+2..]; |
found_pattern=1; |
continue; |
} |
} |
|
} while(found_pattern); |
|
|
|
|
if(sizeof(segments)>=2) |
if(segments[-2]!=".." && segments[-1]=="..") |
segments=segments[..<2]+({""}); |
|
|
|
|
|
|
|
|
segments -= ({ ".." }); |
return segments * "/"; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void reparse_uri(this_program|string|void base_uri) |
{ |
string uri = raw_uri; |
if(stringp(base_uri)) |
{ |
DEBUG("cloning base URI %O", base_uri); |
this::base_uri = this_program(base_uri); |
} |
else |
this::base_uri = [object(this_program)]base_uri; |
|
|
|
|
|
|
|
|
|
|
|
|
if((!uri || uri == "") && this::base_uri) |
{ |
DEBUG("Path is empty -- Inherit entire base URI " |
"as per RFC 2396, §5.2 step 2. Done!"); |
inherit_properties(this::base_uri); |
return; |
} |
|
|
|
|
if( sscanf(uri, "%s#%s", uri, fragment)==2 ) |
{ |
DEBUG("Found fragment %O", fragment); |
if( !sizeof(uri) ) |
{ |
DEBUG("Fragment only. Using entire base URI, except fragment."); |
if( !this::base_uri ) |
error("fragment only URI lacking base URI.\n"); |
string f = fragment; |
inherit_properties(this::base_uri); |
fragment = f; |
return; |
} |
} |
|
|
|
if(sscanf(uri, "%[A-Za-z0-9+.-]:%s", scheme, uri) < 2) |
{ |
scheme = 0; |
if(!this::base_uri) |
error("Standards.URI: got a relative URI (no scheme) lacking a base_uri!\n"); |
} else { |
|
|
|
|
|
|
|
scheme = lower_case(scheme); |
} |
DEBUG("Found scheme %O", scheme); |
|
|
|
if( !has_prefix(uri, "//") && !scheme && this::base_uri?->scheme && |
!sizeof(this::base_uri->authority) && |
!sizeof(this::base_uri->path)) |
{ |
DEBUG("DWIM authority: %O\n", uri); |
uri = "//"+uri; |
} |
|
|
|
|
|
if(sscanf(uri, "//%[^/]%s", authority, uri)) |
{ |
DEBUG("Found authority %O", authority); |
int q = search(authority, "?", search(authority, "@")+1); |
if (q >= 0) { |
|
|
|
|
uri = authority[q..] + uri; |
authority = authority[..q-1]; |
DEBUG("Adjusted authority %O", authority); |
} |
} |
|
|
|
|
sscanf(uri, "%s?%s", uri, query); |
DEBUG("Found query %O", query); |
|
|
|
if ((uri == "") && !scheme && !authority && (this::base_uri)) { |
|
path = this::base_uri->path; |
} else { |
path = uri; |
} |
DEBUG("Found path %O", path); |
|
|
|
|
|
if(scheme) |
{ |
if(authority) |
parse_authority(); |
|
DEBUG("Scheme found! RFC 2396, §5.2, step 3 " |
"says we're absolute. Done!"); |
sprintf_cache['s'] = raw_uri; |
return; |
} |
scheme = this::base_uri->scheme; |
DEBUG("Inherited scheme %O from base URI", scheme); |
|
if(authority) |
parse_authority(); |
|
|
|
|
|
|
|
if(!authority || !sizeof(authority)) |
{ |
authority = this::base_uri->authority; |
DEBUG("Inherited authority %O from base URI", authority); |
if (authority) |
parse_authority(); |
|
|
|
|
|
|
if(!has_prefix(path, "/")) |
{ |
|
|
|
|
|
|
DEBUG("Combining base path %O with path %O => %O", |
this::base_uri->path, path, |
combine_uri_path(this::base_uri->path, path)); |
path = combine_uri_path(this::base_uri->path, path); |
|
} |
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create(this_program|string uri, |
this_program|string|void base_uri) |
{ |
DEBUG("create(%O, %O) called!", uri, base_uri); |
sprintf_cache = ([]); |
if(stringp(uri)) |
raw_uri = [string]uri; |
else if(objectp(uri)) |
raw_uri = uri->raw_uri; |
|
reparse_uri(base_uri); |
} |
|
|
|
|
|
|
|
mixed `->=(string property, mixed value) { return `[]=(property, value); } |
mixed `[]=(string property, mixed value) |
{ |
DEBUG("`[]=(%O, %O)", property, value); |
sprintf_cache = ([]); |
switch(property) |
{ |
case "user": |
case "password": |
case "host": |
if(!stringp(value) && value!=0) |
error("%s value not string.\n", property); |
case "port": |
::`[]=(property, value); |
authority = (user ? user + (password ? ":" + password : "") + "@" : "") + |
(host?(has_value(host, ":")?("["+host+"]"):host):"") + |
(port != Protocols.Ports.tcp[scheme] ? ":" + port : ""); |
return value; |
|
case "authority": |
if(!stringp(value) && value!=0) |
error("authority value not string.\n"); |
authority = [string]value; |
parse_authority(); |
return value; |
|
case "base_uri": |
if(!stringp(value) && value!=0 && !objectp(value)) |
error("base_uri value neither object nor string.\n"); |
reparse_uri([object(this_program)|string]value); |
return base_uri; |
|
case "query": |
variables = 0; |
return ::`[]=(property, value); |
|
case "scheme": |
|
|
|
|
|
|
|
if(!stringp(value) && value!=0) |
error("scheme value not string.\n"); |
value = lower_case([string]value); |
|
|
default: |
return ::`[]=(property, value); |
} |
} |
|
|
|
|
|
protected string|mapping cast(string to) |
{ |
switch(to) |
{ |
case "string": |
return _sprintf('s'); |
case "mapping": |
array(string) i = ({ "scheme", "authority", "user", "password", |
"host", "port", |
"path", "query", "fragment", |
"raw_uri", "base_uri", }); |
return mkmapping(i, rows(this, i)); |
} |
return UNDEFINED; |
} |
|
|
string get_path_query() |
{ |
return (path||"") + (query ? "?" + query : ""); |
} |
|
protected mapping(string:string) variables; |
|
|
mapping(string:string) get_query_variables() { |
if( variables ) return variables; |
if(!query) return ([]); |
|
variables = ([]); |
foreach( query/"&";; string pair ) |
{ |
if( sscanf( pair, "%s=%s", string var, string val )==2 ) |
variables[var] = val; |
else |
variables[pair] = 0; |
} |
|
return variables; |
} |
|
|
void set_query_variables(mapping(string:string) vars) { |
sprintf_cache = ([]); |
variables = vars; |
if(!sizeof(vars)) |
query = 0; |
else |
{ |
query = ""; |
foreach( vars; string var; string val ) |
{ |
if( sizeof(query) ) |
query += "&"; |
query += var; |
if( val ) |
query += "=" + val; |
} |
} |
} |
|
|
|
void add_query_variable(string name, string value) { |
set_query_variables(get_query_variables()+([name:value])); |
} |
|
|
|
|
void add_query_variables(mapping(string:string) vars) { |
set_query_variables(get_query_variables()|vars); |
} |
|
|
|
|
|
protected constant url_non_corresponding = enumerate(0x21) + |
enumerate(0x81,1,0x7f); |
protected constant url_unsafe = ({ '<', '>', '"', '#', '%', '{', '}', |
'|', '\\', '^', '~', '[', ']', '`' }); |
protected constant url_reserved = ({ ';', '/', '?', ':', '@', '=', '&' }); |
|
|
protected constant url_chars = url_non_corresponding + url_unsafe + |
url_reserved + ({ '+', '\'' }); |
protected constant url_from = sprintf("%c", url_chars[*]); |
protected constant url_to = sprintf("%%%02x", url_chars[*]); |
|
string http_encode(string in) |
{ |
|
|
return replace(in, [array(string)]url_from, [array(string)]url_to); |
} |
|
|
string get_http_query() { |
return query; |
} |
|
|
|
string get_http_path_query() { |
string q = get_http_query(); |
return http_encode(((path||"")/"/")[*])*"/" + (q?"?"+q:""); |
} |
|
int __hash() { return hash_value(_sprintf('s')); } |
|
private mapping(int:string) sprintf_cache = ([]); |
string _sprintf(int how, mapping|void args) |
{ |
if( how == 't' ) return "Standards.URI"; |
if( string res = sprintf_cache[how] ) |
return res; |
string look, _host = host, getstring; |
if(how == 'x' && _host) |
_host = lower_case(_host); |
getstring = (path||"") + (query ? "?" + query : ""); |
if(args && args->flag_left) |
return getstring; |
look = |
(scheme?(scheme + ":"):"") + |
(authority ? |
"//" + |
(user ? user + (password ? ":" + password : "") + "@" : "") + |
(_host?(has_value(_host, ":")?("["+_host+"]"):_host):"") + |
(port != Protocols.Ports.tcp[scheme] ? ":" + port : "") : ("")) + |
getstring + |
(fragment ? "#" + fragment : ""); |
|
if(how == 'O') |
look=sprintf("URI(%q)", look); |
return sprintf_cache[how]=look; |
} |
|
|
|
mapping(string:string|int|this_program) _encode() |
{ |
#define P(X) #X:X |
return ([ |
P(scheme), |
P(authority), |
P(path), |
P(query), |
P(fragment), |
P(host), |
P(user), |
P(password), |
P(port), |
P(base_uri), |
P(raw_uri), |
|
]); |
#undef P |
} |
|
|
|
void _decode(mapping m) |
{ |
foreach(m; mixed index; mixed value) |
::`[]=(index, value); |
} |
|
#if 0 |
|
string quote(string s) |
{ |
return replace(s, |
({ "\000", "\001", "\002", "\003", "\004", "\005", "\006", |
"\007", "\010", "\011", "\012", "\013", "\014", "\015", |
"\016", "\017", "\020", "\021", "\022", "\023", "\024", |
"\025", "\026", "\027", "\030", "\031", "\032", "\033", |
"\034", "\035", "\036", "\037", "\200", "\201", "\202", |
"\203", "\204", "\205", "\206", "\207", "\210", "\211", |
"\212", "\213", "\214", "\215", "\216", "\217", "\220", |
"\221", "\222", "\223", "\224", "\225", "\226", "\227", |
"\230", "\231", "\232", "\233", "\234", "\235", "\236", |
"\237", " ", "%", "'", "\"" }), |
({ "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", |
"%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", |
"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", |
"%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", |
"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", |
"%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", |
"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", |
"%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", |
"%20", "%25", "%27", "%22"})); |
} |
#endif |
|
|