Subversion Repositories vgwhois

Rev

Rev 5 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
#!/bin/bash
2
 
3
#
11 daniel-mar 4
#  VGWhoIs (ViaThinkSoft Global WhoIs, a fork of generic Whois / gwhois)
5 daniel-mar 5
#  Maintenance / Developer utilities
2 daniel-mar 6
#
5 daniel-mar 7
#  (c) 2012-2019 by Daniel Marschall, ViaThinkSoft <info@daniel-marschall.de>
2 daniel-mar 8
#
5 daniel-mar 9
#  License: https://www.gnu.org/licenses/gpl-2.0.html (GPL version 2)
2 daniel-mar 10
#
11
 
12
# TODO: use >&2
13
 
14
# TODO: anzeigen von wann der whois output ist... "last checked" -> "last activity" (da ein user-mode-batch-approval ja kein recheck beinhaltet)
15
 
16
DIR=$( dirname "$0" )
17
 
4 daniel-mar 18
ANNOTATION_FILE="$DIR/../../config/testcases.annot"
2 daniel-mar 19
TESTCASE_CACHE_FILE="$DIR/../../.cache/testcases"
20
 
21
# ---
22
 
23
function resetconsole {
24
	# http://superuser.com/questions/122911/bash-reset-and-clear-commands
25
 
26
	# clear
27
 
28
	# clears the console screen, but not the scrollback buffer
29
	# this is actually the escape code to "reset" the terminal
30
	echo -en "\ec"
31
 
32
	# clears the scrollback buffer, but not the console screen
33
	# screen content remains, and cursor position remains at its last position
34
	echo -en "\e[3J"
35
}
36
 
37
function showPatternHighlighted {
38
	A=()
39
 
40
	patterns=$( "$DIR"/allpatterns )
41
	for p in ${patterns[@]}; do
42
		A+=("$p");
43
	done
44
 
45
	out=""
46
	while IFS= read data; do
47
		out="$out$data"$'\n'
48
	done
49
 
50
	# Attention: "less" or "nano" does not work with Unicode for Asian languages :-(
51
	# Maybe this helps? http://serverfault.com/questions/414760/how-to-make-the-less-command-handle-utf-8 .
52
	# But then it still can't handle colors...
53
	echo "$out" | "$DIR"/highlighter "${A[@]}"
54
}
55
 
56
function question {
4 daniel-mar 57
	if [ -f "$ANNOTATION_FILE" ]; then
58
		ANNOTS=$( cat "$ANNOTATION_FILE" | grep -E "^$query:" )
2 daniel-mar 59
		if [ "$ANNOTS" != "" ]; then
60
			echo ""
61
			echo "********************"
4 daniel-mar 62
			echo "Notes in $ANNOTATION_FILE :"
2 daniel-mar 63
			OLDIFS="$IFS"
64
			IFS=$'\n'
65
			for p in ${ANNOTS[@]}; do
66
				pos="${#query}"
67
				(( pos += 2 ))
68
				echo "   ${p:$pos}";
69
			done
70
			IFS="$OLDIFS"
71
			echo "********************"
72
		fi
73
	fi
74
	echo ""
75
	echo "Keep in mind to only set this result as expected state, when:"
76
	echo "   (1) The output is well formatted OR when you add an entry to the ToDo"
77
	echo "       list when the output is 'OK, but not optimal'."
78
	echo "   (2) For web-requests you should also have a testcase which shows"
79
	echo "       non-existant domains"
80
	echo "   (3) All handles should have testcases too, or it should be noted that"
81
	echo "       specific handles cannot be queried by the NIC."
82
	echo "   (4) There should be no volative stuff like dates, serving whois server"
83
	echo "       names or your IP address in the output."
84
	echo "       Otherwise you should grep them away in $0 before you approve this testcase."
85
	echo "   (5) In notices, URLs/information must be correct"
86
	echo "   (6) Highlighting shall be working, otherwise the patterns or $DIR/highlighter needs to be changed"
87
	while true; do
88
		echo ""
89
		if [ -f "$E" ]; then
90
			echo "Do you want to override the expected result? ([y]es, [n]o, [s]kip, [r]etry, [a]dd note, reset[t], e[x]it)"
91
		else
92
			# TODO: braucht man hier reset?
93
			echo "Do you want to define this as the expected result? ([y]es, [n]o, [s]kip, [r]etry, [a]dd note, rese[t], e[x]it)"
94
		fi
95
		read yn
96
		case $yn in
97
			[Yy]* )
