home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / share / YaST2 / modules / URL.ycp < prev    next >
Text File  |  2006-11-29  |  9KB  |  355 lines

  1. /**
  2.  * File:    modules/URL.ycp
  3.  * Package:    yast2
  4.  * Summary:    Manipulate and Parse URLs
  5.  * Authors:    Michal Svec <msvec@suse.cz>
  6.  *        Anas Nashif <nashif@suse.cz>
  7.  * Flags:    Stable
  8.  *
  9.  * $Id: URL.ycp 31472 2006-06-13 11:31:49Z jsrain $
  10.  */
  11.  
  12. {
  13.  
  14. module "URL";
  15. textdomain "base";
  16.  
  17. import "Hostname";
  18. import "String";
  19. import "IP";
  20.  
  21. /**
  22.  * TODO:
  23.  * - read URI(3)
  24.  * - esp. compare the regex mentioned in the URI(3) with ours:
  25.  *   my($scheme, $authority, $path, $query, $fragment) =
  26.  *   $uri =~ m|^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?|;
  27.  */
  28.  
  29. /**
  30.  * Valid characters in URL
  31.  */
  32. global string ValidChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:_-/%";
  33.  
  34.  
  35. /**
  36.  * Transform map used for (un)escaping characters in username/password part of an URL.
  37.  * It doesn't contain '%' because this character must be used in a particular
  38.  * order (the first or the last) during processing
  39.  */
  40. global map<string, string> transform_map_passwd = $[
  41.     ";":"%3b",
  42.     "/":"%2f",
  43.     "?":"%3f",
  44.     ":":"%3a",
  45.     "@":"%40",
  46.     "&":"%26",
  47.     "=":"%3d",
  48.     "+":"%2b",
  49.     "$":"%24",
  50.     ",":"%2c",
  51.     " ":"%20"
  52. ];
  53.  
  54. /**
  55.  * Transform map used for (un)escaping characters in file location part of an URL.
  56.  * It doesn't contain '%' because this character must be used in a particular
  57.  * order (the first or the last) during processing
  58.  */
  59. global map<string, string> transform_map_filename = $[
  60.     ";":"%3b",
  61.     "?":"%3f",
  62.     ":":"%3a",
  63.     "@":"%40",
  64.     "&":"%26",
  65.     "=":"%3d",
  66.     "+":"%2b",
  67.     "$":"%24",
  68.     ",":"%2c",
  69.     " ":"%20"
  70. ];
  71.  
  72. /**
  73.  * Transform map used for (un)escaping characters in query part of a URL.
  74.  * It doesn't contain '%' because this character must be used in a particular
  75.  * order (the first or the last) during processing
  76.  */
  77. global map<string, string> transform_map_query = $[
  78.     ";":"%3b",
  79.     "?":"%3f",
  80.     "@":"%40",
  81.     "+":"%2b",
  82.     "$":"%24",
  83.     ",":"%2c",
  84.     " ":"%20"
  85. ];
  86.  
  87. /**
  88.  * Escape reserved characters in string used as a part of URL (e.g. '%' => '%25', '@' => '%40'...)
  89.  *
  90.  * @param in input string to escape
  91.  * @return string escaped string
  92.  */
  93.  
  94. global string UnEscapeString(string in, map<string,string> transform) {
  95.  
  96.     if (in == nil || in == "")
  97.     {
  98.     return "";
  99.     }
  100.  
  101.     // replace the other reserved characters
  102.     foreach(string tgt, string src, transform, {
  103.         // replace both upper and lower case escape sequences
  104.         in = String::Replace(in, tolower(src), tgt);
  105.         in = String::Replace(in, toupper(src), tgt);
  106.     }
  107.     );
  108.  
  109.     // replace % at the end
  110.     in = String::Replace(in, "%25", "%");
  111.  
  112.     return in;
  113. }
  114.  
  115. /**
  116.  * Escape reserved characters in string used as a part of URL (e.g. '%' => '%25', '@' => '%40'...)
  117.  *
  118.  * @param in input string to escape
  119.  * @return string escaped string
  120.  */
  121.  
  122. global string EscapeString(string in, map<string,string> transform) {
  123.     string ret = "";
  124.  
  125.     if (in == nil || in == "")
  126.     {
  127.     return ret;
  128.     }
  129.  
  130.     // replace % at first
  131.     ret = mergestring(splitstring(in, "%"), "%25");
  132.  
  133.     // replace the other reserved characters
  134.     foreach(string src, string tgt, transform, {
  135.         ret = mergestring(splitstring(ret, src), tgt);
  136.     }
  137.     );
  138.  
  139.     return ret;
  140. }
  141.  
  142. /**
  143.  * Tokenize URL
  144.  * @param url URL to be parsed
  145.  * @return URL split to tokens
  146.  * @example Parse("http://name:pass@www.suse.cz:80/path/index.html?question#part") ->
  147.  *     $[
  148.  *         "scheme"  : "http",
  149.  *         "host"    : "www.suse.cz"
  150.  *         "port"    : "80",
  151.  *         "path"    : /path/index.html",
  152.  *         "user"    : "name",
  153.  *         "pass"    : "pass",
  154.  *         "query"   : "question",
  155.  *         "fragment": "part"
  156.  *     ]
  157.  */
  158. global define map Parse(string url) ``{
  159.  
  160.     y2debug("url=%1", url);
  161.  
  162.     /* We don't parse empty URLs */
  163.     if(url == nil || size(url) < 1) return $[];
  164.  
  165.     /* Extract basic URL parts: scheme://host/path?question#part */
  166.     list rawtokens = regexptokenize(url, "^" +
  167.     /* 0,1: http:// */
  168.     "(([^:/?#]+)://)?" +
  169.     /* 2: user:pass@www.suse.cz:23 */
  170.     "([^/?#]*)?" +
  171.     /* 3: /some/path */
  172.     "([^?#]*)?" +
  173.     /* 4,5: ?question */
  174.     "(\\?([^#]*))?" +
  175.     /* 6,7: #fragment */
  176.     "(#(.*))?"
  177.     );
  178.     y2debug("rawtokens=%1", rawtokens);
  179.  
  180.     map tokens = $[];
  181.     tokens["scheme"] = rawtokens[1]:"";
  182.     string pth = rawtokens[3]:"";
  183.     if (tokens["scheme"]:"" == "ftp")
  184.     {
  185.     if (substring (pth, 0, 4) == "/%2f")
  186.     {
  187.         pth = "/" + substring (pth, 4);
  188.     }
  189.     else
  190.     {
  191.         pth = substring (pth, 1);
  192.     }
  193.     }
  194.     tokens["path"] = UnEscapeString(pth, transform_map_filename);
  195.     tokens["query"] = UnEscapeString(rawtokens[5]:"", transform_map_query);
  196.     tokens["fragment"] = UnEscapeString(rawtokens[7]:"", transform_map_passwd);
  197.  
  198.     /* Extract username:pass@host:port */
  199.     list userpass = regexptokenize(rawtokens[2]:"", "^" +
  200.     /* 0,1,2,3: user:pass@ */
  201.     "(([^@:]+)(:([^@:]+))?@)?" +
  202.     /* 4,5,6,7: hostname|[xxx] */
  203.     "(([^:@]+))" +
  204.     // FIXME "(([^:@]+)|(\\[([^]]+)\\]))" +
  205.     /* 8,9: port */
  206.     "(:([^:@]+))?"
  207.     );
  208.     y2debug("userpass=%1", userpass);
  209.  
  210.     tokens["user"] = UnEscapeString(userpass[1]:"", transform_map_passwd);
  211.     tokens["pass"] = UnEscapeString(userpass[3]:"", transform_map_passwd);
  212.     tokens["port"] = userpass[7]:"";
  213.  
  214.     if(userpass[5]:"" != "")
  215.     tokens["host"] = userpass[5]:"";
  216.     else
  217.     tokens["host"] = userpass[7]:"";
  218.  
  219.     y2debug("tokens=%1", tokens);
  220.     return tokens;
  221. }
  222.  
  223. /**
  224.  * Check URL
  225.  * @param url URL to be checked
  226.  * @return true if correct
  227.  * @see RFC 2396 (updated by RFC 2732)
  228.  * @see also perl-URI: URI(3)
  229.  */
  230. global define boolean Check(string url) ``{
  231.     /* We don't allow empty URLs */
  232.     if(url == nil || size(url) < 1) return false;
  233.  
  234.     /* We don't allow URLs with spaces */
  235.     if(find(url, " ") != -1) return false;
  236.  
  237.     map tokens = Parse(url);
  238.  
  239.     /* Check "scheme"  : "http" */
  240.     if(!regexpmatch(tokens["scheme"]:"", "^[a-z]*$"))
  241.     return false;
  242.  
  243.     /* Check "host"    : "www.suse.cz" */
  244.     if((!Hostname::CheckFQ(tokens["host"]:"") && !IP::Check(tokens["host"]:""))
  245.             && tokens["host"]:"" != "")
  246.     return false;
  247.  
  248.     /* Check "path"    : /path/index.html" */
  249.  
  250.     /* Check "port"    : "80" */
  251.     if(!regexpmatch(tokens["port"]:"", "^[0-9]*$"))
  252.     return false;
  253.  
  254.     /* Check "user"    : "name" */
  255.  
  256.     /* Check "pass"    : "pass" */
  257.  
  258.     /* Check "query"   : "question" */
  259.  
  260.     /* Check "fragment": "part" */
  261.  
  262.     return true;
  263. }
  264.  
  265.  
  266. /**
  267.  * Build URL from tokens as parsed with Parse
  268.  * @param map token as returned from Parse
  269.  * @return string url, empty string if invalid data is used to build the url.
  270.  * @see RFC 2396 (updated by RFC 2732)
  271.  * @see also perl-URI: URI(3)
  272.  */
  273. global define string Build (map tokens) ``{
  274.  
  275.     string url = "";
  276.     string userpass = "";
  277.  
  278.     if(regexpmatch(tokens["scheme"]:"", "^[a-z]*$"))
  279.     {
  280.         url = tokens["scheme"]:"";
  281.     }
  282.     y2debug("url: %1", url);
  283.  
  284.     if (tokens["user"]:"" != "")
  285.     {
  286.         userpass = EscapeString(tokens["user"]:"", transform_map_passwd);
  287.         y2milestone("Escaped username '%1' => '%2'", tokens["user"]:"", userpass);
  288.     }
  289.     if (size(userpass) != 0 && tokens["pass"]:"" != "" )
  290.     {
  291.         userpass = sformat("%1:%2", userpass, EscapeString(tokens["pass"]:"", transform_map_passwd) );
  292.     }
  293.     if (size(userpass) > 0 )
  294.         userpass = userpass + "@";
  295.  
  296.     url = sformat("%1://%2", url, userpass);
  297.     y2debug("url: %1", url);
  298.  
  299.     if(Hostname::CheckFQ(tokens["host"]:"") || IP::Check(tokens["host"]:""))
  300.     {
  301.         url = sformat("%1%2", url, tokens["host"]:"");
  302.     }
  303.     y2debug("url: %1", url);
  304.  
  305.     if (regexpmatch(tokens["port"]:"", "^[0-9]*$") && tokens["port"]:"" != "")
  306.     {
  307.         url = sformat("%1:%2", url, tokens["port"]:"");
  308.     }
  309.     y2debug("url: %1", url);
  310.  
  311.     if (tokens["path"]:"" != "" && find (tokens["path"]:"", "/") != 0 )
  312.         url = sformat("%1/%2", url, EscapeString(tokens["path"]:"", transform_map_filename));
  313.     else if (tokens["path"]:"" != "" && find (tokens["path"]:"", "/") == 0 )
  314.     {
  315.         if (tokens["scheme"]:"" == "ftp") {
  316.             url = sformat("%1/%%2f%2", url, substring(EscapeString(tokens["path"]:"", transform_map_filename), 1));
  317.         }
  318.         else {
  319.             url = sformat("%1%2", url, EscapeString(tokens["path"]:"", transform_map_filename));
  320.         }
  321.     }
  322.     y2debug("url: %1", url);
  323.  
  324.     if (tokens["query"]:"" != "" )
  325.         url = sformat("%1?%2", url, EscapeString(tokens["query"]:"", transform_map_query));
  326.     if (tokens["fragment"]:"" != "" )
  327.         url = sformat("%1#%2", url, EscapeString(tokens["fragment"]:"", transform_map_passwd));
  328.     y2debug("url: %1", url);
  329.  
  330.     if (!Check(url)) {
  331.         y2error("Invalid URL: %1", url);
  332.         return "";
  333.     }
  334.  
  335.     return url;
  336.  
  337. }
  338.  
  339. /*
  340. y2milestone("%1", Parse("http://a:b@www.suse.cz:33/ahoj/nekde?neco#blah"));
  341. y2milestone("%1", Parse("ftp://www.suse.cz/ah"));
  342. y2milestone("%1", Parse("ftp://www.suse.cz:22/ah"));
  343. y2milestone("%1", Parse("www.suse.cz/ah"));
  344.  
  345. y2milestone("%1", Check("http://a:b@www.suse.cz:33/ahoj/nekde?neco#blah"));
  346. y2milestone("%1", Check("ftp://www.suse.cz/ah"));
  347. y2milestone("%1", Check("ftp://www.suse.cz:22/ah"));
  348. y2milestone("%1", Check("www.suse.cz/ah"));
  349. y2milestone("%1", Check("www.suse.cz ah"));
  350. y2milestone("%1", Check(""));
  351. y2milestone("%1", Check(nil));
  352. */
  353.  
  354. }
  355.