This file is indexed.

/usr/share/tdiary/contrib/plugin/search-default.rb is in tdiary-contrib 3.2.2-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# -*- coding: utf-8 -*-
#
# search-default.rb - site search plugin sample using DefaultIO.
#
# Copyright (C) 2003-2005 Minero Aoki
# Copyright (C) 2012, TADA Tadashi <t@tdtds.jp>
# You can redistribute it and/or modify it under GPL2.
#
#

def search_title
	'全文検索'
end

class WrongQuery < StandardError; end

module DefaultIOSearch

	module_function
	def setup_patterns(query)
		patterns = split_string(query).map {|pat|
			check_pattern pat
			Regexp.new( Regexp.quote(pat), Regexp::IGNORECASE )
		}
		raise WrongQuery, 'no pattern' if patterns.empty?
		raise WrongQuery, 'too many sub patterns' if patterns.length > 8
		patterns
	end

	def check_pattern(pat)
		raise WrongQuery, 'no pattern' unless pat
		raise WrongQuery, 'empty pattern' if pat.empty?
		raise WrongQuery, "pattern too short: #{pat}" if pat.length < 2
		raise WrongQuery, 'pattern too long' if pat.length > 128
	end

	def split_string(str)
		str.split(/[\s ]+/ou).reject {|w| w.empty? }
	end

	INF = 1 / 0.0

	def match_components(patterns, data_path)
		foreach_diary_from_latest(data_path) do |diary|
			next unless diary.visible?
			num = 1
			diary.each_section do |sec|
				if patterns.all? {|re| re =~ sec.to_src }
					yield diary, fragment('p', num), sec
				end
				num += 1
			end
			diary.each_visible_comment(INF) do |cmt, num|
				if patterns.all? {|re| re =~ cmt.body }
					yield diary, fragment('c', num), cmt
				end
			end
		end
	end

	def fragment(type, num)
		sprintf('%s%02d', type, num)
	end

	#
	# tDiary Implementation Dependent
	#
	def foreach_diary_from_latest(data_path, &block)
		foreach_data_file(data_path.sub(%r</+\z>, '')) do |path|
			read_diaries(path).sort_by {|diary| diary.date }.reverse_each(&block)
		end
	end

	def foreach_data_file(data_path, &block)
		Dir.glob("#{data_path}/[0-9]*/*.td2").sort.reverse_each do |path|
			yield path.untaint
		end
	end

	def read_diaries(path)
		d = nil
		diaries = {}
		load_tdiary_textdb(path) do |header, body|
			begin
				d = diary_class(header['Format']).new(header['Date'], '', body)
			rescue ArgumentError
				next
			end
			d.show(header['Visible'] != 'false')
			diaries[d.ymd] = d
		end
		(Years[d.y] ||= []).push(d.m) if d
		load_comments diaries, path
		diaries.values
	end

	DIARY_CLASS_CACHE = {}

	def diary_class(style)
		c = DIARY_CLASS_CACHE[style]
		return c if c
		require "tdiary/style/#{style.downcase}_style.rb"
		c = eval("TDiary::#{style.capitalize}Diary")
		c.__send__(:include, DiaryClassDelta)
		DIARY_CLASS_CACHE[style] = c
		c
	end

	module DiaryClassDelta
		def ymd
			date().strftime('%Y%m%d')
		end

		def y_m_d
			date().strftime('%Y-%m-%d')
		end

		def y
			'%04d' % date().year
		end

		def m
			'%02d' % date().month
		end
	end

	def load_comments(diaries, path)
		cmtfile = path.sub(/2\z/, 'c')
		return unless File.file?(cmtfile)
		load_tdiary_textdb(cmtfile) do |header, body|
			c = TDiary::Comment.new(header['Name'], header['Mail'], body,
															Time.at(header['Last-Modified'].to_i))
			c.show = (header['Visible'] != 'false')
			d = diaries[header['Date']]
			d.add_comment c if d
		end
	end

	def load_tdiary_textdb(path)
		File.open(path) {|f|
			ver = f.gets.strip
			raise "unkwnown format: #{ver}" unless ver == 'TDIARY2.00.00' or ver == 'TDIARY2.01.00'
			f.each('') do |header|
				h = {}
				header.untaint.strip.each_line do |line|
					begin
						n, v = *line.split(':', 2)
					rescue ArgumentError
						next
					end
					h[n.strip] = v.strip
				end
				body = f.gets("\n.\n").chomp(".\n").untaint
				yield h, body
			end
		}
	end

	def short_html(component)
		# Section classes do not have common superclass, we can't use class here.
		case component.class.name
		when /Section/
			section = component
			if section.subtitle
				sprintf('%s<br>%s',
					tdiary2text(section.subtitle_to_html),
					tdiary2text(section.body_to_html))
			else
				tdiary2text(section.body_to_html)
			end
		when /Comment/
			cmt = component
			shorten(escape((cmt.name + ': ' + cmt.body)))
		else
			raise "must not happen: #{component.class}"
		end
	end

	def tdiary2text(html)
		re = Regexp.new('<[^>]*>', Regexp::EXTENDED)
		shorten(apply_tdiary_plugins(html).gsub(re, ''))
	end

	Years = {}

	TDiary::Plugin.__send__(:public, :apply_plugin)
	def apply_tdiary_plugins(html)
		#@plugin.apply_plugin(html, false)
		html
	end

	@plugin = nil

	#
	# Utils
	#
	HTML_ESCAPE_TABLE = {
		'&' => '&amp;',
		'<' => '&lt;',
		'>' => '&gt;',
		'"' => '&quot;'
	}

	def escape(str)
		tbl = HTML_ESCAPE_TABLE
		str.gsub(/[&"<>]/) {|ch| tbl[ch] }
	end

	def shorten(str, len = 200)
		matched = str.gsub( /\n/, ' ' ).scan( /^.{0,#{len - 3}}/u )[0]
		if $'.nil? || $'.empty?
			matched
		else
			matched + '...'
		end
	end
end

def search_input_form( q )
	r = <<-HTML
		<form method="GET" action="#{@conf.index}"><div>
			検索キーワード:
			<input name="q" value="#{h q}">
			<input type="submit" value="OK">
		</div></form>
	HTML
end

def search_result
	unless @conf.io_class == TDiary::DefaultIO
		return %Q|<p class="message">could not use this plugin under #{@conf.io_class}.</p>| 
	end

	query = CGI::unescape( @cgi.params['q'][0] )
	start = CGI::unescape( @cgi.params['start'][0] || '0' ).to_i

	begin
		patterns = DefaultIOSearch::setup_patterns(query)
		r = search_input_form( query )

		r << '<dl class="search-result autopagerize_page_element">'
		count = 0
		too_many = false
		DefaultIOSearch::match_components(patterns, @conf.data_path) do |diary, fragment, component|
			count += 1
			if count > 50 # TO MANY HITS
				too_many = true
				break
			end
			href = @conf.index + anchor( "#{diary.ymd}#{fragment}" )
			r << %Q|<dt><a href="#{href}">#{h diary.y_m_d}</a></dt>|
			r << %Q|<dd>#{DefaultIOSearch::short_html(component)}</dd>|
		end
		r << '</dl>'

		r << '<div class="search-navi">'
		r << "<p>#{too_many ? 'too many' : count} hits.</p>"
		r << '</div>'

		r
	rescue WrongQuery
		search_input_form( query ) + %Q|<p class="message">#{$!.message}</p>|
	end
end