3709192002-03-20Martin Nilsson #pike __REAL_VERSION__
61b2e92001-01-11Johan Schön //! This class implements URI parsing and resolving of relative references to
dd534e2005-03-30Henrik Grubbström (Grubba) //! absolute form, as defined in RFC 2396 and RFC 3986.
61b2e92001-01-11Johan Schön  // Implemented by Johan Sundström and Johan Schön.
33593f2001-01-13Henrik Grubbström (Grubba)  #pragma strict_types
61b2e92001-01-11Johan Schön  //! Scheme component of URI string scheme;
81988d2001-06-16Per Hedbor //! Authority component of URI (formerly called net_loc, from RFC 2396 //! known as authority)
61b2e92001-01-11Johan Schön string authority; //! Path component of URI. May be empty, but not undefined. string path; //! Query component of URI. May be 0 if not present. string query; //! The fragment part of URI. May be 0 if not present. string fragment; //! Certain classes of URI (e.g. URL) may have these defined string host, user, password;
81988d2001-06-16Per Hedbor //! If no port number is present in URI, but the scheme used has a //! default port number, this number is put here.
61b2e92001-01-11Johan Schön int port; //! The base URI object, if present
563bd72004-01-11Martin Nilsson this_program base_uri;
61b2e92001-01-11Johan Schön 
f800812001-01-30Johan Sundström // URI hacker docs: // This string is the raw uri the object was instantiated from in the // first place. We save it here for the sole purpose of being able to // replace the base URI, hence also needing to reresolve all of our // properties with respect to that change.
61b2e92001-01-11Johan Schön string raw_uri; #ifdef STANDARDS_URI_DEBUG
d39bb02001-01-22Johan Sundström #define DEBUG(X, Y ...) werror("Standards.URI: "+X+"\n", Y)
61b2e92001-01-11Johan Schön #else
d39bb02001-01-22Johan Sundström #define DEBUG(X, Y ...)
61b2e92001-01-11Johan Schön #endif
10fdad2009-09-17Henrik Grubbström (Grubba) // FIXME: What about decoding of Percent-Encoding (RFC3986 2.1)? // cf pct-encoded in the functions below.
61b2e92001-01-11Johan Schön // Parse authority component (according to RFC 1738, § 3.1)
03ef172005-03-30Henrik Grubbström (Grubba) // Updated to RFC 3986 $ 3.2.
b75ebc2010-05-23Henrik Grubbström (Grubba) // NOTE: Censors the userinfo from the @[authority] variable.
9eaf1d2008-06-28Martin Nilsson protected void parse_authority()
f800812001-01-30Johan Sundström {
303d9a2013-08-28Henrik Grubbström (Grubba)  string host_port = authority;
03ef172005-03-30Henrik Grubbström (Grubba)  // authority = [ userinfo "@" ] host [ ":" port ]
f105902016-02-16Henrik Grubbström (Grubba)  array(string) a = authority/"@"; if (sizeof(a) > 1) { host_port = a[-1]; string userinfo = a[..<1] * "@";
03ef172005-03-30Henrik Grubbström (Grubba)  // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) sscanf(userinfo, "%[^:]:%s", user, password); // user info present
d39bb02001-01-22Johan Sundström  DEBUG("parse_authority(): user=%O, password=%O", user, password);
61b2e92001-01-11Johan Schön  } if(scheme)
6296322003-03-08Martin Nilsson  port = Protocols.Ports.tcp[scheme]; // Set a good default á la RFC 1700
03ef172005-03-30Henrik Grubbström (Grubba)  // host = IP-literal / IPv4address / reg-name
303d9a2013-08-28Henrik Grubbström (Grubba)  if (has_prefix(host_port, "[")) {
03ef172005-03-30Henrik Grubbström (Grubba)  // IP-literal = "[" ( IPv6address / IPvFuture ) "]" // IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
303d9a2013-08-28Henrik Grubbström (Grubba)  sscanf(host_port, "[%s]%*[:]%d", host, port);
03ef172005-03-30Henrik Grubbström (Grubba)  } else {
10fdad2009-09-17Henrik Grubbström (Grubba)  // IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet // reg-name = *( unreserved / pct-encoded / sub-delims )
303d9a2013-08-28Henrik Grubbström (Grubba)  sscanf(host_port, "%[^:]%*[:]%d", host, port);
03ef172005-03-30Henrik Grubbström (Grubba)  }
d39bb02001-01-22Johan Sundström  DEBUG("parse_authority(): host=%O, port=%O", host, port);
61b2e92001-01-11Johan Schön } // Inherit all properties except raw_uri and base_uri from the URI uri. :-)
9eaf1d2008-06-28Martin Nilsson protected void inherit_properties(this_program uri)
f800812001-01-30Johan Sundström {
4274152014-03-24Per Hedbor  sprintf_cache = ([]);
61b2e92001-01-11Johan Schön  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; } //! Compare this URI to something, in a canonical way. //! @param something //! Compare the URI to this int `==(mixed something) {
4274152014-03-24Per Hedbor  if( !objectp( something ) || object_program(something) < this_program ) return false; Standards.URI other = [object(Standards.URI)]something; // For protocols with host/port/user/password we do lower_case on // the host when comparing, and use the port according to RFC 2396 // section 6. 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;
61b2e92001-01-11Johan Schön } string combine_uri_path(string base, string rel) {
9ae9762014-11-20Henrik Grubbström (Grubba)  string buf = rel; array segments;
f800812001-01-30Johan Sundström 
9ae9762014-11-20Henrik Grubbström (Grubba)  // RFC 2396, §5.2.5: // If the path component begins with a slash character ("/"), // then the reference is an absolute-path and we skip to step 7. // // NB: The RFC does not take into account that even absolute // paths may contain segments of ".." and ".", and this // function may get called by external code that wants // to get rid of them. We simply ignore the base URI's // path if rel is absolute. if (!has_prefix(rel, "/")) { // RFC 2396, §5.2.6: // a) All but the last segment of the base URI's path component is // copied to the buffer. In other words, any characters after the // last (right-most) slash character, if any, are excluded. segments = base/"/"; buf = segments[..<1]*"/"+"/"; // b) The reference's path component is appended to the buffer string. buf += rel; }
61b2e92001-01-11Johan Schön  segments = buf / "/";
f800812001-01-30Johan Sundström 
61b2e92001-01-11Johan Schön  // c) All occurrences of "./", where "." is a complete path segment, // are removed from the buffer string. for(int i=0; i<sizeof(segments)-1; i++) if(segments[i]==".") segments[i]=0;
f800812001-01-30Johan Sundström  segments -= ({ 0 });
61b2e92001-01-11Johan Schön  // d) If the buffer string ends with "." as a complete path segment, // that "." is removed. if(segments[-1]==".")
8a531a2006-11-04Martin Nilsson  segments=segments[..<1]+({""});
61b2e92001-01-11Johan Schön  // e) All occurrences of "<segment>/../", where <segment> is a // complete path segment not equal to "..", are removed from the // buffer string. Removal of these path segments is performed // iteratively, removing the leftmost matching pattern on each // iteration, until no matching pattern remains. 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); // f) If the buffer string ends with "<segment>/..", where <segment> // is a complete path segment not equal to "..", that // "<segment>/.." is removed. if(sizeof(segments)>=2) if(segments[-2]!=".." && segments[-1]=="..")
8a531a2006-11-04Martin Nilsson  segments=segments[..<2]+({""});
61b2e92001-01-11Johan Schön  // g) If the resulting buffer string still begins with one or more // complete path segments of "..", then the reference is // considered to be in error. Implementations may handle this // error by retaining these components in the resolved path (i.e., // treating them as part of the final URI), by removing them from // the resolved path (i.e., discarding relative levels above the // root), or by avoiding traversal of the reference.
2374322011-02-02Henrik Grubbström (Grubba)  segments -= ({ ".." });
61b2e92001-01-11Johan Schön  return segments * "/"; } //! @decl void reparse_uri() //! @decl void reparse_uri(URI base_uri) //! @decl void reparse_uri(string base_uri) //! Reparse the URI with respect to a new base URI. If //! no base_uri was supplied, the old base_uri is thrown away. //! The resolving is performed according to the guidelines //! outlined by RFC 2396, Uniform Resource Identifiers (URI): Generic Syntax. //! @param base_uri
f800812001-01-30Johan Sundström //! Set the new base URI to this.
9205162012-05-17Martin Nilsson //! @throws //! An exception is thrown if the @[uri] is a relative URI or only a //! fragment, and missing a @[base_uri].
563bd72004-01-11Martin Nilsson void reparse_uri(this_program|string|void base_uri)
61b2e92001-01-11Johan Schön { string uri = raw_uri;
33593f2001-01-13Henrik Grubbström (Grubba)  if(stringp(base_uri))
61b2e92001-01-11Johan Schön  {
d39bb02001-01-22Johan Sundström  DEBUG("cloning base URI %O", base_uri);
8e06a32014-09-30Martin Nilsson  this::base_uri = this_program(base_uri); // create a new URI object
61b2e92001-01-11Johan Schön  } else
8e06a32014-09-30Martin Nilsson  this::base_uri = [object(this_program)]base_uri;
61b2e92001-01-11Johan Schön  // RFC 2396, §5.2: // 1) The URI reference is parsed into the potential four components and // fragment identifier, as described in Section 4.3.
10fdad2009-09-17Henrik Grubbström (Grubba)  // URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
61b2e92001-01-11Johan Schön  // 2) If the path component is empty and the scheme, authority, and // query components are undefined, then it is a reference to the // current document and we are done. Otherwise, the reference URI's // query and fragment components are defined as found (or not found) // within the URI reference and not inherited from the base URI. // (Doing this at once saves us some useless parsing efforts.)
8e06a32014-09-30Martin Nilsson  if((!uri || uri == "") && this::base_uri)
f800812001-01-30Johan Sundström  {
d39bb02001-01-22Johan Sundström  DEBUG("Path is empty -- Inherit entire base URI " "as per RFC 2396, §5.2 step 2. Done!");
8e06a32014-09-30Martin Nilsson  inherit_properties(this::base_uri);
61b2e92001-01-11Johan Schön  return; } // Parse fragment identifier
10fdad2009-09-17Henrik Grubbström (Grubba)  // fragment = *( pchar / "/" / "?" ) // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
0545102011-02-27Martin Nilsson  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.");
8e06a32014-09-30Martin Nilsson  if( !this::base_uri )
c357c42011-04-01Martin Nilsson  error("fragment only URI lacking base URI.\n");
0545102011-02-27Martin Nilsson  string f = fragment;
8e06a32014-09-30Martin Nilsson  inherit_properties(this::base_uri);
0545102011-02-27Martin Nilsson  fragment = f; return; } }
61b2e92001-01-11Johan Schön  // Parse scheme
10fdad2009-09-17Henrik Grubbström (Grubba)  // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
61b2e92001-01-11Johan Schön  if(sscanf(uri, "%[A-Za-z0-9+.-]:%s", scheme, uri) < 2) { scheme = 0;
8e06a32014-09-30Martin Nilsson  if(!this::base_uri)
61b2e92001-01-11Johan Schön  error("Standards.URI: got a relative URI (no scheme) lacking a base_uri!\n");
03ef172005-03-30Henrik Grubbström (Grubba)  } else { /* RFC 3986 §3.1 * * An implementation should accept uppercase letters as equivalent * to lowercase in scheme names (e.g., allow "HTTP" as well as * "http") for the sake of robustness but should only produce * lowercase scheme names for consistency. */ scheme = lower_case(scheme);
61b2e92001-01-11Johan Schön  }
d39bb02001-01-22Johan Sundström  DEBUG("Found scheme %O", scheme);
61b2e92001-01-11Johan Schön 
44e17d2014-08-26Martin Nilsson  // DWIM for "www.cnn.com" style input, when parsed in the context of // base "http://".
e491e72014-10-22Henrik Grubbström (Grubba)  if( !has_prefix(uri, "//") && !scheme && this::base_uri?->scheme && !sizeof(this::base_uri->authority) && !sizeof(this::base_uri->path))
44e17d2014-08-26Martin Nilsson  {
e491e72014-10-22Henrik Grubbström (Grubba)  DEBUG("DWIM authority: %O\n", uri);
44e17d2014-08-26Martin Nilsson  uri = "//"+uri; }
61b2e92001-01-11Johan Schön  // Parse authority/login
10fdad2009-09-17Henrik Grubbström (Grubba)  // // hier-part = "//" authority path-abempty / path-absolute // / path-rootless / path-empty
f800812001-01-30Johan Sundström  if(sscanf(uri, "//%[^/]%s", authority, uri))
61b2e92001-01-11Johan Schön  {
d39bb02001-01-22Johan Sundström  DEBUG("Found authority %O", authority);
b75ebc2010-05-23Henrik Grubbström (Grubba)  int q = search(authority, "?", search(authority, "@")+1); if (q >= 0) { // There's a question mark in the host and port section // of the authority. This happens when the path is empty // and there's a query part afterwards. // Example: http://foo?bar uri = authority[q..] + uri; authority = authority[..q-1]; DEBUG("Adjusted authority %O", authority); }
61b2e92001-01-11Johan Schön  } // Parse query information
10fdad2009-09-17Henrik Grubbström (Grubba)  // query = *( pchar / "/" / "?" ) // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
61b2e92001-01-11Johan Schön  sscanf(uri, "%s?%s", uri, query);
d39bb02001-01-22Johan Sundström  DEBUG("Found query %O", query);
a95d9f2001-01-14Johan Sundström 
61b2e92001-01-11Johan Schön  // Parse path:
10fdad2009-09-17Henrik Grubbström (Grubba)  // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
8e06a32014-09-30Martin Nilsson  if ((uri == "") && !scheme && !authority && (this::base_uri)) {
3feec22006-05-04Henrik Grubbström (Grubba)  // Empty path.
8e06a32014-09-30Martin Nilsson  path = this::base_uri->path;
3feec22006-05-04Henrik Grubbström (Grubba)  } else { path = uri; }
d39bb02001-01-22Johan Sundström  DEBUG("Found path %O", path);
61b2e92001-01-11Johan Schön  // 3) If the scheme component is defined, indicating that the reference // starts with a scheme name, then the reference is interpreted as an // absolute URI and we are done. Otherwise, the reference URI's // scheme is inherited from the base URI's scheme component. if(scheme)
f800812001-01-30Johan Sundström  {
d1916c2001-01-13Henrik Grubbström (Grubba)  if(authority) parse_authority();
d39bb02001-01-22Johan Sundström  DEBUG("Scheme found! RFC 2396, §5.2, step 3 " "says we're absolute. Done!");
4274152014-03-24Per Hedbor  sprintf_cache['s'] = raw_uri;
f800812001-01-30Johan Sundström  return;
61b2e92001-01-11Johan Schön  }
8e06a32014-09-30Martin Nilsson  scheme = this::base_uri->scheme;
d39bb02001-01-22Johan Sundström  DEBUG("Inherited scheme %O from base URI", scheme);
02aadc2001-01-11Johan Schön 
d1916c2001-01-13Henrik Grubbström (Grubba)  if(authority) parse_authority();
f800812001-01-30Johan Sundström 
61b2e92001-01-11Johan Schön  // 4) If the authority component is defined, then the reference is a // network-path and we skip to step 7. Otherwise, the reference // URI's authority is inherited from the base URI's authority // component, which will also be undefined if the URI scheme does not // use an authority component.
f800812001-01-30Johan Sundström  if(!authority || !sizeof(authority))
61b2e92001-01-11Johan Schön  {
8e06a32014-09-30Martin Nilsson  authority = this::base_uri->authority;
d39bb02001-01-22Johan Sundström  DEBUG("Inherited authority %O from base URI", authority);
6f7d372011-02-02Henrik Grubbström (Grubba)  if (authority) parse_authority();
61b2e92001-01-11Johan Schön  // 5) If the path component begins with a slash character ("/"), then // the reference is an absolute-path and we skip to step 7.
9ae9762014-11-20Henrik Grubbström (Grubba)  // // FIXME: What if it contains "." or ".." segments? // cf combine_uri_path() above.
2805e32011-02-27Martin Nilsson  if(!has_prefix(path, "/"))
61b2e92001-01-11Johan Schön  { // 6) If this step is reached, then we are resolving a relative-path // reference. The relative path needs to be merged with the base // URI's path. Although there are many ways to do this, we will // describe a simple method using a separate string buffer.
d39bb02001-01-22Johan Sundström  DEBUG("Combining base path %O with path %O => %O",
8e06a32014-09-30Martin Nilsson  this::base_uri->path, path, combine_uri_path(this::base_uri->path, path)); path = combine_uri_path(this::base_uri->path, path);
f800812001-01-30Johan Sundström 
61b2e92001-01-11Johan Schön  } } // 7) The resulting URI components, including any inherited from the // base URI, are recombined to give the absolute form of the URI reference. // (Reassembly is done at cast-to-string/sprintf() time) } //! @decl void create(URI uri) //! @decl void create(URI uri, URI base_uri) //! @decl void create(URI uri, string base_uri) //! @decl void create(string uri) //! @decl void create(string uri, URI base_uri) //! @decl void create(string uri, string base_uri) //! @param base_uri //! When supplied, will root the URI a the given location. This is //! needed to correctly verify relative URIs, but may be left out otherwise. //! If left out, and uri is a relative URI, an error is thrown. //! @param uri //! When uri is another URI object, the created //! URI will inherit all properties of the supplied uri //! except, of course, for its base_uri.
9205162012-05-17Martin Nilsson //! @throws //! An exception is thrown if the @[uri] is a relative URI or only a //! fragment, and missing a @[base_uri].
563bd72004-01-11Martin Nilsson void create(this_program|string uri, this_program|string|void base_uri)
61b2e92001-01-11Johan Schön {
d39bb02001-01-22Johan Sundström  DEBUG("create(%O, %O) called!", uri, base_uri);
4274152014-03-24Per Hedbor  sprintf_cache = ([]);
61b2e92001-01-11Johan Schön  if(stringp(uri))
33593f2001-01-13Henrik Grubbström (Grubba)  raw_uri = [string]uri; // Keep for future runs of reparse_uri after a base_uri change
c357c42011-04-01Martin Nilsson  else if(objectp(uri)) // If uri is 0, we want to inherit from the base_uri.
61b2e92001-01-11Johan Schön  raw_uri = uri->raw_uri;
f800812001-01-30Johan Sundström 
33593f2001-01-13Henrik Grubbström (Grubba)  reparse_uri(base_uri);
61b2e92001-01-11Johan Schön } //! Assign a new value to a property of URI //! @param property //! When any of the following properties are used, properties that //! depend on them are recalculated: user, password, host, port, authority, base_uri. //! @param value //! The value to assign to @[property]
adb6d72001-01-26Johan Sundström mixed `->=(string property, mixed value) { return `[]=(property, value); }
61b2e92001-01-11Johan Schön mixed `[]=(string property, mixed value) {
d39bb02001-01-22Johan Sundström  DEBUG("`[]=(%O, %O)", property, value);
4274152014-03-24Per Hedbor  sprintf_cache = ([]);
61b2e92001-01-11Johan Schön  switch(property) { case "user": case "password": case "host":
c8cfcc2005-03-31Martin Nilsson  if(!stringp(value) && value!=0)
da65c32005-03-31Martin Nilsson  error("%s value not string.\n", property);
14e40d2006-07-13Martin Nilsson  case "port":
61b2e92001-01-11Johan Schön  ::`[]=(property, value);
03ef172005-03-30Henrik Grubbström (Grubba)  authority = (user ? user + (password ? ":" + password : "") + "@" : "") + (host?(has_value(host, ":")?("["+host+"]"):host):"") + (port != Protocols.Ports.tcp[scheme] ? ":" + port : "");
61b2e92001-01-11Johan Schön  return value; case "authority":
c8cfcc2005-03-31Martin Nilsson  if(!stringp(value) && value!=0) error("authority value not string.\n");
33593f2001-01-13Henrik Grubbström (Grubba)  authority = [string]value;
61b2e92001-01-11Johan Schön  parse_authority(); // Set user, password, host and port accordingly return value; case "base_uri":
c8cfcc2005-03-31Martin Nilsson  if(!stringp(value) && value!=0 && !objectp(value))
da65c32005-03-31Martin Nilsson  error("base_uri value neither object nor string.\n");
33593f2001-01-13Henrik Grubbström (Grubba)  reparse_uri([object(this_program)|string]value);
61b2e92001-01-11Johan Schön  return base_uri;
8d18e12011-03-25Martin Nilsson  case "query": variables = 0; return ::`[]=(property, value);
03ef172005-03-30Henrik Grubbström (Grubba)  case "scheme": /* RFC 3986 §3.1 * * An implementation should accept uppercase letters as equivalent * to lowercase in scheme names (e.g., allow "HTTP" as well as * "http") for the sake of robustness but should only produce * lowercase scheme names for consistency. */
c8cfcc2005-03-31Martin Nilsson  if(!stringp(value) && value!=0) error("scheme value not string.\n");
da65c32005-03-31Martin Nilsson  value = lower_case([string]value);
03ef172005-03-30Henrik Grubbström (Grubba)  // FALL_THROUGH
61b2e92001-01-11Johan Schön  default: return ::`[]=(property, value); // Set and return the new value } } //! When cast to string, return the URI (in a canonicalized form).
c8cfcc2005-03-31Martin Nilsson //! When cast to mapping, return a mapping with scheme, authority, //! user, password, host, port, path, query, fragment, raw_uri, //! base_uri as documented above.
ec1a0f2014-08-16Martin Nilsson protected string|mapping cast(string to)
61b2e92001-01-11Johan Schön { switch(to) { case "string": return _sprintf('s'); case "mapping":
c8cfcc2005-03-31Martin Nilsson  array(string) i = ({ "scheme", "authority", "user", "password", "host", "port",
61b2e92001-01-11Johan Schön  "path", "query", "fragment", "raw_uri", "base_uri", });
563bd72004-01-11Martin Nilsson  return mkmapping(i, rows(this, i));
61b2e92001-01-11Johan Schön  }
ec1a0f2014-08-16Martin Nilsson  return UNDEFINED;
61b2e92001-01-11Johan Schön }
d909932002-12-12Anders Johansson //! Returns path and query part of the URI if present.
67ee872002-12-11Anders Johansson string get_path_query() { return (path||"") + (query ? "?" + query : ""); }
8d18e12011-03-25Martin Nilsson protected mapping(string:string) variables;
c8cfcc2005-03-31Martin Nilsson //! Returns the query variables as a @expr{mapping(string:string)@}. mapping(string:string) get_query_variables() {
8d18e12011-03-25Martin Nilsson  if( variables ) return variables;
f9a0342009-08-15Martin Nilsson  if(!query) return ([]);
8d18e12011-03-25Martin Nilsson  variables = ([]); foreach( query/"&";; string pair ) { if( sscanf( pair, "%s=%s", string var, string val )==2 ) variables[var] = val; else variables[pair] = 0; } return variables;
c8cfcc2005-03-31Martin Nilsson } //! Sets the query variables from the provided mapping. void set_query_variables(mapping(string:string) vars) {
4274152014-03-24Per Hedbor  sprintf_cache = ([]);
8d18e12011-03-25Martin Nilsson  variables = vars;
c8cfcc2005-03-31Martin Nilsson  if(!sizeof(vars)) query = 0; else
8d18e12011-03-25Martin Nilsson  { query = ""; foreach( vars; string var; string val ) { if( sizeof(query) ) query += "&"; query += var; if( val ) query += "=" + val; } }
c8cfcc2005-03-31Martin Nilsson } //! Adds the provided query variable to the already existing ones. //! Will overwrite an existing variable with the same name. void add_query_variable(string name, string value) { set_query_variables(get_query_variables()+([name:value])); } //! Appends the provided set of query variables with the already //! existing ones. Will overwrite all existing variables with the same //! names. void add_query_variables(mapping(string:string) vars) { set_query_variables(get_query_variables()|vars); } // HTTP stuff // RFC 1738, 2.2. URL Character Encoding Issues
9eaf1d2008-06-28Martin Nilsson protected constant url_non_corresponding = enumerate(0x21) +
c8cfcc2005-03-31Martin Nilsson  enumerate(0x81,1,0x7f);
9eaf1d2008-06-28Martin Nilsson protected constant url_unsafe = ({ '<', '>', '"', '#', '%', '{', '}',
c8cfcc2005-03-31Martin Nilsson  '|', '\\', '^', '~', '[', ']', '`' });
9eaf1d2008-06-28Martin Nilsson protected constant url_reserved = ({ ';', '/', '?', ':', '@', '=', '&' });
c8cfcc2005-03-31Martin Nilsson  // Encode these chars
9eaf1d2008-06-28Martin Nilsson protected constant url_chars = url_non_corresponding + url_unsafe +
c8cfcc2005-03-31Martin Nilsson  url_reserved + ({ '+', '\'' });
9eaf1d2008-06-28Martin Nilsson protected constant url_from = sprintf("%c", url_chars[*]); protected constant url_to = sprintf("%%%02x", url_chars[*]);
c8cfcc2005-03-31Martin Nilsson  string http_encode(string in) { // We shouldn't really have to soft case here. Bug(ish) in constant // type generation... return replace(in, [array(string)]url_from, [array(string)]url_to); }
e07e1f2012-03-09Martin Nilsson //! Return the query part, coded according to RFC 1738, or zero.
c8cfcc2005-03-31Martin Nilsson string get_http_query() {
e07e1f2012-03-09Martin Nilsson  return query;
c8cfcc2005-03-31Martin Nilsson } //! Return the path and query part of the URI, coded according to RFC //! 1738. string get_http_path_query() {
e07e1f2012-03-09Martin Nilsson  string q = get_http_query(); return http_encode(((path||"")/"/")[*])*"/" + (q?"?"+q:"");
c8cfcc2005-03-31Martin Nilsson }
4274152014-03-24Per Hedbor int __hash() { return hash_value(_sprintf('s')); }
c8cfcc2005-03-31Martin Nilsson 
4274152014-03-24Per Hedbor private mapping(int:string) sprintf_cache = ([]);
61b2e92001-01-11Johan Schön string _sprintf(int how, mapping|void args) {
4274152014-03-24Per Hedbor  if( how == 't' ) return "Standards.URI"; if( string res = sprintf_cache[how] ) return res;
5982f72008-01-05Henrik Grubbström (Grubba)  string look, _host = host, getstring;
4274152014-03-24Per Hedbor  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 : "");
61b2e92001-01-11Johan Schön  if(how == 'O')
4274152014-03-24Per Hedbor  look=sprintf("URI(%q)", look); return sprintf_cache[how]=look;
61b2e92001-01-11Johan Schön }
dd36542013-07-02Martin Nilsson // Master codec API function. Allows for serialization with // encode_value. 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), // variables is only a cache ]); #undef P } // Master codec API function. Allows for deserialization with // decode_value. void _decode(mapping m) { foreach(m; mixed index; mixed value)
76a0bd2013-08-30Martin Nilsson  ::`[]=(index, value);
dd36542013-07-02Martin Nilsson } #if 0
61b2e92001-01-11Johan Schön // Not used yet. 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"})); }
dd36542013-07-02Martin Nilsson #endif