pwn 0.5.480 → 0.5.482
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/README.md +3 -3
- data/lib/pwn/plugins/repl.rb +141 -52
- data/lib/pwn/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f08d37f0ef017a76ac0b7bfea02093a55aebd68954ac13761b6969d4937e44f6
|
|
4
|
+
data.tar.gz: e187138d17aac3845fb86a3166bd7b31013110cd93113227bde83ede9cf4041d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd0f3d1caa5a0cc98d79daeb267a7577a1ef2fa1ec40eff5a707743206c9c2cbf4fa6c5fcba124be4bddef90331e91466bbddb94de776df605a72f5f3510e52a
|
|
7
|
+
data.tar.gz: 1dac1bc3dac0ab48b2572d43c1dee10e6c9f89c4389c43acfae4ffff4f8d546e7c29c8f77e95a9cf6d2b23df39fd3303ef85065d7f892037a636051d432378c1
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ $ cd /opt/pwn
|
|
|
37
37
|
$ ./install.sh
|
|
38
38
|
$ ./install.sh ruby-gem
|
|
39
39
|
$ pwn
|
|
40
|
-
pwn[v0.5.
|
|
40
|
+
pwn[v0.5.482]:001 >>> PWN.help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.4@pwn
|
|
|
52
52
|
$ gem uninstall --all --executables pwn
|
|
53
53
|
$ gem install --verbose pwn
|
|
54
54
|
$ pwn
|
|
55
|
-
pwn[v0.5.
|
|
55
|
+
pwn[v0.5.482]:001 >>> PWN.help
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
If you're using a multi-user install of RVM do:
|
|
@@ -62,7 +62,7 @@ $ rvm use ruby-3.4.4@pwn
|
|
|
62
62
|
$ rvmsudo gem uninstall --all --executables pwn
|
|
63
63
|
$ rvmsudo gem install --verbose pwn
|
|
64
64
|
$ pwn
|
|
65
|
-
pwn[v0.5.
|
|
65
|
+
pwn[v0.5.482]:001 >>> PWN.help
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
|
data/lib/pwn/plugins/repl.rb
CHANGED
|
@@ -64,19 +64,10 @@ module PWN
|
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
region = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:region]
|
|
70
|
-
topic = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:topic]
|
|
71
|
-
channel_num = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:channel_num]
|
|
72
|
-
|
|
73
|
-
pi.config.prompt_name = "pwn.mesh:#{region}/#{topic}:#{channel_num}"
|
|
74
|
-
name = "\001\e[1m\002\001\e[32m\002#{pi.config.prompt_name}\001\e[0m\002"
|
|
75
|
-
dchars = "\001\e[32m\002>>>\001\e[33m\002"
|
|
76
|
-
dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
|
|
77
|
-
end
|
|
67
|
+
ps1_proc = "#{name}[#{version}]:#{line_count} #{dchars} ".to_s.scrub
|
|
68
|
+
ps1_proc = '' if pi.config.pwn_mesh
|
|
78
69
|
|
|
79
|
-
|
|
70
|
+
ps1_proc
|
|
80
71
|
end
|
|
81
72
|
rescue StandardError => e
|
|
82
73
|
raise e
|
|
@@ -472,24 +463,77 @@ module PWN
|
|
|
472
463
|
# Init ncurses UI (idempotent) with separate RX (top) and TX (bottom) panes
|
|
473
464
|
Curses.init_screen
|
|
474
465
|
Curses.curs_set(0)
|
|
475
|
-
|
|
476
|
-
|
|
466
|
+
Curses.noecho
|
|
467
|
+
Curses.cbreak
|
|
468
|
+
Curses.crmode
|
|
469
|
+
Curses.ESCDELAY = 0
|
|
477
470
|
Curses.start_color
|
|
478
471
|
Curses.use_default_colors
|
|
479
472
|
|
|
473
|
+
mesh_highlight_colors = [
|
|
474
|
+
{ fg: Curses::COLOR_RED, bg: Curses::COLOR_WHITE },
|
|
475
|
+
{ fg: Curses::COLOR_GREEN, bg: Curses::COLOR_BLACK },
|
|
476
|
+
{ fg: Curses::COLOR_YELLOW, bg: Curses::COLOR_BLACK },
|
|
477
|
+
{ fg: Curses::COLOR_BLUE, bg: Curses::COLOR_WHITE },
|
|
478
|
+
{ fg: Curses::COLOR_CYAN, bg: Curses::COLOR_BLACK },
|
|
479
|
+
{ fg: Curses::COLOR_MAGENTA, bg: Curses::COLOR_WHITE },
|
|
480
|
+
{ fg: Curses::COLOR_WHITE, bg: Curses::COLOR_BLUE }
|
|
481
|
+
]
|
|
482
|
+
mesh_highlight_colors.each_with_index do |hash, idx|
|
|
483
|
+
color_id = idx + 1
|
|
484
|
+
color_fg = hash[:fg]
|
|
485
|
+
color_bg = hash[:bg]
|
|
486
|
+
Curses.init_pair(color_id, color_fg, color_bg)
|
|
487
|
+
end
|
|
488
|
+
PWN.const_set(:MeshColors, (1..mesh_highlight_colors.length).to_a)
|
|
489
|
+
PWN.const_set(:MeshLastPair, PWN::MeshColors.sample)
|
|
490
|
+
|
|
491
|
+
mesh_ui_colors = []
|
|
492
|
+
mesh_highlight_colors.each_with_index do |hl_hash, idx|
|
|
493
|
+
ui_hash = {
|
|
494
|
+
color_id: idx + 10,
|
|
495
|
+
fg: hl_hash[:fg],
|
|
496
|
+
bg: -1
|
|
497
|
+
}
|
|
498
|
+
Curses.init_pair(ui_hash[:color_id], ui_hash[:fg], ui_hash[:bg])
|
|
499
|
+
mesh_ui_colors.push(ui_hash)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
red = mesh_ui_colors[0][:color_id]
|
|
503
|
+
green = mesh_ui_colors[1][:color_id]
|
|
504
|
+
yellow = mesh_ui_colors[2][:color_id]
|
|
505
|
+
blue = mesh_ui_colors[3][:color_id]
|
|
506
|
+
cyan = mesh_ui_colors[4][:color_id]
|
|
507
|
+
magenta = mesh_ui_colors[5][:color_id]
|
|
508
|
+
white = mesh_ui_colors[6][:color_id]
|
|
509
|
+
|
|
480
510
|
rx_height = Curses.lines - 4
|
|
481
|
-
rx_win = Curses::Window.new(rx_height, Curses.cols,
|
|
511
|
+
rx_win = Curses::Window.new(rx_height, Curses.cols, 0, 0)
|
|
482
512
|
rx_win.scrollok(true)
|
|
483
513
|
rx_win.nodelay = true
|
|
484
|
-
rx_win.
|
|
485
|
-
|
|
514
|
+
rx_win.attron(Curses.color_pair(cyan) | Curses::A_BOLD)
|
|
515
|
+
|
|
516
|
+
# Make rx_header bold and green
|
|
517
|
+
rx_win.attron(Curses.color_pair(green) | Curses::A_BOLD)
|
|
518
|
+
rx_header = "<<< #{host}:#{port} | #{region}/#{topic} | ch:#{channel_num} >>>"
|
|
519
|
+
rx_header_len = rx_header.length
|
|
520
|
+
rx_header_pos = (Curses.cols / 2) - (rx_header_len / 2)
|
|
521
|
+
rx_win.setpos(1, rx_header_pos)
|
|
522
|
+
rx_win.addstr(rx_header)
|
|
523
|
+
rx_win.attroff(Curses.color_pair(green) | Curses::A_BOLD)
|
|
524
|
+
# Jump two lines below header before messages begin
|
|
525
|
+
rx_win.setpos(2, 0)
|
|
526
|
+
rx_win.attron(Curses.color_pair(cyan) | Curses::A_BOLD)
|
|
527
|
+
header_line = "\u2014" * Curses.cols
|
|
528
|
+
rx_header_bottom_line_pos = (Curses.cols / 2) - (header_line.length / 2)
|
|
529
|
+
rx_win.addstr(header_line)
|
|
530
|
+
rx_win.attroff(Curses.color_pair(cyan) | Curses::A_BOLD)
|
|
531
|
+
|
|
486
532
|
rx_win.refresh
|
|
487
533
|
|
|
488
534
|
tx_win = Curses::Window.new(4, Curses.cols, rx_height, 0)
|
|
489
535
|
tx_win.scrollok(false)
|
|
490
536
|
tx_win.nodelay = true
|
|
491
|
-
tx_win.box('|', "\u2500")
|
|
492
|
-
tx_win.addstr("TX Pane (echo while typing, last sent shown after Enter)\n")
|
|
493
537
|
tx_win.refresh
|
|
494
538
|
|
|
495
539
|
PWN.const_set(:MeshRxWin, rx_win)
|
|
@@ -498,24 +542,59 @@ module PWN
|
|
|
498
542
|
PWN.const_set(:MeshPi, pi)
|
|
499
543
|
|
|
500
544
|
# Live typing echo thread (idempotent)
|
|
501
|
-
tx_prompt = "#{region}/#{topic}
|
|
545
|
+
tx_prompt = "#{region}/#{topic} >>>"
|
|
502
546
|
echo_thread = Thread.new do
|
|
547
|
+
last_drawn = nil
|
|
503
548
|
loop do
|
|
504
549
|
break unless pi.config.pwn_mesh
|
|
505
550
|
|
|
506
551
|
tx_win = PWN.const_get(:MeshTxWin)
|
|
507
552
|
mutex = PWN.const_get(:MeshMutex)
|
|
508
553
|
msg_input = pi.input.line_buffer.to_s
|
|
509
|
-
ts = Time.now.strftime('%H:%M:%S')
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
554
|
+
ts = Time.now.strftime('%H:%M:%S%z')
|
|
555
|
+
cursor_pos = Readline.point
|
|
556
|
+
prefix = "[#{ts}] [TX] #{tx_prompt} "
|
|
557
|
+
base_line = "#{prefix}#{msg_input}"
|
|
558
|
+
cursor_abs_index = prefix.length + cursor_pos
|
|
559
|
+
current_line = base_line
|
|
560
|
+
if current_line != last_drawn
|
|
561
|
+
mutex.synchronize do
|
|
562
|
+
tx_win.clear
|
|
563
|
+
tx_win.attron(Curses.color_pair(red) | Curses::A_BOLD)
|
|
564
|
+
tx_header_line_pos = (Curses.cols / 2) - (header_line.length / 2)
|
|
565
|
+
tx_win.addstr(header_line)
|
|
566
|
+
tx_win.attroff(Curses.color_pair(red) | Curses::A_BOLD)
|
|
567
|
+
|
|
568
|
+
tx_win.attron(Curses.color_pair(yellow) | Curses::A_BOLD)
|
|
569
|
+
inner_width = Curses.cols
|
|
570
|
+
segments = current_line.chars.each_slice(inner_width).map(&:join)
|
|
571
|
+
available_rows = tx_win.maxy - 1
|
|
572
|
+
segments.first(available_rows).each_with_index do |seg, idx|
|
|
573
|
+
tx_win.setpos(1 + idx, 0)
|
|
574
|
+
start_index = idx * inner_width
|
|
575
|
+
end_index = start_index + inner_width
|
|
576
|
+
if cursor_abs_index.between?(start_index, end_index)
|
|
577
|
+
cursor_col = cursor_abs_index - start_index
|
|
578
|
+
(0..inner_width).each do |col|
|
|
579
|
+
ch = seg[col] || ' '
|
|
580
|
+
if col == cursor_col
|
|
581
|
+
tx_win.attron(Curses.color_pair(red) | Curses::A_REVERSE | Curses::A_BOLD)
|
|
582
|
+
tx_win.addch(ch)
|
|
583
|
+
tx_win.attroff(Curses.color_pair(red) | Curses::A_REVERSE | Curses::A_BOLD)
|
|
584
|
+
else
|
|
585
|
+
tx_win.addch(ch)
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
else
|
|
589
|
+
tx_win.addstr(seg.ljust(inner_width))
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
tx_win.attroff(Curses.color_pair(yellow) | Curses::A_BOLD)
|
|
593
|
+
tx_win.refresh
|
|
594
|
+
end
|
|
595
|
+
last_drawn = current_line
|
|
517
596
|
end
|
|
518
|
-
sleep 0.
|
|
597
|
+
sleep 0.001
|
|
519
598
|
end
|
|
520
599
|
end
|
|
521
600
|
echo_thread.abort_on_exception = false
|
|
@@ -528,6 +607,7 @@ module PWN
|
|
|
528
607
|
max_threads: 1,
|
|
529
608
|
detach: true
|
|
530
609
|
) do |_|
|
|
610
|
+
last_from = nil
|
|
531
611
|
Meshtastic::MQTT.subscribe(
|
|
532
612
|
mqtt_obj: mqtt_obj,
|
|
533
613
|
region: region,
|
|
@@ -545,22 +625,44 @@ module PWN
|
|
|
545
625
|
mutex = PWN.const_get(:MeshMutex)
|
|
546
626
|
|
|
547
627
|
from = packet[:node_id_from]
|
|
548
|
-
absolute_topic = "#{region}/#{topic.gsub('#', from)}
|
|
628
|
+
absolute_topic = "#{region}/#{topic.gsub('#', from)}"
|
|
549
629
|
to = packet[:node_id_to]
|
|
550
630
|
rx_text = decoded[:payload]
|
|
551
|
-
ts = Time.now.strftime('%H:%M:%S')
|
|
631
|
+
ts = Time.now.strftime('%H:%M:%S%z')
|
|
632
|
+
|
|
633
|
+
# Select a random color pair different from the last used one
|
|
634
|
+
colors_arr = PWN.const_get(:MeshColors)
|
|
635
|
+
last_pair = PWN.const_get(:MeshLastPair)
|
|
636
|
+
PWN.send(:remove_const, :MeshLastPair)
|
|
637
|
+
if last_from != from
|
|
638
|
+
pair_choices = colors_arr.reject { |c| c == last_pair }
|
|
639
|
+
pair = pair_choices.sample
|
|
640
|
+
else
|
|
641
|
+
pair = last_pair
|
|
642
|
+
end
|
|
643
|
+
PWN.const_set(:MeshLastPair, pair)
|
|
644
|
+
rx_win.attron(Curses.color_pair(pair) | Curses::A_REVERSE)
|
|
645
|
+
|
|
646
|
+
current_line = "\n[#{ts}] [RX] #{absolute_topic} >>> #{rx_text}"
|
|
647
|
+
current_line = "\n[#{ts}] [RX][DM INTERCEPTED] #{absolute_topic} to: #{to} >>> #{rx_text}" unless to == '!ffffffff'
|
|
648
|
+
|
|
552
649
|
mutex.synchronize do
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
650
|
+
inner_width = Curses.cols - 2
|
|
651
|
+
content = current_line.sub(/\A\n/, '')
|
|
652
|
+
segments = content.scan(/.{1,#{inner_width}}/)
|
|
653
|
+
segments.each do |seg|
|
|
654
|
+
rx_win.addstr("\n")
|
|
655
|
+
rx_win.setpos(rx_win.cury, 1)
|
|
656
|
+
line = seg.ljust(inner_width)
|
|
657
|
+
rx_win.addstr(line)
|
|
557
658
|
end
|
|
558
|
-
# rx_win.addstr("#{msg.inspect}\n\n\n")
|
|
559
659
|
rx_win.refresh
|
|
560
660
|
end
|
|
661
|
+
rx_win.attroff(Curses.color_pair(pair) | Curses::A_REVERSE)
|
|
662
|
+
|
|
663
|
+
last_from = from
|
|
561
664
|
end
|
|
562
665
|
end
|
|
563
|
-
PWN.const_set(:MqttSubThread, true)
|
|
564
666
|
rescue StandardError => e
|
|
565
667
|
raise e
|
|
566
668
|
end
|
|
@@ -648,6 +750,8 @@ module PWN
|
|
|
648
750
|
PWN.const_get(:MeshTxWin).close
|
|
649
751
|
PWN.send(:remove_const, :MeshTxWin)
|
|
650
752
|
end
|
|
753
|
+
PWN.send(:remove_const, :MeshColors) if PWN.const_defined?(:MeshColors)
|
|
754
|
+
PWN.send(:remove_const, :MeshLastPair) if PWN.const_defined?(:MeshLastPair)
|
|
651
755
|
PWN.send(:remove_const, :MeshPi) if PWN.const_defined?(:MeshPi)
|
|
652
756
|
PWN.send(:remove_const, :MeshMutex) if PWN.const_defined?(:MeshMutex)
|
|
653
757
|
PWN.send(:remove_const, :MqttSubThread) if PWN.const_defined?(:MqttSubThread)
|
|
@@ -805,21 +909,6 @@ module PWN
|
|
|
805
909
|
text: tx_text,
|
|
806
910
|
psks: psks
|
|
807
911
|
)
|
|
808
|
-
|
|
809
|
-
# Update TX pane (bottom) with last sent message
|
|
810
|
-
if PWN.const_defined?(:MeshTxWin)
|
|
811
|
-
tx_win = PWN.const_get(:MeshTxWin)
|
|
812
|
-
mutex = PWN.const_get(:MeshMutex)
|
|
813
|
-
absolute_topic = "#{region}/#{topic.gsub('#', from)}:#{channel_num}"
|
|
814
|
-
ts = Time.now.strftime('%H:%M:%S')
|
|
815
|
-
mutex.synchronize do
|
|
816
|
-
tx_win.clear
|
|
817
|
-
tx_win.box('|', "\u2500")
|
|
818
|
-
tx_win.setpos(1, 1)
|
|
819
|
-
tx_win.addstr("[#{ts}] [TX] #{absolute_topic} >>> #{to}: #{tx_text}")
|
|
820
|
-
tx_win.refresh
|
|
821
|
-
end
|
|
822
|
-
end
|
|
823
912
|
end
|
|
824
913
|
end
|
|
825
914
|
rescue StandardError => e
|
data/lib/pwn/version.rb
CHANGED