flnews_post_proc 1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,71 @@
1
+ #encoding: UTF-8
2
+ =begin
3
+ /***************************************************************************
4
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
5
+ * This program is free software; you can redistribute it and/or modify *
6
+ * it under the terms of the WTFPL 2.0 or later, see *
7
+ * http://www.wtfpl.net/about/ *
8
+ * *
9
+ * This program is distributed in the hope that it will be useful, *
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
12
+ * *
13
+ ***************************************************************************/
14
+ =end
15
+
16
+ require 'yaml'
17
+ require 'ostruct'
18
+ # --needs the Diffy gem
19
+ require 'diffy'
20
+
21
+ require_relative 'configuration'
22
+ require_relative 'basic_logging'
23
+ require_relative 'headers'
24
+ require_relative 'body'
25
+
26
+ # line-break in the final article
27
+ $LN = "\r\n"
28
+
29
+ # The main application class.
30
+ # Does it.
31
+ class PostProcessor
32
+ # class-level configuration object
33
+ @@config = Configuration.instance
34
+ include BasicLogging
35
+
36
+ def initialize(article_text)
37
+ # for simplicity.
38
+ # separate the headers and the body.
39
+ debug ' initializing headers'
40
+ headers = Headers.new(article_text)
41
+ debug('headers is ' << headers.inspect)
42
+ body = Body.new(article_text)
43
+
44
+ debug('calling headers.update')
45
+ headers.update()
46
+
47
+ newsgroups = headers.newsgroups
48
+
49
+ # Order matters. These actions work on a
50
+ # preliminary version of the article, each
51
+ # one on the result of the previous !
52
+ body.set_intro(newsgroups.intro)
53
+
54
+ # if need be, extract references and footnotes.
55
+ body.handle_references
56
+ body.set_signature(newsgroups.signature)
57
+
58
+ # verify and eventually correct URLs
59
+ body.handle_urls
60
+
61
+ # get the headers and the body as a string.
62
+ # Assemble.
63
+ @article = headers.join << $LN << body.join
64
+
65
+ diff = Diffy::Diff.new(article_text, @article, :context => 2).to_s
66
+ info("\n" << "–" * 20 << "\nDiffs\n" << "–" * 20 << "\n" << diff)
67
+ end
68
+
69
+ attr_reader :article
70
+ end
71
+ # EOF
data/lib/headers.rb ADDED
@@ -0,0 +1,148 @@
1
+ #encoding: UTF-8
2
+ =begin
3
+ /***************************************************************************
4
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
5
+ * This program is free software; you can redistribute it and/or modify *
6
+ * it under the terms of the WTFPL 2.0 or later, see *
7
+ * http://www.wtfpl.net/about/ *
8
+ * *
9
+ * This program is distributed in the hope that it will be useful, *
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
12
+ * *
13
+ ***************************************************************************/
14
+ =end
15
+
16
+ require_relative 'basic_logging'
17
+ require_relative 'configuration'
18
+ require_relative 'newsgroups'
19
+
20
+ # an object of this class represents the headers of a news-article
21
+
22
+ class Headers
23
+ # class-level configuration object
24
+ @@config = Configuration.instance
25
+ include BasicLogging
26
+
27
+ # read the headers from the article
28
+ def initialize(article_text)
29
+
30
+ line = nil
31
+ # transform the article to an array.
32
+ debug('before split, article_text is : ' << article_text)
33
+ line_array = article_text.split($LN)
34
+ debug('after split, line_array is : ' << line_array.inspect)
35
+ # find the first empty line
36
+ end_index = line_array.index {|ele| ele.strip == ''}
37
+ # keep the preceding lines.
38
+ @lines = line_array.slice!(0, end_index)
39
+ debug('headers: ' << @lines.to_s)
40
+
41
+ # headername: headervalue
42
+ @headers = {}
43
+
44
+ # fill the headers Hash from the header-lines.
45
+ # headers may have been line-wrapped.
46
+
47
+ cur_header = nil
48
+ @lines.each do |l|
49
+ # has the header been wrapped?
50
+ if !l.start_with?(/\s+/)
51
+ # header is all before the first colon
52
+ begin
53
+ cur_header = l.match(/^(.*?):/)[1].to_sym
54
+ rescue Exception => ex
55
+ error ("Cannot match a header in line " << l << "(" << ex.message << ")")
56
+ exit false;
57
+ end
58
+ # value is all after the first colon
59
+ # BUGGY: val = l.match(/:(.*)/)[1].strip
60
+ # BUGFIX 3/2024
61
+ val = l.match(/:(.*)/)[1].lstrip
62
+ else # start_with?(' ')
63
+ # a wrapped value is not devided
64
+ # BUGGY: val = l.strip
65
+ # BUGFIX 3/2024
66
+ val = l
67
+ end
68
+ # add value to the existing
69
+ if cur_header && @headers[cur_header]
70
+ @headers[cur_header] += val
71
+ else
72
+ # or add a new value
73
+ @headers[cur_header] = val
74
+ end
75
+ #@headers[l.match(/^(.*?):/)[1].to_sym] = l.match(/:(.*)/)[1].strip
76
+ # h = l.split(':')
77
+ # @headers[h[0].strip.to_sym] = h[1...h.size].join(':').strip
78
+ end
79
+ debug('headers are ' << @headers.to_s)
80
+ @newsgroups = Newsgroups.new(header(:Newsgroups))
81
+ debug('Newsgroups is ' << @newsgroups.inspect)
82
+
83
+ end
84
+
85
+ # returns the value of header 'name'
86
+ def header(name)
87
+ # name must be a symbol.
88
+ if name.respond_to?(:to_sym)
89
+ @headers[name]
90
+ else
91
+ error(name.to_s << ' is not a symbol!')
92
+ nil
93
+ end
94
+ end
95
+
96
+ # Modify headers, if need be.
97
+ def update()
98
+ xnay = @newsgroups.xnay
99
+ debug('xnay should be set now : ' << xnay.to_s)
100
+ if xnay
101
+ @headers["X-No-Archive".to_sym] = xnay
102
+ end
103
+ ch = @@config.CUSTOM_HEADERS
104
+ debug('setting custom headers : ' << ch.inspect)
105
+ if @@config.CUSTOM_HEADERS
106
+ @@config.CUSTOM_HEADERS.each do |pair|
107
+ ch = pair.split(':')
108
+ hn = ch[0].strip
109
+ hv = ch[1].strip
110
+ # Ensure header is ascii only
111
+ if hv.ascii_only? && hn.ascii_only?
112
+ # <---------- special treatment Post-Processor ---------->
113
+ hv << ' ' << PROGVERSION.to_s if hn == 'X-Post-Processor' && hv == 'flnews_post_proc'
114
+ # >----------<
115
+ @headers[hn.to_sym] = hv
116
+ else
117
+ warn "Custom header [#{hn}:#{hv}] should be ASCII only! Header is ignored!"
118
+ end
119
+ end
120
+ @headers.compact!
121
+ end
122
+ debug('updated headers are ' << @headers.inspect)
123
+ end
124
+
125
+ # remove a header
126
+ def remove(name)
127
+ @headers.delete(name) if @headers[name]
128
+ end
129
+
130
+ # basically a replacement for header(name), above.
131
+ # But you can call self.Newsgroups or self.From etc.
132
+ def method_missing(method, args = nil)
133
+ return @headers[method] if @headers[method]
134
+ error("unknown symbol '#{method}'")
135
+ end
136
+
137
+ # return the headers as a String.
138
+ def join
139
+ htext = ''
140
+ @headers.each_pair {|h, t| htext << h.to_s << ': ' << t << $LN }
141
+ debug('joined headers: ' << htext)
142
+ htext
143
+ end
144
+
145
+ attr_reader :lines, :newsgroups
146
+
147
+ end
148
+ # EOF
data/lib/newsgroups.rb ADDED
@@ -0,0 +1,134 @@
1
+ #encoding: UTF-8
2
+ =begin
3
+ /***************************************************************************
4
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
5
+ * This program is free software; you can redistribute it and/or modify *
6
+ * it under the terms of the WTFPL 2.0 or later, see *
7
+ * http://www.wtfpl.net/about/ *
8
+ * *
9
+ * This program is distributed in the hope that it will be useful, *
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
12
+ * *
13
+ ***************************************************************************/
14
+ =end
15
+
16
+ # An object of this class concentrates the specificities of chosen
17
+ # newsgroups, as defined in the configuration.
18
+
19
+ require_relative 'configuration'
20
+ require_relative 'basic_logging'
21
+
22
+ class Newsgroups
23
+ # class-level configuration object
24
+ @@config = Configuration.instance
25
+ include BasicLogging
26
+
27
+ def initialize(groups)
28
+ @groups = groups.split(',')
29
+ debug('set signature, intro, xnay')
30
+ # set details for this post
31
+ if @groups.size == 1
32
+ set_signature
33
+ set_intro
34
+ set_xnay
35
+ end
36
+ end
37
+
38
+ def xnay
39
+ debug('returning ' <<( @xnay ? @xnay : ' nil ') )
40
+ return @xnay ? @xnay : nil
41
+ end
42
+ attr_reader :signature, :intro, :groups
43
+
44
+ private
45
+
46
+ # defines the intro-line as per group.
47
+ def set_intro
48
+
49
+ @intro = nil
50
+ # only one group.
51
+ group = @groups[0]
52
+ # all configured intro-lines.
53
+ gintros = @@config.GROUP_INTROS
54
+
55
+ if gintros && gintros.respond_to?(:to_hash)
56
+ # find the intro for the group.
57
+ # either by name
58
+ if gintros.keys.include?(group)
59
+ @intro = gintros[group]
60
+ else
61
+ # or by a regular expression.
62
+ gintros.each do |gr, intro|
63
+ unless @intro
64
+ @intro = intro if group.match(gr)
65
+ debug "matched group against " << gr if @intro
66
+ end
67
+ end
68
+ end
69
+ debug('group_intro is ' << @intro.to_s)
70
+ else
71
+ msg = 'Cannot set the introduction line from the configuration!'
72
+ msg << "\nPlease verify that GROUP_INTROS is set"
73
+ warn(msg)
74
+ end
75
+ end
76
+
77
+ # replace all \n by \r\n
78
+ def correct_linebreaks(text)
79
+ warned = false
80
+ # find all lonely \n
81
+ while text.match(/([^\r])\n/) do
82
+ warn("ATTN! Line-breaks should be \\r\\n! Verify signatures!" ) if !warned
83
+ warned ||= true
84
+ # ... and silently marry them to \r
85
+ text.gsub!($~[0],$~[1] + "\r\n")
86
+ # Luxury you can afford.
87
+ end # \n
88
+ text
89
+ end
90
+
91
+ # define the signature
92
+ def set_signature
93
+ @signature = nil
94
+ # 1 group
95
+ group = @groups[0]
96
+ gsigs = @@config.GROUP_SIGS
97
+
98
+ if gsigs && gsigs.respond_to?(:to_hash)
99
+ # find the signature for the group
100
+ # either by name
101
+ if gsigs.keys.include?(group)
102
+ @signature = gsigs[group]
103
+ debug('signature is ' << @signature ) if @signature
104
+ # .., or by applying a regexp.
105
+ else
106
+ gsigs.each do |g, s|
107
+ unless @signature
108
+ rg = Regexp.new(g)
109
+ sm = group.match(rg)
110
+ debug('signature for group(s) ' << g << ': ' << s) if sm
111
+ if sm
112
+ @signature = correct_linebreaks(s)
113
+ end # if sm
114
+ end # if no signature
115
+ end # gsigs.each
116
+ end # gsigs for group?
117
+ else # gsigs and is hash?
118
+ msg = "Cannot read the signatures from the configuration."
119
+ msg << "\nPlease verify that GROUP_SIGS is set."
120
+ warn(msg)
121
+ end
122
+ end
123
+
124
+ # define the xnay header.
125
+ def set_xnay
126
+ @xnay = nil
127
+ xgs = @@config.XNAY_GROUPS
128
+ if xgs && !xgs.empty? && xgs.detect {|g| @groups[0].match(g) }
129
+ debug("setting XNAY")
130
+ @xnay = 'YES'
131
+ end
132
+ end
133
+ end
134
+ # EOF
data/lib/override.rb ADDED
@@ -0,0 +1,199 @@
1
+ #encoding: UTF-8
2
+ =begin
3
+ /***************************************************************************
4
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
5
+ * This program is free software; you can redistribute it and/or modify *
6
+ * it under the terms of the WTFPL 2.0 or later, see *
7
+ * http://www.wtfpl.net/about/ *
8
+ * *
9
+ * This program is distributed in the hope that it will be useful, *
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
12
+ * *
13
+ ***************************************************************************/
14
+ =end
15
+
16
+ # This class manages a dialog to alter the behavior of the
17
+ # post-processor on a “per article” basis. It can be displayed
18
+ # before posting, to do some “last minute” changes,
19
+ # notably to deactivate them.
20
+ #
21
+ # Post-processing CANNOT be deactivated completely in this dialog.
22
+ # I do not remember, why a previous comment stated the contrary.
23
+
24
+ require_relative 'basic_logging'
25
+
26
+ class OverrideDlg
27
+
28
+ include BasicLogging
29
+
30
+ XTERM = 'xterm'
31
+ # TERMINAL = 'x-terminal-emulator'
32
+ WHIPTAIL = 'whiptail'
33
+ YAD = 'yad'
34
+ ZENITY = 'zenity'
35
+ # The external programs which are supported for the time. They will be run in
36
+ # a new process (IO.popen() ). The first program from this array, which is
37
+ # found in the Path, will be used. Whiptail and a pure ruby dialog
38
+ # necessitate that xterm be found, too.
39
+ @@Executables = [YAD, ZENITY, WHIPTAIL, XTERM]
40
+ @@LIBDIR = File::dirname(__FILE__)
41
+ # The configuration variables that can be unset.
42
+ # This class instance variable is exposed via a getter.
43
+ @cvars = [:GROUP_SIGS, :CUSTOM_HEADERS, :XNAY_GROUPS, :VFY_URLS, :DEBUG_LOG]
44
+
45
+ # ... here
46
+ # For the record: this is rather cool.
47
+ # ... believe me! It is!
48
+ class << self
49
+ attr_reader :cvars
50
+ end
51
+ # Love it.
52
+
53
+ # Create the object
54
+ def initialize
55
+ @config = Configuration::instance
56
+ # find the supported programs
57
+ @executables = @@Executables.select do |ex_name|
58
+ program?(ex_name)
59
+ end
60
+ # As we are calling external programs anyway,
61
+ # why not continue with that ...
62
+ @xmsg = ENV['PATH'].split(':').any? {|d| Dir.children(d).include?('xmessage')}
63
+ # bof.
64
+ end
65
+
66
+ # display a dialog and return the new options.
67
+ def show
68
+ if(@executables && !@executables.empty?)
69
+ debug('found executables ' << @executables.join(', ') )
70
+ opts = nil
71
+ begin
72
+ if has?(YAD)
73
+ return yad_dlg.split.collect {|o| o.to_s}.join(' ')
74
+ elsif has?(ZENITY)
75
+ return zenity_dlg.split.collect {|o| o.to_s}.join(' ')
76
+ elsif has? XTERM
77
+ if has?(WHIPTAIL)
78
+ return whiptail_dlg.split.collect {|o| o.to_s}.join(' ')
79
+ else
80
+ debug 'using naked xterm'
81
+ return ruby_dlg.split.collect {|o| o.to_s}.join(' ')
82
+ end
83
+ end
84
+ rescue SystemExit
85
+ msg = 'Process is cancelled (SystemExit)'
86
+ info msg
87
+ exit true
88
+ rescue Exception => ex
89
+ msg = 'Error upon showing dialog ' << ex.message
90
+ error(msg)
91
+ end
92
+ # no program to show the dialog
93
+ else
94
+ msg = "#{File.basename($0)}: No suitable executable found to display the dialog"
95
+ warn msg
96
+ # if xmessage is available, give the user a last chance.
97
+ if @xmsg
98
+ io = IO.popen('xmessage -buttons continue:0,abort:1 -default abort -print ' << msg)
99
+ Process.wait(io.pid)
100
+ # ATTN! read io only once!
101
+ exit if ('continue' != io.read.to_s.strip )
102
+ # ... but then again, we still have a log.
103
+ debug('continue with configured settings')
104
+ end
105
+ end
106
+ end
107
+
108
+ private
109
+ # I know what it does and that there is some
110
+ # redundancy with the following method, but I
111
+ # do not feel like sorting it out, now.
112
+ def program?(ex_name)
113
+ ENV['PATH'].split(':').each do |dir_name|
114
+ return true if Dir.entries(dir_name).include?(ex_name)
115
+ end
116
+ return false
117
+ end
118
+
119
+ # returns true if the executable ex_name is to be
120
+ # called for the dialog
121
+ def has?(ex_name)
122
+ @executables.include? ex_name
123
+ end
124
+
125
+ ###### The following methods call a script, each, in an external process.
126
+ # There appears to be some Exception handling missing, but it does not
127
+ # hurt for the time being. Anyway, I do not care.
128
+
129
+ # creates a whiptail dialog in xterm.
130
+ def whiptail_dlg
131
+ debug('whiptail dialog')
132
+ tf = Tempfile.new
133
+ # dlg = @config.BINDIR << File::Separator << 'whiptail_dlg'
134
+ dlg = @@LIBDIR << File::Separator << 'whiptail_dlg'
135
+ dialog = XTERM.dup << ' -e ' << dlg << ' ' << tf.path
136
+ io = IO::popen(dialog)
137
+ Process.wait(io.pid )
138
+ nopts = tf.read
139
+ exit true if('exit' == nopts.strip)
140
+ tf.unlink
141
+ return nopts
142
+ end
143
+
144
+ # creates a textual dialog in xterm.
145
+ def ruby_dlg
146
+ debug('xterm dialog')
147
+ tf = Tempfile.new
148
+ # dlg = @config.BINDIR << File::Separator << 'ruby_dlg'
149
+ dlg = @@LIBDIR << File::Separator << 'ruby_dlg'
150
+ begin
151
+ dialog = XTERM.dup << ' -e ' << dlg << ' ' << tf.path
152
+ debug('dialog will be ' << dialog)
153
+ rescue Exception => ex
154
+ error ex.message
155
+ wait_for_user
156
+ exit false
157
+ end
158
+ io = IO::popen(dialog)
159
+ debug('wait for ' << io.pid.to_s)
160
+ Process.wait(io.pid )
161
+ nopts = tf.read
162
+ exit true if('exit' == nopts.strip)
163
+ tf.unlink
164
+ return nopts
165
+ end
166
+
167
+ # creates a Zenity dialog.
168
+ def zenity_dlg
169
+ debug('Zenity dialog')
170
+ # dialog = @config.BINDIR << File::Separator << 'zenity_dlg'
171
+ dialog = @@LIBDIR << File::Separator << 'zenity_dlg'
172
+ io = IO.popen(dialog)
173
+ Process::wait(io.pid)
174
+ rc = $?.exitstatus
175
+
176
+ exit true if rc != 0
177
+
178
+ nopts = io.read
179
+ return nopts
180
+ end
181
+
182
+ # creates a YAD dialog.
183
+ def yad_dlg
184
+ debug('yad dialog')
185
+ # dialog = @config.BINDIR << File::Separator << 'yad_dlg'
186
+ dialog = @@LIBDIR << File::Separator << 'yad_dlg'
187
+ debug('dialog will be ' << dialog)
188
+ io = IO::popen(dialog)
189
+ Process::wait(io.pid)
190
+ exit true if $?.exitstatus != 0
191
+
192
+ nopts = io.read
193
+ return nopts
194
+ end
195
+
196
+ end
197
+
198
+ # EOF
199
+
data/lib/ruby_dlg ADDED
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+ /***************************************************************************
5
+ * This program is free software; you can redistribute it and/or modify *
6
+ * it under the terms of the WTFPL 2.0 or later, see *
7
+ * http://www.wtfpl.net/about/ *
8
+ * *
9
+ * This program is distributed in the hope that it will be useful, *
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
12
+ * *
13
+ ***************************************************************************/
14
+ =end
15
+
16
+ require 'readline'
17
+ require_relative '../lib/color_output'
18
+ require 'io/wait'
19
+ require 'io/console'
20
+
21
+ def wait_for_user()
22
+ char = nil
23
+ # There is a difference which makes me prefer the below code from STDIN.raw(args)
24
+ # You can try
25
+ # char = STDIN.raw(&:getc)
26
+ # .., which does *not* work the same way and it won't for me.
27
+
28
+ STDIN.raw do
29
+ STDIN.noecho do
30
+ until (STDIN.ready?)
31
+ sleep(0.1)
32
+ end
33
+ char = (STDIN.read_nonblock(1).ord rescue nil)
34
+ end
35
+ end
36
+ return char
37
+ end
38
+
39
+
40
+ ofl = nil
41
+ begin
42
+ ofl = File.open(ARGV[0], 'w+') if ARGV.length > 0
43
+ puts 'writing to ' << ARGV[0]
44
+ rescue Exception => ex
45
+ puts red ('cannot open ' << ARGV[1])
46
+ puts 'hit any key to exit'
47
+ wait_for_user
48
+ exit false
49
+ end
50
+
51
+
52
+
53
+ opt_array = []
54
+ message = ''
55
+ menu ||= %=
56
+ 1 Unset Signature GROUP_SIGS
57
+ 2 Unset Custom headers CUSTOM_HEADERS
58
+ 3 Unset X-No-Archive XNAY_GROUPS
59
+ 4 Do not correct URLs VFY_URLS
60
+ 5 Disable log DEBUG_LOG
61
+ ––––––––––––––––––––––––––––––––––––––––––––––
62
+ 6 Summary
63
+ ––––––––––––––––––––––––––––––––––––––––––––––
64
+ 0 Okay, use settings.
65
+ ––––––––––––––––––––––––––––––––––––––––––––––––
66
+ #{bold("Esc, Ctrl+C and 'q'")} terminate the Post-processor
67
+ and no changes will be applied.
68
+ =
69
+
70
+ loop do
71
+ system 'clear'
72
+ puts yellow(menu)
73
+ puts cyan(message)
74
+ option = wait_for_user - 48
75
+ case option
76
+ # Esc, Ctrl+C, 'q'
77
+ when -21, -45, 65
78
+ puts red('Aborting. Bye.')
79
+ ofl.write 'exit'
80
+ ofl.close
81
+ exit true
82
+ when 0
83
+ puts green('Applying changes.') if !opt_array.empty?
84
+ # write the list of variables to unset (others remain)
85
+ ofl.write opt_array.join(' ')
86
+ ofl.close
87
+ exit true
88
+
89
+ # toggle options
90
+ # ADD option to the option array, if it should be *removed*
91
+ # from the configuration, else remove it from the array.
92
+ when 1
93
+ active = !opt_array.include?(:GROUP_SIGS)
94
+ opt_array << :GROUP_SIGS if active
95
+ opt_array.delete(:GROUP_SIGS) if !active
96
+
97
+ message = "Signature #{active ? 'unset' : 'as configured'}!"
98
+ menu.sub!("Unset Signature", "Set Signature ") if active
99
+ menu.sub!("Set Signature ", "Unset Signature") if !active
100
+ when 2
101
+ active = !opt_array.include?(:CUSTOM_HEADERS)
102
+ opt_array << :CUSTOM_HEADERS if active
103
+ opt_array.delete(:CUSTOM_HEADERS) if !active
104
+
105
+ message = "Custom headers #{active ? 'removed' : 'are added'}!"
106
+ menu.sub!("Unset Custom headers", "Add Custom headers ") if active
107
+ menu.sub!("Add Custom headers ", "Unset Custom headers") if !active
108
+ when 3
109
+ active = !opt_array.include?(:XNAY_GROUPS)
110
+ opt_array << :XNAY_GROUPS if active
111
+ opt_array.delete(:XNAY_GROUPS) if !active
112
+
113
+ message = "X-No-Archive #{active ? 'removed' : 'is added'}!"
114
+ menu.sub!("Unset X-No-Archive", "Add X-No-Archive ") if active
115
+ menu.sub!("Add X-No-Archive ", "Unset X-No-Archive") if !active
116
+ when 4
117
+ active = !opt_array.include?(:VFY_URLS)
118
+ opt_array << :VFY_URLS if active
119
+ opt_array.delete(:VFY_URLS) if !active
120
+
121
+ message = "URLS will #{active ? 'not ' : ''}" << "be verified!"
122
+ menu.sub!("Do not correct URLs", "Correct URLs ") if active
123
+ menu.sub!("Correct URLs ", "Do not correct URLs") if !active
124
+ when 5
125
+ active = !opt_array.include?(:DEBUG_LOG)
126
+ opt_array << :DEBUG_LOG if active
127
+ opt_array.delete(:DEBUG_LOG) if !active
128
+
129
+ message = "Log is #{ active ? 'not ' : ''} " << "written!"
130
+ menu.sub!("Disable log", "Enable log ") if active
131
+ menu.sub!("Enable log ", "Disable log") if !active
132
+ when 6
133
+ message = "Summary of " << bold('disabled') << " options: " << opt_array.join(' ')
134
+ end
135
+ end
136
+