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 / Punycode.ycp < prev    next >
Text File  |  2006-11-29  |  11KB  |  375 lines

  1. /**
  2.  * File:    modules/Punycode.ycp
  3.  * Package:    Main yast package
  4.  * Summary:    DNS Punycode Handling
  5.  * Authors:    Lukas Ocilka <lukas.ocilka@suse.cz>
  6.  * Tags:    Unstable
  7.  *
  8.  * $Id:$
  9.  *
  10.  */
  11.  
  12. {
  13.  
  14.     module "Punycode";
  15.     textdomain "base";
  16.  
  17.     string tmp_dir = nil;
  18.  
  19.     // string, matching this regexp, is not cached
  20.     string not_cached_regexp = "^[0123456789\.]*$";
  21.  
  22.     //
  23.     // Encoded string in cache has the same index
  24.     // as its Decoded format in the second list.
  25.     //
  26.  
  27.     // list of encoded strings to be cached (Punycode or Unicode)
  28.     list <string> cache_encoded = [];
  29.     // list of decoded strings to be cached (Unicode or Punycode)
  30.     list <string> cache_decoded = [];
  31.  
  32.     integer current_cache_index = 0;
  33.  
  34.     // cached amount of data should be controled
  35.     integer current_cache_size  = 0;
  36.     integer maximum_cache_size  = 32768;
  37.  
  38.     /**
  39.      * Returns the maximum cache size (sum of already converted
  40.      * strings).
  41.      *
  42.      * @return integer maximum_cache_size
  43.      * @see SetMaximumCacheSize()
  44.      */
  45.     global define integer GetMaximumCacheSize () {
  46.     return maximum_cache_size;
  47.     }
  48.     
  49.     /**
  50.      * Offers to set the maximum cache size (sum of already
  51.      * converted strings).
  52.      *
  53.      * @param integer new_max_size
  54.      * @see GetMaximumCacheSize()
  55.      */
  56.     global define void SetMaximumCacheSize (integer new_max_size) {
  57.     if (new_max_size != nil) {
  58.         maximum_cache_size = new_max_size;
  59.     } else {
  60.         y2error("Cannot set MaximumCacheSize to nil!");
  61.     }
  62.     }
  63.  
  64.     /**
  65.      * Adds new cache records for encoded and decoded strings.
  66.      *
  67.      * @params string decoded
  68.      * @params string encoded
  69.      */
  70.     define void CreateNewCacheRecord (string decoded, string encoded) {
  71.     // Erroneous cache record
  72.     if (decoded == nil || encoded == nil) return;
  73.  
  74.     // Already cached
  75.     if (contains(cache_decoded, decoded)) return;
  76.  
  77.     integer decoded_size = size(decoded);
  78.     integer encoded_size = size(encoded);
  79.     
  80.     // Do not store this record if the cache would exceed maximum
  81.     if ((current_cache_size + decoded_size + encoded_size) > maximum_cache_size) return;
  82.  
  83.     current_cache_size = current_cache_size + decoded_size + encoded_size;
  84.     cache_decoded[current_cache_index] = decoded;
  85.     cache_encoded[current_cache_index] = encoded;
  86.     current_cache_index = current_cache_index + 1;
  87.     }
  88.  
  89.     /**
  90.      * Returns string encoded in Punycode if it has been
  91.      * already cached. Returns nil if not found.
  92.      *
  93.      * @param string decoded_string (Unicode)
  94.      * @return string encoded_string (Punycode)
  95.      */
  96.     define string GetEncodedCachedString (string decoded_string) {
  97.     string ret = nil;
  98.  
  99.     // numbers and empty strings are not converted
  100.     if (regexpmatch(decoded_string, not_cached_regexp)) {
  101.         return decoded_string;
  102.     }
  103.  
  104.     integer counter = -1;
  105.     // searching through decoded strings to find the index
  106.     foreach (string cached_string, cache_decoded, {
  107.         counter = counter + 1;
  108.         if (cached_string == decoded_string) {
  109.         // returning encoded representation
  110.         ret = cache_encoded[counter]:nil;
  111.         break;
  112.         }
  113.     });
  114.     
  115.     return ret;
  116.     }
  117.  
  118.     /**
  119.      * Returns string encoded in Punycode if it has been
  120.      * already cached. Returns nil if not found.
  121.      *
  122.      * @param string encoded_string (Punycode)
  123.      * @return string decoded_string (Unicode)
  124.      */
  125.     define string GetDecodedCachedString (string encoded_string) {
  126.     string ret = nil;
  127.  
  128.     // numbers and empty strings are not converted
  129.     if (regexpmatch(encoded_string, not_cached_regexp)) {
  130.         return encoded_string;
  131.     }
  132.  
  133.     integer counter = -1;
  134.     // searching through encoded strings to find the index
  135.     foreach (string cached_string, cache_encoded, {
  136.         counter = counter + 1;
  137.         if (cached_string == encoded_string) {
  138.         // returning decoded representation
  139.         ret = cache_decoded[counter]:nil;
  140.         break;
  141.         }
  142.     });
  143.     
  144.     return ret;
  145.     }
  146.  
  147.     /**
  148.      * Returns the current temporary directory.
  149.      * Lazy loading for the initialization is used.
  150.      */
  151.     define string GetTmpDirectory () {
  152.     if (tmp_dir == nil) {
  153.         tmp_dir = (string) SCR::Read(.target.tmpdir);
  154.     }
  155.  
  156.     return tmp_dir;
  157.     }
  158.  
  159.     /**
  160.      * Function takes the list of strings and returns them in the converted
  161.      * format. Unicode to Punycode or Punycode to Unicode (param to_punycode).
  162.      * It uses a cache of already-converted strings.
  163.      */
  164.     define list <string> ConvertBackAndForth (list <string> strings_in, boolean to_punycode) {
  165.     // list of returned strings
  166.     list <string> strings_out = [];
  167.  
  168.     // Some (or maybe all) strings needn't be cached
  169.     list <string> not_cached = [];
  170.  
  171.     // Check the cache for already entered strings
  172.     integer current_index = -1;
  173.     map <string, string> test_cached = listmap (string string_in, strings_in, {
  174.         string string_out = nil;
  175.  
  176.         // Numbers, IPs and empty strings are not converted
  177.         if (regexpmatch(string_in, not_cached_regexp)) {
  178.         string_out = string_in;
  179.         } else {
  180.         if (to_punycode) {
  181.             string_out = GetEncodedCachedString(string_in);
  182.         } else {
  183.             string_out = GetDecodedCachedString(string_in);
  184.         }
  185.         }
  186.  
  187.         if (string_out == nil) {
  188.         current_index = current_index + 1;
  189.         not_cached[current_index] = string_in;
  190.         }
  191.  
  192.         return $[string_in : string_out];
  193.     });
  194.  
  195.     list <string> converted_not_cached = [];
  196.  
  197.     // There is something not cached, converting them at once
  198.     if (not_cached != []) {
  199.         string tmp_in  = GetTmpDirectory() + "/tmp-idnconv-in.ycp";
  200.         string tmp_out = GetTmpDirectory() + "/tmp-idnconv-out.ycp";
  201.  
  202.         SCR::Write(.target.ycp, tmp_in, not_cached);
  203.         string convert_command = sformat(
  204.         "/usr/bin/idnconv %1 %2 > %3",
  205.         (to_punycode ? "":"-reverse"), tmp_in, tmp_out
  206.         );
  207.  
  208.         if ((integer) SCR::Execute(.target.bash, convert_command) != 0) {
  209.         y2error("Conversion failed!");
  210.         } else {
  211.         converted_not_cached = (list <string>) SCR::Read(.target.ycp, tmp_out);
  212.         // Parsing the YCP file failed
  213.         if (converted_not_cached == nil) {
  214.             y2error("Erroneous YCP file: %1", SCR::Read(.target.string, tmp_out));
  215.         }
  216.         }
  217.     }
  218.  
  219.     // Listing through the given list and adjusting the return list
  220.     current_index = -1;
  221.     integer found_index = -1;
  222.     foreach (string string_in, strings_in, {
  223.         current_index = current_index + 1;
  224.  
  225.         // Already cached string
  226.         if (test_cached[string_in]:nil != nil) {
  227.         strings_out[current_index] = test_cached[string_in]:"";
  228.  
  229.         // Recently converted strings
  230.         } else {
  231.         found_index = found_index + 1;
  232.         strings_out[current_index] = converted_not_cached[found_index]:"";
  233.         
  234.         // Adding converted strings to cache
  235.         if (to_punycode) {
  236.             CreateNewCacheRecord(string_in, converted_not_cached[found_index]:"");
  237.         } else {
  238.             CreateNewCacheRecord(converted_not_cached[found_index]:"", string_in);
  239.         }
  240.         }
  241.     });
  242.     
  243.     return strings_out;
  244.     }
  245.  
  246.     /**
  247.      * Converts list of UTF-8 strings into their Punycode
  248.      * ASCII repserentation.
  249.      *
  250.      * @param list <string> punycode_strings
  251.      * @return list <string> encoded_strings
  252.      */
  253.     global define list <string> EncodePunycodes (list <string> punycode_strings) {
  254.     return ConvertBackAndForth(punycode_strings, true);
  255.     }
  256.  
  257.     /**
  258.      * Converts list of Punycode strings into their UTF-8
  259.      * representation.
  260.      *
  261.      * @param list <string> punycode_strings
  262.      * @return list <string> decoded_strings
  263.      */
  264.     global define list <string> DecodePunycodes (list <string> punycode_strings) {
  265.     return ConvertBackAndForth(punycode_strings, false);
  266.     }
  267.  
  268.     /**
  269.      * Encodes the domain name (relative or FQDN) to the Punycode.
  270.      *
  271.      * @param string decoded domain_name
  272.      * @return string encoded domain_name
  273.      *
  274.      * @example
  275.      *    EncodeDomainName("┼╛├¡┼╛ala.j┼»linka.go.home")
  276.      *        -> "xn--ala-qma83eb.xn--jlinka-3mb.go.home"
  277.      */
  278.     global define string EncodeDomainName (string decoded_domain_name) {
  279.     return mergestring(
  280.         EncodePunycodes(
  281.         splitstring(decoded_domain_name, ".")
  282.         ),
  283.         "."
  284.     );
  285.     }
  286.  
  287.     /**
  288.      * Decodes the domain name (relative or FQDN) from the Punycode.
  289.      *
  290.      * @param string encoded_domain_name
  291.      * @return string decoded domain_name
  292.      *
  293.      * @example
  294.      *    DecodeDomainName("xn--ala-qma83eb.xn--jlinka-3mb.go.home")
  295.      *        -> "┼╛├¡┼╛ala.j┼»linka.go.home"
  296.      */
  297.     global define string DecodeDomainName (string encoded_domain_name) {
  298.     return mergestring(
  299.         DecodePunycodes(
  300.         splitstring(encoded_domain_name, ".")
  301.         ),
  302.         "."
  303.     );
  304.     }
  305.  
  306.     /**
  307.      * Decodes the list of domain names to their Unicode representation.
  308.      * This function is similar to DecodePunycodes but it works with every
  309.      * string as a domain name (that means every domain name is parsed
  310.      * by dots and separately evaluated).
  311.      *
  312.      * @param list <string> encoded_domain_names
  313.      * @return list <string> decoded_domain_names
  314.      *
  315.      * @example
  316.      *     DocodeDomainNames(["mx1.example.org", "xp3.example.org.", "xn--ala-qma83eb.org.example."])
  317.      *        -> ["mx1.example.org", "xp3.example.org.", "┼╛├¡┼╛ala.org.example."]
  318.      */
  319.     global define list <string> DocodeDomainNames (list <string> encoded_domain_names) {
  320.     list <string> decoded_domain_names = [];
  321.     list <string> strings_to_decode = [];
  322.  
  323.     // $[0 : [0, 2], 1 : [3, 5]]
  324.     map <integer, list <integer> > backward_map_of_conversion = $[];
  325.  
  326.     integer current_domain_index = -1;
  327.     integer current_domain_item  =  0;
  328.  
  329.     // parsing all domain names one by one
  330.     foreach (string one_domain_name, encoded_domain_names, {
  331.         current_domain_index = current_domain_index + 1;
  332.         integer start = current_domain_item;
  333.  
  334.         // parsing the domain name by dots
  335.         foreach (string domain_item, splitstring(one_domain_name, "."), {
  336.         strings_to_decode[current_domain_item] = domain_item;
  337.         current_domain_item = current_domain_item + 1;
  338.         });
  339.         // creating backward index
  340.         backward_map_of_conversion[current_domain_index] = [start, (current_domain_item-1)];
  341.     });
  342.     
  343.     // Transformating strings to the decoded format
  344.     strings_to_decode = DecodePunycodes(strings_to_decode);
  345.     
  346.     current_domain_index = -1;
  347.     foreach (string one_encoded, encoded_domain_names, {
  348.         current_domain_index = current_domain_index + 1;
  349.  
  350.         // Where the current string starts and ends
  351.         integer current = backward_map_of_conversion[current_domain_index, 0]:nil;
  352.         integer end     = backward_map_of_conversion[current_domain_index, 1]:nil;
  353.         
  354.         // error?
  355.         if (current == nil || end == nil) {
  356.         y2error("Cannot find start/end for %1 in %2",
  357.             one_encoded, backward_map_of_conversion[current_domain_index]:nil
  358.         ); 
  359.         decoded_domain_names[current_domain_index] = one_encoded;
  360.         } else {
  361.         // create a list of items of the current domain (translated)
  362.         list <string> decoded_domain = [];
  363.         while (current <= end) {
  364.             decoded_domain = add(decoded_domain, strings_to_decode[current]:"");
  365.             current = current + 1;
  366.         }
  367.         // create a domain name from these strings
  368.         decoded_domain_names[current_domain_index] = mergestring(decoded_domain, ".");
  369.         }
  370.     });
  371.  
  372.     return decoded_domain_names;
  373.     }
  374. }
  375.