diff --git a/library/types/src/modules/Punycode.rb b/library/types/src/modules/Punycode.rb index fffd5bd91..d882a2abe 100644 --- a/library/types/src/modules/Punycode.rb +++ b/library/types/src/modules/Punycode.rb @@ -30,17 +30,18 @@ # $Id$ # require "yast" +require "simpleidn" module Yast class PunycodeClass < Module + # string, matching this regexp, is not cached + NOT_CACHED_REGEXP = /^[0123456789.]*$/ + def main textdomain "base" @tmp_dir = nil - # string, matching this regexp, is not cached - @not_cached_regexp = "^[0123456789.]*$" - # # Encoded string in cache has the same index # as its Decoded format in the second list. @@ -124,9 +125,7 @@ def GetEncodedCachedString(decoded_string) ret = nil # numbers and empty strings are not converted - if Builtins.regexpmatch(decoded_string, @not_cached_regexp) - return decoded_string - end + return decoded_string if NOT_CACHED_REGEXP.match(decoded_string) counter = -1 # searching through decoded strings to find the index @@ -151,9 +150,7 @@ def GetDecodedCachedString(encoded_string) ret = nil # numbers and empty strings are not converted - if Builtins.regexpmatch(encoded_string, @not_cached_regexp) - return encoded_string - end + return encoded_string if NOT_CACHED_REGEXP.match(encoded_string) counter = -1 # searching through encoded strings to find the index @@ -183,105 +180,51 @@ def GetTmpDirectory # format. Unicode to Punycode or Punycode to Unicode (param to_punycode). # It uses a cache of already-converted strings. def ConvertBackAndForth(strings_in, to_punycode) - strings_in = deep_copy(strings_in) - # list of returned strings - strings_out = [] - # Some (or maybe all) strings needn't be cached not_cached = [] # Check the cache for already entered strings - current_index = -1 - test_cached = Builtins.listmap(strings_in) do |string_in| + test_cached = strings_in.each_with_object({}) do |string_in, all| # Numbers, IPs and empty strings are not converted - string_out = if Builtins.regexpmatch(string_in, @not_cached_regexp) - string_in - elsif to_punycode - GetEncodedCachedString(string_in) - else - GetDecodedCachedString(string_in) - end + string_out = + if NOT_CACHED_REGEXP.match(string_in) + string_in + elsif to_punycode + GetEncodedCachedString(string_in) + else + GetDecodedCachedString(string_in) + end - if string_out.nil? - current_index = Ops.add(current_index, 1) - Ops.set(not_cached, current_index, string_in) - end - { string_in => string_out } + not_cached << string_in if string_out.nil? + all[string_in] = string_out end converted_not_cached = [] # There is something not cached, converting them at once - if not_cached != [] - tmp_in = Ops.add(GetTmpDirectory(), "/tmp-idnconv-in.ycp") - tmp_out = Ops.add(GetTmpDirectory(), "/tmp-idnconv-out.ycp") - - SCR.Write(path(".target.ycp"), tmp_in, not_cached) - convert_command = Builtins.sformat( - "/usr/bin/idnconv %1 %2 > %3", - to_punycode ? "" : "-reverse", - tmp_in, - tmp_out - ) - - if Convert.to_integer( - SCR.Execute(path(".target.bash"), convert_command) - ) != 0 - Builtins.y2error("Conversion failed!") - else - converted_not_cached = Convert.convert( - SCR.Read(path(".target.ycp"), tmp_out), - from: "any", - to: "list " - ) - # Parsing the YCP file failed - if converted_not_cached.nil? - Builtins.y2error( - "Erroneous YCP file: %1", - SCR.Read(path(".target.string"), tmp_out) - ) - end - end + if !not_cached.empty? + meth = to_punycode ? :to_ascii : :to_unicode + converted_not_cached = not_cached.map { |v| SimpleIDN.public_send(meth, v) } end # Listing through the given list and adjusting the return list - current_index = -1 found_index = -1 - Builtins.foreach(strings_in) do |string_in| - current_index = Ops.add(current_index, 1) + strings_in.each_with_object([]) do |string_in, all| # Already cached string - if !Ops.get(test_cached, string_in).nil? - Ops.set( - strings_out, - current_index, - Ops.get(test_cached, string_in, "") - ) - - # Recently converted strings - else - found_index = Ops.add(found_index, 1) - Ops.set( - strings_out, - current_index, - Ops.get(converted_not_cached, found_index, "") - ) + if test_cached[string_in] + all << test_cached[string_in] + else # Recently converted strings + found_index += 1 + all << converted_not_cached[found_index] || "" # Adding converted strings to cache if to_punycode - CreateNewCacheRecord( - string_in, - Ops.get(converted_not_cached, found_index, "") - ) + CreateNewCacheRecord(string_in, converted_not_cached[found_index] || "") else - CreateNewCacheRecord( - Ops.get(converted_not_cached, found_index, ""), - string_in - ) + CreateNewCacheRecord(converted_not_cached[found_index] || "", string_in) end end end - - deep_copy(strings_out) end # Converts list of UTF-8 strings into their Punycode diff --git a/library/types/test/Makefile.am b/library/types/test/Makefile.am index 8264ec39c..07622d588 100644 --- a/library/types/test/Makefile.am +++ b/library/types/test/Makefile.am @@ -2,8 +2,10 @@ TESTS = \ ip_test.rb \ ipv4_netmask_test.rb \ hostname_test.rb \ + punycode_test.rb \ + string_test.rb \ url_test.rb \ - string_test.rb + urlrecode_test.rb TEST_EXTENSIONS = .rb RB_LOG_COMPILER = rspec diff --git a/library/types/test/punycode_test.rb b/library/types/test/punycode_test.rb new file mode 100755 index 000000000..229cc5240 --- /dev/null +++ b/library/types/test/punycode_test.rb @@ -0,0 +1,113 @@ +#!/usr/bin/env rspec +# encoding: utf-8 +# +# Copyright (c) [2018] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "./test_helper" +Yast.import "Punycode" + +describe Yast::Punycode do + subject(:punycode) { Yast::Punycode } + + IP = "192.168.122.1".freeze + NUMBER = "15".freeze + + before do + punycode.main # reset the cache + end + + describe "#ConvertBackAndForth" do + context "when converting to Punycode" do + it "returns all strings converted to Punycode" do + expect(punycode.ConvertBackAndForth(["españa"], true)).to eq(["xn--espaa-rta"]) + end + + it "caches converted values" do + expect(punycode).to receive(:CreateNewCacheRecord).with("españa", "xn--espaa-rta") + punycode.ConvertBackAndForth(["españa"], true) + end + + context "when the string is cached" do + before do + allow(subject).to receive(:GetEncodedCachedString).with("españa") + .and_return("cached_value") + end + + it "returns the cached value" do + expect(punycode.ConvertBackAndForth(["españa"], true)).to eq(["cached_value"]) + end + end + end + + context "when converting to UTF-8" do + it "returns all strings converted to UTF-8" do + expect(punycode.ConvertBackAndForth(["xn--espaa-rta"], false)).to eq(["españa"]) + end + + it "caches converted values" do + expect(punycode).to receive(:CreateNewCacheRecord).with("españa", "xn--espaa-rta") + punycode.ConvertBackAndForth(["xn--espaa-rta"], false) + end + + context "when the string is cached" do + before do + allow(subject).to receive(:GetDecodedCachedString).with("xn--espaa-rta") + .and_return("cached_value") + end + + it "returns the cached value" do + expect(punycode.ConvertBackAndForth(["xn--espaa-rta"], false)).to eq(["cached_value"]) + end + end + end + + context "when an IP address is given" do + it "is not converted" do + expect(punycode.ConvertBackAndForth([IP], true)).to eq([IP]) + end + end + + context "when a string representing a number is given" do + it "is not converted" do + expect(punycode.ConvertBackAndForth([NUMBER], true)).to eq([NUMBER]) + end + end + + context "when an empty string is given" do + it "is not converted" do + expect(punycode.ConvertBackAndForth([""], true)).to eq([""]) + end + end + end + + describe "#EncodePunycodes" do + it "returns strings converted to punycode ignoring numbers, IP addresses and empty strings" do + punycodes = punycode.EncodePunycodes(["españa", NUMBER, IP, ""]) + expect(punycodes).to eq(["xn--espaa-rta", NUMBER, IP, ""]) + end + end + + describe "#DecodePunycodes" do + it "returns strings converted to unicode ignoring numbers, IP addresses and empty strings" do + punycodes = punycode.DecodePunycodes(["xn--espaa-rta", NUMBER, IP, ""]) + expect(punycodes).to eq(["españa", NUMBER, IP, ""]) + end + end +end diff --git a/package/yast2.changes b/package/yast2.changes index 1f862bdeb..59ae89bee 100644 --- a/package/yast2.changes +++ b/package/yast2.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Mon Jul 23 09:07:05 UTC 2018 - knut.anderssen@suse.com + +- Backported the replacement of idnconv with simpleidn library + (bsc#1101851) +- 3.2.47 + ------------------------------------------------------------------- Tue Jul 3 08:01:07 UTC 2018 - knut.anderssen@suse.com diff --git a/package/yast2.spec b/package/yast2.spec index 2b9682c89..1fb14fb72 100644 --- a/package/yast2.spec +++ b/package/yast2.spec @@ -17,7 +17,7 @@ Name: yast2 -Version: 3.2.46 +Version: 3.2.47 Release: 0 Summary: YaST2 - Main Package License: GPL-2.0 @@ -39,6 +39,8 @@ BuildRequires: rubygem(%{rb_default_ruby_abi}:cheetah) BuildRequires: update-desktop-files # For running RSpec tests during build BuildRequires: rubygem(%{rb_default_ruby_abi}:rspec) +# For converting to/from punycode strings +BuildRequires: rubygem(%{rb_default_ruby_abi}:simpleidn) # Needed already in build time BuildRequires: yast2-core >= 2.18.12 BuildRequires: yast2-devtools >= 3.1.10 @@ -66,6 +68,8 @@ Requires: perl-XML-Simple Requires: rubygem(%{rb_default_ruby_abi}:abstract_method) # for file access using augeas Requires: rubygem(%{rb_default_ruby_abi}:cfa) +# For converting to/from punycode strings +Requires: rubygem(%{rb_default_ruby_abi}:simpleidn) Requires: sysconfig >= 0.80.0 # for running scripts Requires: rubygem(%{rb_default_ruby_abi}:cheetah) @@ -96,13 +100,6 @@ Conflicts: yast2-mail < 3.1.7 # Older packager use removed API Conflicts: yast2-packager < 3.1.34 BuildRoot: %{_tmppath}/%{name}-%{version}-build -# for Punycode.rb (bnc#651893) - the idnconv tool is located in -# different packages (SLE12/Leap-42.1: bind-utils, TW/Factory: idnkit) -%if 0%{?suse_version} >= 1330 -Requires: idnkit -%else -Requires: bind-utils -%endif Obsoletes: yast2-devel-doc # for the PackageExtractor class, just make sure they are present, # these should be present even in a very minimal installation