From 17e1936d8bdd897cebd03ed198a826ee395572ac Mon Sep 17 00:00:00 2001 From: nagai Date: Tue, 24 Jun 2003 16:46:07 +0000 Subject: [PATCH] tk.rb : * TkToplevel, TkFrame, TkPanedwindow, TkOptionDB : bug fix * TkOptionDB : make it more secure to use procs defined on resourceDB sample/tkoptdb.rb, sample/resource.ja, sample/resource.en : * sample script how to use TkOptionDB. resource.ja and resource.en are samples of resource definition file which are read by tkoptdb.rb. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@3998 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/tk/MANIFEST | 3 + ext/tk/lib/tk.rb | 148 ++++++++++++++++++++++++++++++++------ ext/tk/sample/resource.en | 12 ++++ ext/tk/sample/resource.ja | 12 ++++ ext/tk/sample/tkoptdb.rb | 49 +++++++++++++ 5 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 ext/tk/sample/resource.en create mode 100644 ext/tk/sample/resource.ja create mode 100644 ext/tk/sample/tkoptdb.rb diff --git a/ext/tk/MANIFEST b/ext/tk/MANIFEST index 4f7fca4430..def3d3ddb0 100644 --- a/ext/tk/MANIFEST +++ b/ext/tk/MANIFEST @@ -27,5 +27,8 @@ sample/tkfrom.rb sample/tkhello.rb sample/tkline.rb sample/tkmenubutton.rb +sample/tkoptdb.rb +sample/resource.ja +sample/resource.en sample/tktimer.rb sample/tktimer2.rb diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb index dabc78753c..d7531b2f8f 100644 --- a/ext/tk/lib/tk.rb +++ b/ext/tk/lib/tk.rb @@ -2676,26 +2676,43 @@ module TkOptionDB @@resource_proc_class = Class.new class << @@resource_proc_class private :new - + CARRIER = '.'.freeze METHOD_TBL = {} ADD_METHOD = false SAFE_MODE = 4 + def __closed_block_check__(str) + depth = 0 + str.scan(/[{}]/){|x| + if x == "{" + depth += 1 + elsif x == "}" + depth -= 1 + end + if depth <= 0 && !($' =~ /\A\s*\Z/) + fail RuntimeError, "bad string for procedure : #{str.inspect}" + end + } + str + end + def __check_proc_string__(str) # If you want to check the proc_string, do it in this method. + # Please define this in the block given to 'new_proc_class' method. str end def method_missing(id, *args) res_proc = self::METHOD_TBL[id] unless res_proc.kind_of? Proc - if id == :new || (!self::METHOD_TBL.has_key?(id) && !self::ADD_METHOD) + if id == :new || !(self::METHOD_TBL.has_key?(id) || self::ADD_METHOD) raise NoMethodError, "not support resource-proc '#{id.id2name}' for #{self.name}" end - proc_str = TkOptionDB.get(self::CARRIER, id.id2name, '') + proc_str = TkOptionDB.get(self::CARRIER, id.id2name, '').strip proc_str = '{' + proc_str + '}' unless /\A\{.*\}\Z/ =~ proc_str + proc_str = __closed_block_check__(proc_str) proc_str = __check_proc_string__(proc_str) res_proc = eval 'Proc.new' + proc_str self::METHOD_TBL[id] = res_proc @@ -2706,10 +2723,11 @@ module TkOptionDB }.call end - private :__check_proc_string__, :method_missing + private :__closed_block_check__, :__check_proc_string__, :method_missing end + @@resource_proc_class.freeze - def new_proc_class(klass, func, safe = 4, add = false, parent = nil) + def __create_new_class(klass, func, safe = 4, add = false, parent = nil) klass = klass.to_s if klass.kind_of? Symbol unless (?A..?Z) === klass[0] fail ArgumentError, "bad string '#{klass}' for class name" @@ -2733,21 +2751,86 @@ module TkOptionDB METHOD_TBL = {} ADD_METHOD = #{add} SAFE_MODE = #{safe} - %w(#{func_str}).each{|f| METHOD_TBL.delete(f.intern) } + %w(#{func_str}).each{|f| METHOD_TBL[f.intern] = nil } end EOD if parent.kind_of?(Class) && parent <= @@resource_proc_class - parent.class_eval body - eval parent.name + '::' + klass + parent.class_eval(body) + eval(parent.name + '::' + klass) else - eval body - eval 'TkOptionDB::' + klass + eval(body) + eval('TkOptionDB::' + klass) end end + module_function :__create_new_class + private_class_method :__create_new_class + + def __remove_methods_of_proc_class(klass) + # for security, make these methods invalid + class << klass + attr_reader :class_eval, :name, :superclass, + :ancestors, :const_defined?, :const_get, :const_set, + :constants, :included_modules, :instance_methods, + :method_defined?, :module_eval, :private_instance_methods, + :protected_instance_methods, :public_instance_methods, + :remove_const, :remove_method, :undef_method, + :to_s, :inspect, :display, :method, :methods, + :instance_eval, :instance_variables, :kind_of?, :is_a?, + :private_methods, :protected_methods, :public_methods + end + end + module_function :__remove_methods_of_proc_class + private_class_method :__remove_methods_of_proc_class + + RAND_BASE_CNT = [0] + RAND_BASE_HEAD = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + RAND_BASE_CHAR = RAND_BASE_HEAD + 'abcdefghijklmnopqrstuvwxyz0123456789_' + def __get_random_basename + name = '%s%03d' % [RAND_BASE_HEAD[rand(RAND_BASE_HEAD.size),1], + RAND_BASE_CNT[0]] + len = RAND_BASE_CHAR.size + (6+rand(10)).times{ + name << RAND_BASE_CHAR[rand(len),1] + } + RAND_BASE_CNT[0] = RAND_BASE_CNT[0] + 1 + name + end + module_function :__get_random_basename + private_class_method :__get_random_basename + + # define new proc class : + # If you want to modify the new class or create a new subclass, + # you must do such operation in the block parameter. + # Because the created class is flozen after evaluating the block. + def new_proc_class(klass, func, safe = 4, add = false, parent = nil, &b) + new_klass = __create_new_class(klass, func, safe, add, parent) + new_klass.class_eval(&b) if block_given? + __remove_methods_of_proc_class(new_klass) + new_klass.freeze + new_klass + end module_function :new_proc_class + + def eval_under_random_base(parent = nil, &b) + new_klass = __create_new_class(__get_random_basename(), + [], 4, false, parent) + ret = new_klass.class_eval(&b) if block_given? + __remove_methods_of_proc_class(new_klass) + new_klass.freeze + ret + end + module_function :eval_under_random_base + + def new_proc_class_random(klass, func, safe = 4, add = false, &b) + eval_under_random_base(){ + TkOption.new_proc_class(klass, func, safe, add, self, &b) + } + end + module_function :new_proc_class_random end TkOption = TkOptionDB +TkResourceDB = TkOptionDB module TkTreatFont def font_configinfo @@ -3638,7 +3721,6 @@ class TkToplevel#{str.tainted?} : " + str + end + end +} + +TkFrame.new(:class=>'BtnFrame'){|f| + pack(:padx=>5, :pady=>5) + TkButton.new(:parent=>f, :widgetname=>'hello'){ + command proc{ + print "($SAFE=#{$SAFE}) : " + cmd.show_msg(TkOptionDB.inspect) + } + pack(:fill=>:x, :padx=>10, :pady=>10) + } + TkButton.new(:command=>proc{print "($SAFE=#{$SAFE}) : "; cmd.bye_msg; exit}, + :parent=>f, :widgetname=>'quit'){ + pack(:fill=>:x, :padx=>10, :pady=>10) + } +} + +Tk.mainloop