98
				cat "$T" > "$E"
99
				rm "$T"
100
				if [ -f "$Q" ] && [ "$Q" != "$T" ]; then
101
					rm "$Q"
102
				fi
103
				touch "$tsfile"
104
				return 0
105
				;;
106
			[Nn]* )
107
				rm "$T"
108
				return 1
109
				;;
110
			[Ss]* )
111
				return 1
112
				;;
113
			[Rr]* )
114
				bakmode="$mode"
115
				mode="i"
116
				bakforce="$force"
117
				force=1
118
				process "$query"
119
				RES=$?
120
				mode="$bakmode"
121
				force="$bakforce"
122
				return $RES
123
				;;
124
			[Aa]* )
125
				echo "Please enter a note you want to add or empty string if you want to cancel"
126
				read note
127
 
128
				if [ "$note" == "" ]; then
129
					echo "Cancelled"
130
				else
131
					echo "Note added to testcase of $query"
4 daniel-mar 132
					echo "$query: $note" >> "$ANNOTATION_FILE"
2 daniel-mar 133
				fi
134
				;;
135
			[Xx]* )
136
				return 2
137
				;;
138
			[Tt]* )
139
				# Clears the expected state
140
				#rm $TESTCASE_CACHE_FILE/*/$query
141
				rm "$E"
142
				rm "$Q"
143
				rm "$tsfile"
144
				# Now retry
145
				# TODO: codeduplikat vermeiden?
146
				bakmode="$mode"
147
				mode="i"
148
				bakforce="$force"
149
				force=1
150
				process "$query"
151
				RES=$?
152
				mode="$bakmode"
153
				force="$bakforce"
154
				return $RES
155
				;;
156
			* )
157
				echo "Please answer with the letter written in square brackets."
158
				;;
159
		esac
160
	done
161
}
162
 
163
function question2 {
164
	while true; do
165
		echo ""
11 daniel-mar 166
		echo "vgwhois might already have been fixed. Do you want to enforce a vgwhois recheck now? ([y]es, [n]o, e[x]it)"
2 daniel-mar 167
		read yn
168
		case $yn in
169
			[YyRr]* )
170
				if [ -f "$Q" ]; then
171
					rm "$Q"
172
				fi
173
				bakmode="$mode"
174
				mode="i"
175
				bakforce="$force"
176
				force=1
177
				process "$query"
178
				RES=$?
179
				mode="$bakmode"
180
				force="$bakforce"
181
				return $RES
182
				;;
183
			[NnSs]* )
184
				return 1
185
				;;
186
			[Xx]* )
187
				return 2
188
				;;
189
			* )
190
				echo "Please answer with the letter written in square brackets."
191
				;;
192
		esac
193
	done
194
}
195
 
196
function unquote {
197
	echo "$1" | sed "s/^'\(.*\)'$/\1/";
198
}
199
 
