root/platform/tdiary/filter/antirefspam.rb @ 5585

Revision 5585, 9.4 kB (checked in by drry, 7 years ago)

platform/tdiary/filter/rblcheck.rb
platform/tdiary/filter/antirefspam.rb: removed an unnecessary prop svn:executable.

Line 
1#
2# antirefspamfilter.rb
3#
4# Copyright (c) 2004-2005 T.Shimomura <redbug@netlife.gr.jp>
5# You can redistribute it and/or modify it under GPL2.
6# Please use version 1.0.0 (not 1.0.0G) if GPL doesn't want to be forced on me.
7#
8
9require 'net/http'
10require 'uri'
11
12module TDiary
13  module Filter
14
15    class AntirefspamFilter < Filter
16      # 有効にすると指定したファイルにデバッグ情報文字列を追記する
17      def debug_out(filename, str)
18        if $debug
19          filename = File.join(@conf.data_path,"AntiRefSpamFilter",filename)
20          File::open(filename, "a+") {|f|
21            f.puts str
22          }
23        end
24      end
25
26      # str に指定された文字列が適切なリンク先を含んでいるかをチェック
27      def isIncludeMyUrl(str)
28        # str に日記のURLが含まれているかどうか
29        base_url = @conf.base_url
30        unless base_url.empty?
31          if str.include? base_url
32            return true
33          end
34        end
35
36        # str にトップページURLが含まれているかどうか
37        unless @conf.index_page.empty?
38          if /\Ahttps?:\/\// =~ @conf.index_page
39            if str.include? @conf.index_page
40              return true
41            end
42          end
43        end
44
45        # str に許容するリンク先が含まれているかどうか
46        if (myurl = @conf['antirefspam.myurl']) && !myurl.empty?
47          if str.include? myurl
48            return true
49          end
50         
51          url = myurl.gsub("/", "\\/").gsub(":", "\\:")
52          exp = Regexp.new(url)
53          if exp =~ str
54            return true
55          end
56        end
57
58        return false
59      end
60
61      def referer_filter(referer)
62        conf_disable = @conf['antirefspam.disable'] != nil ? @conf['antirefspam.disable'].to_s : ''
63        conf_checkreftable = @conf['antirefspam.checkreftable'] != nil ? @conf['antirefspam.checkreftable'].to_s : ''
64        conf_trustedurl = @conf['antirefspam.trustedurl'] != nil ? @conf['antirefspam.trustedurl'].to_s : ''
65        conf_proxy_server = @conf['antirefspam.proxy_server'] != nil && @conf['antirefspam.proxy_server'].size > 0 ? @conf['antirefspam.proxy_server'].to_s : nil
66        conf_proxy_port = @conf['antirefspam.proxy_port'] != nil && @conf['antirefspam.proxy_port'].size > 0 ? @conf['antirefspam.proxy_port'].to_s : nil
67
68        if conf_disable == 'true'  or    # リンク元チェックが有効ではない場合はスルーする
69           referer == nil          or    # リンク元が無い
70           referer.size <= 1       or    # 一部のアンテナで更新時刻が取れなくなる問題に対応するため、リンク元が1文字以内の場合は許容
71           isIncludeMyUrl(referer)       # 自分の日記内からのリンクは信頼する
72        then
73          return true
74        end
75
76        # "信頼できるURL" を1つずつ取り出してrefererと合致するかチェックする
77        conf_trustedurl.each_line do |trusted|
78          trusted.sub!(/\r?\n/,'')
79          next if trusted =~ /\A(\#|\s*)\z/  # #または空白で始まる行は読み飛ばす
80         
81          # まずは "信頼できる URL" が referer に含まれるかどうか
82          if referer.include? trusted
83            debug_out("trusted", trusted+" (include?) "+referer)
84            return true
85          end
86         
87          # 含まれなかった場合は "信頼できる URL" を正規表現とみなして再チェック
88          begin
89            if referer =~ Regexp.new( trusted.gsub("/", "\\/").gsub(":", "\\:") )
90              debug_out("trusted", trusted+" (=~) "+referer)
91              return true
92            end
93          rescue
94            debug_out("error_config", "trustedurl: "+trusted)
95          end
96        end
97
98        # URL置換リストを見る
99        if conf_checkreftable == 'true'
100          # "URL置換リスト" を1つずつ取り出してrefererと合致するかチェックする
101          @conf.referer_table.each do |url, name|
102            begin
103              if /#{url}/i =~ referer && url != '^(.{50}).*$'
104                debug_out("trusted", url+" (=~referer_table)  "+referer)
105                return true
106              end
107            rescue
108              debug_out("error_config", "referer_table: "+url)
109            end
110          end
111        end
112
113        @work_path = File.join(@conf.data_path,"AntiRefSpamFilter")
114        @spamurl_list = File.join(@work_path,"spamurls")  # referer spam のリンク元一覧
115        @spamip_list  = File.join(@work_path,"spamips")   # referer spam のIP一覧
116        @safeurl_list = File.join(@work_path,"safeurls")  # おそらくは問題のないリンク元一覧
117
118        # ディレクトリ/ファイルが存在しなければ作る
119        unless File.exist? @work_path
120          Dir::mkdir(@work_path)
121        end
122        unless File.exist? @spamurl_list
123          File::open(@spamurl_list, "a").close
124        end
125        unless File.exist? @safeurl_list
126          File::open(@safeurl_list, "a").close
127        end
128
129        uri = URI.parse(referer)
130        temp_filename = File.join(@work_path,uri.host)
131        # チェック時には対象のドメイン名を持った一時ファイルを作る
132        begin
133          File::open(temp_filename, File::RDONLY | File::CREAT | File::EXCL).close
134
135          # 一度 SPAM URL とみなしたら以後は以後は拒否
136          spamurls = IO::readlines(@spamurl_list).map {|url| url.chomp }
137          if spamurls.include? referer
138            return false
139          end
140
141          # 一度 SPAM URL でないと判断したら以後は許可
142          safeurls = IO::readlines(@safeurl_list).map {|url| url.chomp }
143          if safeurls.include? referer
144            return true
145          end
146
147          # リンク元 URL の HTML を引っ張ってくる
148          Net::HTTP.version_1_2   # おまじないらしい
149          body = ""
150          begin
151            Net::HTTP::Proxy(conf_proxy_server, conf_proxy_port).start(uri.host, uri.port) do |http|
152              if uri.path == ""
153                response, = http.get("/")
154              else
155                response, = http.get(uri.request_uri)
156              end
157              body = response.body
158            end
159
160            # body に日記の URL が含まれていなければ SPAM とみなす
161            unless isIncludeMyUrl(body)
162              File::open(@spamurl_list, "a+") {|f|
163                f.puts referer
164              }
165              File::open(@spamip_list, "a+") {|f|
166                f.puts [@cgi.remote_addr, Time.now.utc.strftime("%Y/%m/%d %H:%M:%S UTC")].join("\t")
167              }
168              return false
169            else
170              File::open(@safeurl_list, "a+") {|f|
171                f.puts referer
172              }
173            end
174          rescue
175            # エラーが出た場合は @spamurl_list に入れない&リンク元にも入れない
176            return false
177          end
178
179        rescue StandardError, TimeoutError
180          # 現在チェック中なら、今回はリンク元に勘定しない
181          return false
182        ensure
183          begin
184            File::delete(temp_filename)
185          rescue
186          end
187        end
188
189        return true
190      end
191
192
193
194      def log_spamcomment( diary, comment )
195        @work_path = File.join(@conf.data_path,"AntiRefSpamFilter")
196        @spamcomment_list = File.join(@work_path,"spamcomments")  # comment spam の一覧
197
198        # ディレクトリ/ファイルが存在しなければ作る
199        unless File.exist? @work_path
200          Dir::mkdir(@work_path)
201        end
202        unless File.exist? @spamcomment_list
203          File::open(@spamcomment_list, "a").close
204        end
205
206        File::open(@spamcomment_list, "a+") {|f|
207          f.puts "From: "+comment.name+" <"+comment.mail+">"
208          f.puts "To: "+diary.date.to_s
209          f.puts "Date: "+comment.date.to_s
210          f.puts comment.body
211          f.puts ".\n\n"
212        }
213      end
214
215      def comment_filter( diary, comment )
216        # ツッコミに日本語(ひらがな/カタカナ)が含まれていなければ不許可
217        if @conf['antirefspam.comment_kanaonly'] != nil
218          if @conf['antirefspam.comment_kanaonly'].to_s == 'true'
219            unless comment.body =~ /[ぁ-んァ-ヴー]/
220              log_spamcomment( diary, comment )
221              return false
222            end
223          end
224        end
225
226        # ツッコミの文字数が指定した上限以内でないなら不許可
227        maxsize = @conf['antirefspam.comment_maxsize'].to_i
228        if maxsize > 0
229          unless comment.body.size <= maxsize
230            log_spamcomment( comment )
231            return false
232          end
233        end
234
235        # NGワードが1つでも含まれていたら不許可
236        if @conf['antirefspam.comment_ngwords'] != nil
237          ngwords = @conf['antirefspam.comment_ngwords']
238          ngwords.to_s.each_line do |ngword|
239            ngword.sub!(/\r?\n/,'')
240            if comment.body.downcase.include? ngword.downcase
241              log_spamcomment( comment )
242              return false
243            end
244
245            # 含まれなかった場合は "NGワード" を正規表現とみなして再チェック
246            begin
247              if comment.body =~ Regexp.new( ngword, Regexp::MULTILINE )
248                log_spamcomment( comment )
249                return false
250              end
251            rescue
252              debug_out("error_config", "comment_ngwords: "+ngword)
253            end
254          end
255        end
256
257        return true
258      end
259    end
260  end
261end
Note: See TracBrowser for help on using the browser.