1 #!/usr/bin/ruby -w |
|
2 # |
|
3 # Copyright (C) 2006,2007 Adam Wolk "Mulander" <netprobe@gmail.com> |
|
4 # Copyright (C) 2006 Mateusz Karkula "Karql" |
|
5 # |
|
6 # This script is provided under the terms of the GNU General Public License, |
|
7 # see the file COPYING in the root mcabber source directory. |
|
8 # |
|
9 # |
|
10 |
|
11 require 'getoptlong' |
|
12 |
|
13 ## |
|
14 # strings of colors ;) |
|
15 module Colors |
|
16 @@color = true |
|
17 |
|
18 ESC = 27.chr |
|
19 |
|
20 RED = ESC + '[31m' |
|
21 GREEN = ESC + '[32m' |
|
22 YELLOW= ESC + '[33m' |
|
23 BLUE = ESC + '[34m' |
|
24 PURPLE= ESC + '[35m' |
|
25 CYAN = ESC + '[36m' |
|
26 |
|
27 BGREEN= ESC + '[42m' |
|
28 |
|
29 ENDCOL= ESC + '[0m' |
|
30 |
|
31 def color(color) |
|
32 return '[' + self + ']' unless @@color |
|
33 color + self + ENDCOL |
|
34 end |
|
35 def red; color(RED); end |
|
36 def green; color(GREEN); end |
|
37 def yellow; color(YELLOW); end |
|
38 def blue; color(BLUE); end |
|
39 def purple; color(PURPLE); end |
|
40 def cyan; color(CYAN); end |
|
41 def bgreen; color(BGREEN); end |
|
42 end |
|
43 |
|
44 class String; include Colors; end |
|
45 |
|
46 class Option |
|
47 attr_accessor :value, :current, :default |
|
48 attr_reader :name,:msg |
|
49 |
|
50 def initialize(args) |
|
51 @name = args[:name] |
|
52 @msg = args[:msg] |
|
53 @default = args[:default] |
|
54 @value = nil |
|
55 @current = nil |
|
56 @prompt = '' |
|
57 end |
|
58 |
|
59 def set?; !@value.nil?; end |
|
60 def to_s; "set #@name=#@value"; end |
|
61 def additional?; false; end |
|
62 |
|
63 def ask() |
|
64 puts @msg |
|
65 print @prompt |
|
66 $stdin.gets.chomp |
|
67 end |
|
68 |
|
69 end |
|
70 |
|
71 class YesNo < Option |
|
72 def initialize(args) |
|
73 super(args) |
|
74 @ifSet = args[:ifSet] |
|
75 @prompt = '[Yes/no]: ' |
|
76 end |
|
77 def ask() |
|
78 # 1 == yes, 0 == no |
|
79 case super |
|
80 when /^Y/i |
|
81 @value = 1 |
|
82 return additional() |
|
83 when /^N/i |
|
84 @value = 0 |
|
85 return [] |
|
86 else |
|
87 puts 'Please answer yes or no' |
|
88 puts |
|
89 ask() |
|
90 end |
|
91 end |
|
92 |
|
93 def additional? |
|
94 (@value == 1 && !@ifSet.nil?) ? true : false |
|
95 end |
|
96 |
|
97 def additional |
|
98 (@ifSet.nil?) ? [] : @ifSet |
|
99 end |
|
100 end |
|
101 |
|
102 class Edit < Option |
|
103 def initialize(args) |
|
104 super(args) |
|
105 @regex = args[:regex] |
|
106 @prompt = '[edit]: ' |
|
107 end |
|
108 def ask() |
|
109 answer = super |
|
110 if answer.empty? || ( !@regex.nil? && !(answer =~ @regex) ) |
|
111 ask() |
|
112 else |
|
113 @value = answer |
|
114 end |
|
115 return [] |
|
116 end |
|
117 end |
|
118 |
|
119 class Multi < Option |
|
120 attr_reader :choices |
|
121 def initialize(args) |
|
122 super(args) |
|
123 @choices = args[:choices] |
|
124 @max = @choices.length - 1 |
|
125 @prompt = "[0-#{ @max }] " |
|
126 end |
|
127 |
|
128 def ask() |
|
129 puts @msg |
|
130 @choices.each_with_index do |choice,idx| |
|
131 print "#{idx}. #{choice}\n" |
|
132 end |
|
133 print @prompt |
|
134 answer = $stdin.gets.chomp |
|
135 |
|
136 ask() if answer.empty? # we ask here because ''.to_i == 0 |
|
137 |
|
138 case answer.to_i |
|
139 when 0 ... @max |
|
140 @value = answer |
|
141 else |
|
142 ask() |
|
143 end |
|
144 return [] |
|
145 end |
|
146 end |
|
147 |
|
148 class Wizzard |
|
149 VERSION = 0.04 |
|
150 attr_accessor :options, :ignore_previous, :ignore_auto, :target |
|
151 def initialize() |
|
152 if File.exists?(ENV['HOME'] + '/.mcabberrc') |
|
153 @target = ENV['HOME'] + '/.mcabberrc' |
|
154 else |
|
155 Dir.mkdir(ENV['HOME'] + '/.mcabber') unless File.exists?(ENV['HOME'] + '/.mcabber') |
|
156 @target = ENV['HOME'] + '/.mcabber/mcabberrc' |
|
157 end |
|
158 @ignore_previous = false |
|
159 @ignore_auto = false |
|
160 @options = Hash.new |
|
161 @order = Array.new |
|
162 @processed = Array.new |
|
163 @old = Array.new # for storing the users file untouched |
|
164 end |
|
165 |
|
166 ## |
|
167 # add a group of settings to the order queue |
|
168 def enqueue(group) |
|
169 group = group.to_a if group.class.to_s == 'String' |
|
170 @order += group |
|
171 end |
|
172 |
|
173 |
|
174 ## adds options to the settings object |
|
175 def add(args) |
|
176 @options[args[:name]] = args[:type].new(args) |
|
177 end |
|
178 |
|
179 ## run the wizzard |
|
180 def run() |
|
181 parse() |
|
182 display(@order) |
|
183 save() |
|
184 end |
|
185 |
|
186 ## |
|
187 # displays the setting and allows the user to modify it |
|
188 def display(order) |
|
189 order.each do |name| |
|
190 # this line here is less efficient then on the end of this method |
|
191 # but if placed on the end, recursion then breaks the order of settings |
|
192 @processed.push(name) |
|
193 ## |
|
194 # I know this is not efficient, but I have no better idea to modify the default port |
|
195 @options['port'].default = 5223 if @options['ssl'].value == 1 |
|
196 |
|
197 puts |
|
198 puts "'#{name}'" |
|
199 puts @options[name].msg |
|
200 puts 'e'.green + 'dit setting' |
|
201 puts 'l'.green + 'eave current setting ' + show(@options[name],:current).cyan unless @options[name].current.nil? |
|
202 puts 'u'.green + 'se default ' + show(@options[name],:default).cyan unless @options[name].default.nil? |
|
203 puts 's'.green + 'kip' |
|
204 puts 'a'.red + 'bort configuration' |
|
205 print '[action]: ' |
|
206 case $stdin.gets.chomp |
|
207 when /^s/ |
|
208 next |
|
209 when /^l/ |
|
210 @options[name].value = @options[name].current |
|
211 display(@options[name].additional) if @options[name].additional? |
|
212 when /^u/ |
|
213 @options[name].value = @options[name].default |
|
214 display(@options[name].additional) if @options[name].additional? |
|
215 when /^e/ |
|
216 additional = @options[name].ask |
|
217 display(additional) if additional.empty? |
|
218 when /^a/ |
|
219 puts 'aborted!!'.red |
|
220 exit |
|
221 end |
|
222 end |
|
223 end |
|
224 |
|
225 ## |
|
226 # this allows us to print 'yes' 'no' or descriptions of multi option settings |
|
227 # insted of just showing an integer |
|
228 def show(option,type) |
|
229 value = '' |
|
230 if type == :default |
|
231 value = option.default |
|
232 else |
|
233 value = option.current |
|
234 end |
|
235 |
|
236 case option.class.to_s |
|
237 when 'YesNo' |
|
238 return (value.to_i==1) ? 'yes' : 'no' |
|
239 when 'Multi' |
|
240 return option.choices[value.to_i] |
|
241 else |
|
242 return value.to_s |
|
243 end |
|
244 end |
|
245 |
|
246 ## save |
|
247 # save all settings to a file |
|
248 def save() |
|
249 flag,dumped = true,false |
|
250 target = File.new(@target,"w") |
|
251 |
|
252 @old.each do |line| |
|
253 flag = false if line =~ /^#BEGIN AUTO GENERATED SECTION/ |
|
254 flag = true if line =~ /^#END AUTO GENERATED SECTION/ |
|
255 if flag |
|
256 target << line |
|
257 elsif( !flag && !dumped ) |
|
258 target << "#BEGIN AUTO GENERATED SECTION\n\n" |
|
259 @processed.each do |name| |
|
260 target << @options[name].to_s + "\n" if @options[name].set? |
|
261 end |
|
262 puts |
|
263 dumped = true |
|
264 end |
|
265 end |
|
266 |
|
267 unless dumped |
|
268 target << "#BEGIN AUTO GENERATED SECTION\n\n" |
|
269 @processed.each do |name| |
|
270 target << @options[name].to_s + "\n" if @options[name].set? |
|
271 end |
|
272 target << "#END AUTO GENERATED SECTION\n\n" |
|
273 end |
|
274 |
|
275 target.close |
|
276 end |
|
277 ## parse |
|
278 # attempt to load settings from file |
|
279 def parse() |
|
280 return if @ignore_previous |
|
281 return unless File.exists?(@target) |
|
282 keyreg = @options.keys.join('|') |
|
283 parse = true |
|
284 File.open(@target) do |config| |
|
285 config.each do |line| |
|
286 |
|
287 @old << line |
|
288 parse = false if @ignore_auto && line =~ /^#BEGIN AUTO GENERATED SECTION/ |
|
289 parse = true if @ignore_auto && line =~ /^#END AUTO GENERATED SECTION/ |
|
290 |
|
291 if parse && line =~ /^set\s+(#{keyreg})\s*=\s*(.+)$/ |
|
292 @options[$1].current = $2 if @options.has_key?($1) |
|
293 end |
|
294 |
|
295 end |
|
296 end |
|
297 end |
|
298 |
|
299 ## |
|
300 # display onscreen help |
|
301 def Wizzard.help() |
|
302 puts %{ |
|
303 Usage: #{ $0.to_s.blue } #{ 'options'.green } |
|
304 |
|
305 This script generates configuration files for mcabber jabber client |
|
306 |
|
307 #{ "Options:".green } |
|
308 #{ "-h".green }, #{ "--help".green } display this help screen |
|
309 #{ "-v".green }, #{ "--version".green } display version information |
|
310 #{ "-T".green }, #{ "--target".green } configuration file |
|
311 #{ "-i".green }, #{ "--ignore".green } ignore previous configuration |
|
312 #{ "-I".green }, #{ "--ignore-auto".green } ignore auto generated section |
|
313 #{ "-S".green }, #{ "--status".green } ask for status settings |
|
314 #{ "-P".green }, #{ "--proxy".green } ask for proxy settings |
|
315 #{ "-k".green }, #{ "--keep".green } ping/keepalive connection settings |
|
316 #{ "-t".green }, #{ "--tracelog".green } ask for tracelog settings |
|
317 #{ "-C".green }, #{ "--nocolor".green } turn of color output |
|
318 } |
|
319 exit |
|
320 end |
|
321 |
|
322 ## |
|
323 # display version information |
|
324 def Wizzard.version() |
|
325 puts "mcwizz v#{VERSION.to_s.purple} coded by #{ 'Karql'.purple } & #{ 'mulander'.purple } <netprobe@gmail.com>" |
|
326 exit |
|
327 end |
|
328 end |
|
329 |
|
330 required = %w{ username server resource nickname ssl port pgp logging } |
|
331 proxy = %w{ proxy_host proxy_port proxy_user proxy_pass } |
|
332 status = %w{ buddy_format roster_width show_status_in_buffer autoaway message message_avail message_free |
|
333 message_dnd message_notavail message_away message_autoaway } |
|
334 tracelog = %w{ tracelog_level tracelog_file } |
|
335 |
|
336 opts = GetoptLong.new( |
|
337 ["--help","-h", GetoptLong::NO_ARGUMENT], |
|
338 ["--version","-v", GetoptLong::NO_ARGUMENT], |
|
339 ["--target", "-T", GetoptLong::REQUIRED_ARGUMENT], |
|
340 ["--ignore","-i", GetoptLong::NO_ARGUMENT], |
|
341 ["--ignore-auto","-I",GetoptLong::NO_ARGUMENT], |
|
342 ["--proxy","-P", GetoptLong::NO_ARGUMENT], |
|
343 ["--keep","-k", GetoptLong::NO_ARGUMENT], |
|
344 ["--status","-S", GetoptLong::NO_ARGUMENT], |
|
345 ["--tracelog","-t", GetoptLong::NO_ARGUMENT], |
|
346 ["--nocolor","-C", GetoptLong::NO_ARGUMENT] |
|
347 ) |
|
348 |
|
349 opts.ordering = GetoptLong::REQUIRE_ORDER |
|
350 |
|
351 config = Wizzard.new() |
|
352 config.enqueue(required) |
|
353 config.enqueue( %w{ beep_on_message hide_offline_buddies iq_version_hide_os autoaway } ) |
|
354 |
|
355 ## |
|
356 # Description of the add() syntax |
|
357 # :name - name of the setting |
|
358 # :msg - message displayed to the user |
|
359 # :type - type of settings - avaible types are: YesNo, Edit, Multi |
|
360 # :default - default setting |
|
361 # YesNo type specific flag: |
|
362 # :ifSet - an array of other options, that will be asked if the flag holding option is set to true |
|
363 # Edit type specific flag: |
|
364 # :regex- regular expression to which input will be compared |
|
365 # Multi type specific flag: |
|
366 # :choices - an array of possible settings |
|
367 # |
|
368 |
|
369 ## |
|
370 # here we add all the settings that we want to be able to handle |
|
371 |
|
372 ## |
|
373 # ungrouped settings |
|
374 config.add( :name => 'beep_on_message', |
|
375 :msg => 'Should mcabber beep when you receive a message?', |
|
376 :type => YesNo, |
|
377 :default => 0 ) |
|
378 |
|
379 config.add( :name => 'hide_offline_buddies', |
|
380 :msg => 'Display only connected buddies in the roster?', |
|
381 :type => YesNo, |
|
382 :default => 0 ) |
|
383 |
|
384 config.add( :name => 'pinginterval', |
|
385 :msg => 'Enter pinginterval in seconds for keepalive settings' \ |
|
386 ' set this to 0 to disable.', |
|
387 :type => Edit, |
|
388 :regex => /^\d+$/, |
|
389 :default => 40) |
|
390 |
|
391 config.add( :name => 'iq_version_hide_os', |
|
392 :msg => 'Hide Your OS information?', |
|
393 :type => YesNo, |
|
394 :default => 0 ) |
|
395 |
|
396 |
|
397 config.add( :name => 'port', |
|
398 :msg => 'Enter port number', |
|
399 :type => Edit, |
|
400 :regex => /^\d+$/, |
|
401 :default => 5222 ) |
|
402 |
|
403 ## |
|
404 # server settings |
|
405 config.add( :name => 'username', |
|
406 :msg => 'Your username', |
|
407 :type => Edit, |
|
408 :regex => /^[^\s\@:<>&\'"]+$/ ) |
|
409 |
|
410 config.add( :name => 'server', |
|
411 :msg => 'Your jabber server', |
|
412 :type => Edit, |
|
413 :regex => /^\S+$/ ) |
|
414 |
|
415 config.add( :name => 'resource', |
|
416 :msg => 'Resource (If you don\'t know what a resource is, use the default setting)', |
|
417 :type => Edit, |
|
418 :regex => /^.{1,1024}$/, |
|
419 :default => 'mcabber' ) |
|
420 |
|
421 config.add( :name => 'nickname', |
|
422 :msg => 'Conference nickname (if you skip this setting your username will be used as' \ |
|
423 ' nickname in MUC chatrooms)', |
|
424 :type => Edit ) |
|
425 |
|
426 ## |
|
427 # ssl settings |
|
428 config.add( :name => 'ssl', |
|
429 :msg => 'Enable ssl?', |
|
430 :type => YesNo, |
|
431 :ifSet => %w{ ssl_verify ssl_cafile ssl_capath ciphers }, |
|
432 :default => 0 ) |
|
433 |
|
434 |
|
435 config.add( :name => 'ssl_verify', |
|
436 :msg => 'Set to 0 to disable certificate verification, or non-zero to set desired maximum CA' \ |
|
437 ' verification depth. Use -1 to specify an unlimited depth.', |
|
438 :type => Edit, |
|
439 :regex => /^(-1)|(\d+)$/, |
|
440 :default => -1 ) |
|
441 |
|
442 config.add( :name => 'ssl_cafile', |
|
443 :msg => 'Set to a path to a CA certificate file (may contain multiple CA certificates)', |
|
444 :type => Edit ) |
|
445 |
|
446 config.add( :name => 'ssl_capath', |
|
447 :msg => 'Set to a directory containing CA certificates (use c_rehash to generate hash links)', |
|
448 :type => Edit ) |
|
449 |
|
450 config.add( :name => 'ciphers', |
|
451 :msg => 'Set to a list of desired SSL ciphers (run "openssl ciphers" for a candidate values)', |
|
452 :type => Edit ) |
|
453 |
|
454 ## |
|
455 # pgp support |
|
456 config.add( :name => 'pgp', |
|
457 :msg => 'Enable OpenPGP support?', |
|
458 :type => YesNo, |
|
459 :ifSet => %w{ pgp_private_key }, |
|
460 :default => 0 ) |
|
461 |
|
462 config.add( :name => 'pgp_private_key', |
|
463 :msg => 'Enter your private key id. You can get the Key Id with gpg: ' \ |
|
464 '"gpg --list-keys --keyid-format long"', |
|
465 :type => Edit ) |
|
466 ## |
|
467 # proxy settings |
|
468 config.add( :name => 'proxy_host', |
|
469 :msg => 'Proxy host', |
|
470 :type => Edit, |
|
471 :regex => /^\S+?\.\S+?$/ ) |
|
472 |
|
473 config.add( :name => 'proxy_port', |
|
474 :msg => 'Proxy port', |
|
475 :type => Edit, |
|
476 :regex => /^\d+$/, |
|
477 :default => 3128 ) |
|
478 |
|
479 config.add( :name => 'proxy_user', |
|
480 :msg => 'Proxy user', |
|
481 :type => Edit ) |
|
482 |
|
483 config.add( :name => 'proxy_pass', |
|
484 :msg => 'Proxy pass (will be stored unencrypted an the pass will be echoed during input)', |
|
485 :type => Edit ) |
|
486 ## |
|
487 # trace logs |
|
488 config.add( :name => 'tracelog_level', |
|
489 :msg => 'Specify level of advanced traces', |
|
490 :type => Multi, |
|
491 :choices => [ 'lvl0: I don\'t want advanced tracing', |
|
492 'lvl1: most events of the log window are written to the file', |
|
493 'lvl2: debug logging (XML etc.)' ], |
|
494 :default => 0 ) |
|
495 |
|
496 config.add( :name => 'tracelog_file', |
|
497 :msg => 'Specify a file to which the logs will be written', |
|
498 :type => Edit ) |
|
499 ## |
|
500 # logging settings |
|
501 config.add( :name => 'logging', |
|
502 :msg => 'Enable logging?', |
|
503 :type => YesNo, |
|
504 :ifSet => %w{ log_win_height log_display_sender load_logs logging_dir log_muc_conf}, |
|
505 :default => 1 ) |
|
506 |
|
507 config.add( :name => 'log_win_height', |
|
508 :msg => 'Set log window height (minimum 1)', |
|
509 :type => Edit, |
|
510 :regex => /^[1-9]\d*/, |
|
511 :default => 5 ) |
|
512 |
|
513 config.add( :name => 'log_display_sender', |
|
514 :msg => 'Display the message sender\'s jid in the log window?', |
|
515 :type => YesNo, |
|
516 :default => 0 ) |
|
517 |
|
518 config.add( :name => 'load_logs', |
|
519 :msg => 'Enable loading logs?', |
|
520 :type => YesNo, |
|
521 :default => 1 ) |
|
522 |
|
523 config.add( :name => 'logging_dir', |
|
524 :msg => 'Enter logging directory', |
|
525 :type => Edit ) |
|
526 |
|
527 config.add( :name => 'log_muc_conf', |
|
528 :msg => 'Log MUC chats?', |
|
529 :ifSet => %w{ load_muc_logs }, |
|
530 :type => YesNo, |
|
531 :default => 1 ) |
|
532 |
|
533 config.add( :name => 'load_muc_logs', |
|
534 :msg => 'Load MUC chat logs?', |
|
535 :type => YesNo, |
|
536 :default => 0 ) |
|
537 ## |
|
538 # status settings |
|
539 config.add( :name => 'roster_width', |
|
540 :msg => 'Set buddylist window width (minimum 2)', |
|
541 :type => Edit, |
|
542 :regex => /^[2-9]\d*$/, |
|
543 :default => 24 ) |
|
544 |
|
545 config.add( :name => 'buddy_format', |
|
546 :msg => 'What buddy format (in status window) do you prefer?', |
|
547 :type => Multi, |
|
548 :choices => [ '<jid/resource>', |
|
549 'name <jid/resource> (name is omitted if same as the jid)', |
|
550 'name/resource (if the name is same as the jid, use <jid/res>', |
|
551 'name (if the name is the same as the jid, use <jid/res>' ] ) |
|
552 |
|
553 config.add( :name => 'show_status_in_buffer', |
|
554 :msg => 'What status changes should be displayed in the buffer?', |
|
555 :type => Multi, |
|
556 :choices => [ 'none', |
|
557 'connect/disconnect', |
|
558 'all' ], |
|
559 :default => 2 ) |
|
560 |
|
561 config.add( :name => 'autoaway', |
|
562 :msg => 'After how many seconds of inactivity should You become away? (0 for never)', |
|
563 :type => Edit, |
|
564 :regex => /^\d+$/, |
|
565 :default => 0 ) |
|
566 |
|
567 config.add( :name => 'message', |
|
568 :msg => 'Skip this setting unless you want to override all other status messages', |
|
569 :type => Edit ) |
|
570 |
|
571 config.add( :name => 'message_avail', |
|
572 :message => 'Set avaible status', |
|
573 :type => Edit, |
|
574 :default => 'I\'m avaible' ) |
|
575 |
|
576 |
|
577 config.add( :name => 'message_free', |
|
578 :message => 'Set free for chat status', |
|
579 :type => Edit, |
|
580 :default => 'I\'m free for chat' ) |
|
581 |
|
582 config.add( :name => 'message_dnd', |
|
583 :message => 'Set do not disturb status', |
|
584 :type => Edit, |
|
585 :default => 'Please do not disturb' ) |
|
586 |
|
587 config.add( :name => 'message_notavail', |
|
588 :message => 'Set not avaible status', |
|
589 :type => Edit, |
|
590 :default => 'I\'m not avaible' ) |
|
591 |
|
592 config.add( :name => 'message_away', |
|
593 :message => 'Set away status', |
|
594 :type => Edit, |
|
595 :default => 'I\'m away' ) |
|
596 |
|
597 config.add( :name => 'message_autoaway', |
|
598 :msg => 'Set auto-away status', |
|
599 :type => Edit, |
|
600 :default => 'Auto-away' ) |
|
601 |
|
602 begin |
|
603 opts.each do |opt,arg| |
|
604 case opt |
|
605 when '--help' |
|
606 Wizzard.help() |
|
607 when '--version' |
|
608 Wizzard.version() |
|
609 when '--target' |
|
610 config.target = arg |
|
611 when '--ignore' |
|
612 config.ignore_previous = true |
|
613 when '--ignore-auto' |
|
614 config.ignore_auto = true |
|
615 when '--proxy' |
|
616 config.enqueue(proxy) |
|
617 when '--keep' |
|
618 config.enqueue('pinginterval') |
|
619 when '--tracelog' |
|
620 config.enqueue(tracelog) |
|
621 when '--status' |
|
622 config.enqueue(status) |
|
623 when '--nocolor' |
|
624 class String; @@color = false; end |
|
625 end |
|
626 end |
|
627 rescue GetoptLong::InvalidOption |
|
628 Wizzard.help() |
|
629 end |
|
630 |
|
631 config.run |
|