200
function process {
201
	if [ "$mode" != "b" ]; then
202
		# TODO: diese meldung soll nicht kommen, wenn einfach nur da steht "does not need a recheck" now...
203
		resetconsole
204
	fi
205
 
206
	query="$1"
207
	echo "Query: $query"
208
 
209
	E="$TESTCASE_CACHE_FILE/expected/$query"
210
	Q="$TESTCASE_CACHE_FILE/problems/$query"
211
	tsfile="$TESTCASE_CACHE_FILE/checktimestamps/$query"
212
 
213
	if [ "$mode" == "u" ]; then
214
		T="$Q"
215
		if [ ! -f "$T" ]; then
216
			echo "There is no action needed by the user"
217
			return 0
218
		fi
219
	else
11 daniel-mar 220
		T=$( mktemp --suffix='.vgwhoisTC' )
2 daniel-mar 221
 
222
		# In interactive/background mode, we will always do a webrequest when there is no problem and no expectation file without respect of the last checktime, to avoid that the status monitor will show entries with "no expectation file" when the user pressed "no", and then he would have to wait 7 days until "batch u" works again.
223
		# TODO: how to do that in 1 line?
224
		CALL_GWI=0
225
		if [ $force -eq 1 ]; then
226
			CALL_GWI=1
227
		else
228
			test \( ! -f "$Q" \) -a \( ! -f "$E" \)
229
			if [ $? -eq 0 ]; then
230
				CALL_GWI=1
231
			elif [ ! -f "$tsfile" ] || [ $( stat --format=%Y "$tsfile" ) -le $(( $( date +%s ) - $recheck_time )) ]; then
232
				if [ -f "$Q" ] && [ $( stat --format=%Y "$Q" ) -gt $(( $( date +%s ) - $recheck_time )) ]; then
233
					CALL_GWI=0
234
				else
235
					CALL_GWI=1
236
				fi
237
			fi
238
		fi
239
		if [ $CALL_GWI -eq 1 ]; then
11 daniel-mar 240
			echo "... calling vgwhois ..."
241
			# We have to use loc_vgwhois to allow $trytor to work correctly.
2 daniel-mar 242
			# The torifiers "torify" and "usewithtor" always outputting bogus "libtorsocks" warning messages which would be saved in the output
243
			# "vtor" - if applied to this script - can only filter them from STDOUT and STDERR, but not intercept this "&>" pipe
11 daniel-mar 244
			# So we have to use this loc_vgwhois script, where we torify manually
2 daniel-mar 245
			# Also, it is important that we do the warning message filtering in this step, because in the final output the lines will be colored/highlighted, and therefore "vtor" cannot grep them correctly anymore.
246
			# Note: regex only valid in the years 1000-1099, 1900-2099, 2900-2999
247
			# grep away volative stuff like server names or times
248
			(
11 daniel-mar 249
				"$DIR"/loc_vgwhois "$query" \
2 daniel-mar 250
					| grep -v "This query was served by " \
251
					| grep -v "(c)[12][90][0-9]\{2\}" \
252
					| grep -v "[12][90][0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}" \
253
					| grep -v "\[[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\} REQUEST\]" \
254
					| grep -v "RL Net \[.*\] - RL IP \[.*\]" \
255
					| grep -v "% Query time:" \
256
					| grep -v "% request from" \
257
					| grep -v "% Last update of whois database:" \
258
					| grep -v "%AM TLD whois server #" \
259
					| grep -v "Last updated on " \
260
					| grep -v "This is the Ukrainian Whois query server #" \
261
					| grep -v "Last update of whois database" \
262
					| grep -v "Query time:" \
263
					| grep -v "nsstat:" \
264
					| grep -v "nslastaa:"
265
			) &> "$T"
266
			# auskommentiert wegen problem: wenn man einen recheck wegen perl errors macht und dann abbricht, ist dann der PROBLEM state gespeichert? nein, er wurde im i-mode gelöscht!
267
			# -> lösung ?rm "$Q" nur im erfolgsfall
268
			#			if [ -f "$Q" ]; then
269
			#				rm "$Q"
270
			#			fi
271
 
11 daniel-mar 272
			# behoben? in mode=i and mode=u, should the output of a new vgwhois request be saved into the problem file, if the user cancels? (to avoid a second web request?)
2 daniel-mar 273
			#       also, when doing a recheck after a perl error, and then cancel, the problem-file will not be updated!
274
			# TODO: was ist besser?
275
			#if [ ! -f "$Q" ] && [ "$T" != "$Q" ]; then
276
			if [ "$T" != "$Q" ]; then
277
				cat "$T" > "$Q"
278
			fi
279
 
280
 
281
		else
282
			rm "$T"
283
			if [ "$mode" == "b" ]; then
284
				echo "The query does not need a recheck now. Use --force to enforce it."
285
#				if [ -f "$Q" ] && [ "$Q" != "$T" ]; then
286
#					rm "$Q"
287
#				fi
288
				return 0
289
			else
290
				T="$Q"
291
				if [ ! -f "$T" ]; then
292
					echo "There is no action needed by the user"
293
					return 0
294
				fi
295
			fi
296
		fi
297
	fi
298
 
299
	WARNINGS=()
300
	cat "$T" | grep -E "at /(bin|usr|etc|var)/\S+ line" > /dev/null
301
	if [ $? -eq 0 ]; then
11 daniel-mar 302
		WARNINGS+=("Perl errors found in vgwhois output!")
2 daniel-mar 303
	fi
304
	cat "$T" | head -n 1 | grep -E "^("$'\xEF\xBB\xBF'"){0,1}Process query: '$query'" > /dev/null
305
	if [ $? -ne 0 ]; then
11 daniel-mar 306
		WARNINGS+=("The vgwhois output does not begin with 'Process query'!")
2 daniel-mar 307
	fi
11 daniel-mar 308
	cat "$T" | grep "vgwhois remarks: If this is a valid domainname or handle, please file a bug report." > /dev/null
2 daniel-mar 309
	if [ $? -eq 0 ]; then
310
		WARNINGS+=("No pattern match!")
311
	fi
312
	if [ ${#WARNINGS[@]} -gt 0 ]; then
313
		if [ "$T" != "$Q" ]; then
314
			cat "$T" > "$Q"
315
			rm "$T"
316
		fi
317
#		if [ "$mode" != "b" ]; then
318
#			resetconsole
319
#		fi
320
		OLDIFS="$IFS"
321
		IFS=$'\n'
322
		for p in ${WARNINGS[@]}; do
323
			echo "WARNING: $p"
324
		done
325
		IFS="$OLDIFS"
326
		if [ "$mode" == "b" ]; then
327
			echo "Saved for later analysis."
328
			# TODO: problem: this will prevent another background runner to check it again, but it will prevent the user-batch to investigate this, too?!!
329
#			touch "$tsfile"
330
			return 1
331
		else
332
			# Hinweis: niemals vor "question" oder "question2" ein ts aktualisieren. denn wenn man in question* einen recheck beantragt und dann abbricht, dann würde dieser aktualisierte ts bewirken, dass der testcase erst wieder in 7 tagen angezeigt wird
333
			echo ""
334
			cat "$Q" | showPatternHighlighted
335
			question2
336
			return $?
337
		fi
338
	fi
339
 
340
	if [ -f "$E" ]; then
341
		D=$( date -r "$E" )
342
		echo "Compare with results of $D"
343
		diff -U 0 "$E" "$T" | showPatternHighlighted
344
		if [ ${PIPESTATUS[0]} -eq 0 ]; then
345
			echo "OK! No differences found!"
346
			rm "$T"
347
			touch "$tsfile"
348
			if [ -f "$Q" ] && [ "$Q" != "$T" ]; then
349
				rm "$Q"
350
			fi
351
			return 0
352
		else
353
			echo "Problem! Differences found!"
354
 
355
			if [ "$mode" == "b" ]; then
356
				echo "Saved for later analysis."
357
				if [ "$T" != "$Q" ]; then
358
					cat "$T" > "$Q"
359
					rm "$T"
360
				fi
361
 
362
				# TODO: problem: this will prevent another background runner to check it again, but it will prevent the user-batch to investigate this, too?!!
363
#				touch "$tsfile"
364
				return 1
365
			else
366
				if [ "$T" != "$Q" ]; then
367
					rm "$T"
368
				fi
369
 
370
				question
371
				return $?
372
			fi
373
		fi
374
	else
375
		if [ "$mode" == "b" ]; then
376
			echo "This query has no expected state. Please define one."
377
			echo "Saved for later analysis."
378
			if [ "$T" != "$Q" ]; then
379
				cat "$T" > "$Q"
380
				rm "$T"
381
			fi
382
			# TODO: problem: this will prevent another background runner to check it again, but it will prevent the user-batch to investigate this, too?!!
383
#			touch "$tsfile"
384
			return 1
385
		else
386
#			resetconsole
387
			echo "This query has no expected state. Please define one."
11 daniel-mar 388
			echo "This is the current output of vgwhois:"
2 daniel-mar 389
 
390
			echo ""
391
			cat "$T" | showPatternHighlighted
392
			echo ""
393
 
394
			if [ "$T" != "$Q" ]; then
395
				rm "$T"
396
			fi
397
 
398
			question
399
			return $?
400
		fi
401
	fi
402
}
403
 
404
function HumanReadableTime {
405
	local seconds=$1
406
	local days=$(($seconds/86400))
407
	seconds=$(($seconds-($days*86400) ))
408
	local hours=$(($seconds/3600))
409
	seconds=$((seconds-($hours*3600) ))
410
	local minutes=$(($seconds/60))
411
	seconds=$(( $seconds-($minutes*60) ))
412
 
413
	# echo -n "${days}D ${hours}H ${minutes}M ${seconds}S"
414
	if [ $days -gt 0 ]; then
415
		if [ $hours -gt 12 ]; then
416
			((days++));
417
		fi
418
		echo -n "${days} days"
419
	elif [ $hours -gt 0 ]; then
420
		if [ $minutes -gt 30 ]; then
421
			((hours++));
422
		fi
423
		echo -n "${hours} hours"
424
	elif [ $minutes -gt 0 ]; then
425
		if [ $seconds -gt 30 ]; then
426
			((minutes++));
427
		fi
428
		echo -n "${minutes} minutes"
429
	else
430
		echo -n "a few seconds"
431
	fi
432
}
433
 
434
function usage {
435
	. "$DIR/../../config/testcases.conf"
436
 
437
	hf_recheck_time=$( HumanReadableTime $recheck_time )
438
 
439
	echo "Syntax: $0 options query"
440
	echo "	-h|--help"
441
	echo "	-m|--mode mode"
442
	echo "		Default: $mode"
443
	echo "		Mode: i = interactive (download and then show results/ask the developer if the result is OK)"
444
	echo "		      b = background (download only and save the results for later query)"
445
	echo "		      u = user-dialog only (only ask the developer if there are questions, e.g. which were generated by mode b)."
446
	echo "	-r|--rechecktime seconds"
447
	echo "		Default: $recheck_time = approx. $hf_recheck_time)"
448
	echo "	-f|--force"
11 daniel-mar 449
	echo "		Ignores --rechecktime and forces a new vgwhois request"
2 daniel-mar 450
	echo "		Default: $force"
451
}
452
 
453
# ---
454
 
455
if [ $# -eq 0 ]; then
456
	usage;
457
	exit;
458
fi
459
 
460
if [ ! -d "$TESTCASE_CACHE_FILE/checktimestamps" ]; then
3 daniel-mar 461
	mkdir -p "$TESTCASE_CACHE_FILE/checktimestamps"
2 daniel-mar 462
fi
463
 
464
if [ ! -d "$TESTCASE_CACHE_FILE/problems" ]; then
3 daniel-mar 465
	mkdir -p "$TESTCASE_CACHE_FILE/problems"
2 daniel-mar 466
fi
467
 
468
if [ ! -d "$TESTCASE_CACHE_FILE/expected" ]; then
3 daniel-mar 469
	mkdir -p "$TESTCASE_CACHE_FILE/expected"
2 daniel-mar 470
fi
471
 
472
# defaults
473
. "$DIR/../../config/testcases.conf"
474
 
475
PARAMS=( "$@" );
476
optarr=( $( getopt --name "$0" --options 'hfr:m:' --long 'help,force,rechecktime:,mode:' -- "${PARAMS[@]}" 2> /dev/null ) );
477
 
478
# Now process the arguments
479
i=0;
480
while true; do
481
	case "${optarr[$i]}" in
482
		-h|--help)
483
			usage;
484
			exit 0;
485
			;;
486
		-f|--force)
487
			force=1;
488
			((i++));
489
			;;
490
		-r|--rechecktime)
