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 >
Wrap
Text File
|
2006-11-29
|
11KB
|
375 lines
/**
* File: modules/Punycode.ycp
* Package: Main yast package
* Summary: DNS Punycode Handling
* Authors: Lukas Ocilka <lukas.ocilka@suse.cz>
* Tags: Unstable
*
* $Id:$
*
*/
{
module "Punycode";
textdomain "base";
string tmp_dir = nil;
// string, matching this regexp, is not cached
string not_cached_regexp = "^[0123456789\.]*$";
//
// Encoded string in cache has the same index
// as its Decoded format in the second list.
//
// list of encoded strings to be cached (Punycode or Unicode)
list <string> cache_encoded = [];
// list of decoded strings to be cached (Unicode or Punycode)
list <string> cache_decoded = [];
integer current_cache_index = 0;
// cached amount of data should be controled
integer current_cache_size = 0;
integer maximum_cache_size = 32768;
/**
* Returns the maximum cache size (sum of already converted
* strings).
*
* @return integer maximum_cache_size
* @see SetMaximumCacheSize()
*/
global define integer GetMaximumCacheSize () {
return maximum_cache_size;
}
/**
* Offers to set the maximum cache size (sum of already
* converted strings).
*
* @param integer new_max_size
* @see GetMaximumCacheSize()
*/
global define void SetMaximumCacheSize (integer new_max_size) {
if (new_max_size != nil) {
maximum_cache_size = new_max_size;
} else {
y2error("Cannot set MaximumCacheSize to nil!");
}
}
/**
* Adds new cache records for encoded and decoded strings.
*
* @params string decoded
* @params string encoded
*/
define void CreateNewCacheRecord (string decoded, string encoded) {
// Erroneous cache record
if (decoded == nil || encoded == nil) return;
// Already cached
if (contains(cache_decoded, decoded)) return;
integer decoded_size = size(decoded);
integer encoded_size = size(encoded);
// Do not store this record if the cache would exceed maximum
if ((current_cache_size + decoded_size + encoded_size) > maximum_cache_size) return;
current_cache_size = current_cache_size + decoded_size + encoded_size;
cache_decoded[current_cache_index] = decoded;
cache_encoded[current_cache_index] = encoded;
current_cache_index = current_cache_index + 1;
}
/**
* Returns string encoded in Punycode if it has been
* already cached. Returns nil if not found.
*
* @param string decoded_string (Unicode)
* @return string encoded_string (Punycode)
*/
define string GetEncodedCachedString (string decoded_string) {
string ret = nil;
// numbers and empty strings are not converted
if (regexpmatch(decoded_string, not_cached_regexp)) {
return decoded_string;
}
integer counter = -1;
// searching through decoded strings to find the index
foreach (string cached_string, cache_decoded, {
counter = counter + 1;
if (cached_string == decoded_string) {
// returning encoded representation
ret = cache_encoded[counter]:nil;
break;
}
});
return ret;
}
/**
* Returns string encoded in Punycode if it has been
* already cached. Returns nil if not found.
*
* @param string encoded_string (Punycode)
* @return string decoded_string (Unicode)
*/
define string GetDecodedCachedString (string encoded_string) {
string ret = nil;
// numbers and empty strings are not converted
if (regexpmatch(encoded_string, not_cached_regexp)) {
return encoded_string;
}
integer counter = -1;
// searching through encoded strings to find the index
foreach (string cached_string, cache_encoded, {
counter = counter + 1;
if (cached_string == encoded_string) {
// returning decoded representation
ret = cache_decoded[counter]:nil;
break;
}
});
return ret;
}
/**
* Returns the current temporary directory.
* Lazy loading for the initialization is used.
*/
define string GetTmpDirectory () {
if (tmp_dir == nil) {
tmp_dir = (string) SCR::Read(.target.tmpdir);
}
return tmp_dir;
}
/**
* Function takes the list of strings and returns them in the converted
* format. Unicode to Punycode or Punycode to Unicode (param to_punycode).
* It uses a cache of already-converted strings.
*/
define list <string> ConvertBackAndForth (list <string> strings_in, boolean to_punycode) {
// list of returned strings
list <string> strings_out = [];
// Some (or maybe all) strings needn't be cached
list <string> not_cached = [];
// Check the cache for already entered strings
integer current_index = -1;
map <string, string> test_cached = listmap (string string_in, strings_in, {
string string_out = nil;
// Numbers, IPs and empty strings are not converted
if (regexpmatch(string_in, not_cached_regexp)) {
string_out = string_in;
} else {
if (to_punycode) {
string_out = GetEncodedCachedString(string_in);
} else {
string_out = GetDecodedCachedString(string_in);
}
}
if (string_out == nil) {
current_index = current_index + 1;
not_cached[current_index] = string_in;
}
return $[string_in : string_out];
});
list <string> converted_not_cached = [];
// There is something not cached, converting them at once
if (not_cached != []) {
string tmp_in = GetTmpDirectory() + "/tmp-idnconv-in.ycp";
string tmp_out = GetTmpDirectory() + "/tmp-idnconv-out.ycp";
SCR::Write(.target.ycp, tmp_in, not_cached);
string convert_command = sformat(
"/usr/bin/idnconv %1 %2 > %3",
(to_punycode ? "":"-reverse"), tmp_in, tmp_out
);
if ((integer) SCR::Execute(.target.bash, convert_command) != 0) {
y2error("Conversion failed!");
} else {
converted_not_cached = (list <string>) SCR::Read(.target.ycp, tmp_out);
// Parsing the YCP file failed
if (converted_not_cached == nil) {
y2error("Erroneous YCP file: %1", SCR::Read(.target.string, tmp_out));
}
}
}
// Listing through the given list and adjusting the return list
current_index = -1;
integer found_index = -1;
foreach (string string_in, strings_in, {
current_index = current_index + 1;
// Already cached string
if (test_cached[string_in]:nil != nil) {
strings_out[current_index] = test_cached[string_in]:"";
// Recently converted strings
} else {
found_index = found_index + 1;
strings_out[current_index] = converted_not_cached[found_index]:"";
// Adding converted strings to cache
if (to_punycode) {
CreateNewCacheRecord(string_in, converted_not_cached[found_index]:"");
} else {
CreateNewCacheRecord(converted_not_cached[found_index]:"", string_in);
}
}
});
return strings_out;
}
/**
* Converts list of UTF-8 strings into their Punycode
* ASCII repserentation.
*
* @param list <string> punycode_strings
* @return list <string> encoded_strings
*/
global define list <string> EncodePunycodes (list <string> punycode_strings) {
return ConvertBackAndForth(punycode_strings, true);
}
/**
* Converts list of Punycode strings into their UTF-8
* representation.
*
* @param list <string> punycode_strings
* @return list <string> decoded_strings
*/
global define list <string> DecodePunycodes (list <string> punycode_strings) {
return ConvertBackAndForth(punycode_strings, false);
}
/**
* Encodes the domain name (relative or FQDN) to the Punycode.
*
* @param string decoded domain_name
* @return string encoded domain_name
*
* @example
* EncodeDomainName("žížala.jůlinka.go.home")
* -> "xn--ala-qma83eb.xn--jlinka-3mb.go.home"
*/
global define string EncodeDomainName (string decoded_domain_name) {
return mergestring(
EncodePunycodes(
splitstring(decoded_domain_name, ".")
),
"."
);
}
/**
* Decodes the domain name (relative or FQDN) from the Punycode.
*
* @param string encoded_domain_name
* @return string decoded domain_name
*
* @example
* DecodeDomainName("xn--ala-qma83eb.xn--jlinka-3mb.go.home")
* -> "žížala.jůlinka.go.home"
*/
global define string DecodeDomainName (string encoded_domain_name) {
return mergestring(
DecodePunycodes(
splitstring(encoded_domain_name, ".")
),
"."
);
}
/**
* Decodes the list of domain names to their Unicode representation.
* This function is similar to DecodePunycodes but it works with every
* string as a domain name (that means every domain name is parsed
* by dots and separately evaluated).
*
* @param list <string> encoded_domain_names
* @return list <string> decoded_domain_names
*
* @example
* DocodeDomainNames(["mx1.example.org", "xp3.example.org.", "xn--ala-qma83eb.org.example."])
* -> ["mx1.example.org", "xp3.example.org.", "žížala.org.example."]
*/
global define list <string> DocodeDomainNames (list <string> encoded_domain_names) {
list <string> decoded_domain_names = [];
list <string> strings_to_decode = [];
// $[0 : [0, 2], 1 : [3, 5]]
map <integer, list <integer> > backward_map_of_conversion = $[];
integer current_domain_index = -1;
integer current_domain_item = 0;
// parsing all domain names one by one
foreach (string one_domain_name, encoded_domain_names, {
current_domain_index = current_domain_index + 1;
integer start = current_domain_item;
// parsing the domain name by dots
foreach (string domain_item, splitstring(one_domain_name, "."), {
strings_to_decode[current_domain_item] = domain_item;
current_domain_item = current_domain_item + 1;
});
// creating backward index
backward_map_of_conversion[current_domain_index] = [start, (current_domain_item-1)];
});
// Transformating strings to the decoded format
strings_to_decode = DecodePunycodes(strings_to_decode);
current_domain_index = -1;
foreach (string one_encoded, encoded_domain_names, {
current_domain_index = current_domain_index + 1;
// Where the current string starts and ends
integer current = backward_map_of_conversion[current_domain_index, 0]:nil;
integer end = backward_map_of_conversion[current_domain_index, 1]:nil;
// error?
if (current == nil || end == nil) {
y2error("Cannot find start/end for %1 in %2",
one_encoded, backward_map_of_conversion[current_domain_index]:nil
);
decoded_domain_names[current_domain_index] = one_encoded;
} else {
// create a list of items of the current domain (translated)
list <string> decoded_domain = [];
while (current <= end) {
decoded_domain = add(decoded_domain, strings_to_decode[current]:"");
current = current + 1;
}
// create a domain name from these strings
decoded_domain_names[current_domain_index] = mergestring(decoded_domain, ".");
}
});
return decoded_domain_names;
}
}