491
			recheck_time=$( unquote "${optarr[$((i+1))]}" );
492
			((i=i+2));
493
			;;
494
		-m|--mode)
495
			mode=$( unquote "${optarr[$((i+1))]}" );
496
 
497
			if [ "$mode" != "i" ] && [ "$mode" != "b" ] && [ "$mode" != "u" ]; then
498
				echo "Invalid mode '$mode'"
499
				usage
500
				exit 2
501
			fi
502
 
503
			((i=i+2));
504
			;;
505
		--)
506
			((i++));
507
			break;
508
			;;
509
		*)
510
			# Should never happen
511
			echo "$0: Internal error while command-line-processing! Please report this error as bug." >&2;
512
			exit 2;
513
			;;
514
	esac
515
done
516
 
517
RESTARGS=${optarr[@]:i}; # i..end
518
CMD="${optarr[i]}";
519
ARG="${optarr[@]:((i+1))}"; # (i+1)..end
520
 
521
 
522
EXITSTATUS=0
523
for X in ${RESTARGS[@]}
524
do
525
	X=$( unquote "$X" )
526
	process "$X"
527
	EC=$?
528
 
529
	if [ $EC -eq 2 ]; then
530
		# user pressed x for exit
531
		exit $EXITSTATUS;
532
	elif [ $EC -gt $EXITSTATUS ]; then
533
		# exitcode = max(exitcodes from processes)
534
		EXITSTATUS=$EC
535
	fi
536
 
537
done
538
 
539
exit $EXITSTATUS