meshchat 0.8.0 → 0.10.0

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.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +39 -11
  3. data/lib/meshchat.rb +48 -42
  4. data/lib/meshchat/configuration.rb +14 -0
  5. data/lib/meshchat/configuration/app_config.rb +63 -0
  6. data/lib/meshchat/configuration/database.rb +41 -0
  7. data/lib/meshchat/{config → configuration}/hash_file.rb +7 -6
  8. data/lib/meshchat/configuration/identity.rb +79 -0
  9. data/lib/meshchat/{config → configuration}/settings.rb +22 -26
  10. data/lib/meshchat/debug.rb +69 -0
  11. data/lib/meshchat/encryption.rb +7 -2
  12. data/lib/meshchat/encryption/aes_rsa.rb +2 -1
  13. data/lib/meshchat/encryption/passthrough.rb +5 -3
  14. data/lib/meshchat/locale/en.yml +14 -0
  15. data/lib/meshchat/models/node.rb +140 -0
  16. data/lib/meshchat/network.rb +19 -0
  17. data/lib/meshchat/network/dispatcher.rb +83 -0
  18. data/lib/meshchat/network/errors.rb +11 -0
  19. data/lib/meshchat/network/incoming.rb +13 -0
  20. data/lib/meshchat/network/incoming/message_decryptor.rb +51 -0
  21. data/lib/meshchat/network/incoming/message_processor.rb +75 -0
  22. data/lib/meshchat/network/incoming/request_processor.rb +30 -0
  23. data/lib/meshchat/network/local.rb +12 -0
  24. data/lib/meshchat/network/local/connection.rb +58 -0
  25. data/lib/meshchat/network/local/server.rb +69 -0
  26. data/lib/meshchat/network/message.rb +34 -0
  27. data/lib/meshchat/network/message/base.rb +139 -0
  28. data/lib/meshchat/network/message/chat.rb +9 -0
  29. data/lib/meshchat/network/message/disconnect.rb +21 -0
  30. data/lib/meshchat/network/message/emote.rb +9 -0
  31. data/lib/meshchat/network/message/factory.rb +80 -0
  32. data/lib/meshchat/network/message/node_list.rb +75 -0
  33. data/lib/meshchat/network/message/node_list_diff.rb +18 -0
  34. data/lib/meshchat/network/message/node_list_hash.rb +32 -0
  35. data/lib/meshchat/network/message/ping.rb +31 -0
  36. data/lib/meshchat/network/message/ping_reply.rb +12 -0
  37. data/lib/meshchat/network/message/whisper.rb +28 -0
  38. data/lib/meshchat/network/remote.rb +13 -0
  39. data/lib/meshchat/network/remote/connection.rb +28 -0
  40. data/lib/meshchat/network/remote/relay.rb +109 -0
  41. data/lib/meshchat/network/remote/relay_pool.rb +52 -0
  42. data/lib/meshchat/ui.rb +13 -0
  43. data/lib/meshchat/ui/cli.rb +48 -0
  44. data/lib/meshchat/ui/cli/base.rb +39 -0
  45. data/lib/meshchat/ui/cli/input_factory.rb +50 -0
  46. data/lib/meshchat/ui/cli/keyboard_line_input.rb +14 -0
  47. data/lib/meshchat/ui/command.rb +51 -0
  48. data/lib/meshchat/ui/command/base.rb +77 -0
  49. data/lib/meshchat/ui/command/bind.rb +47 -0
  50. data/lib/meshchat/ui/command/chat.rb +31 -0
  51. data/lib/meshchat/ui/command/config.rb +37 -0
  52. data/lib/meshchat/ui/command/emote.rb +23 -0
  53. data/lib/meshchat/ui/command/exit.rb +16 -0
  54. data/lib/meshchat/ui/command/help.rb +20 -0
  55. data/lib/meshchat/ui/command/identity.rb +16 -0
  56. data/lib/meshchat/ui/command/import.rb +42 -0
  57. data/lib/meshchat/ui/command/irb.rb +22 -0
  58. data/lib/meshchat/ui/command/offline.rb +23 -0
  59. data/lib/meshchat/ui/command/online.rb +18 -0
  60. data/lib/meshchat/ui/command/ping.rb +65 -0
  61. data/lib/meshchat/ui/command/ping_all.rb +19 -0
  62. data/lib/meshchat/ui/command/send_disconnect.rb +20 -0
  63. data/lib/meshchat/ui/command/server.rb +22 -0
  64. data/lib/meshchat/ui/command/share.rb +16 -0
  65. data/lib/meshchat/ui/command/whisper.rb +40 -0
  66. data/lib/meshchat/ui/display.rb +78 -0
  67. data/lib/meshchat/ui/display/base.rb +58 -0
  68. data/lib/meshchat/ui/display/manager.rb +59 -0
  69. data/lib/meshchat/ui/notifier.rb +9 -0
  70. data/lib/meshchat/ui/notifier/base.rb +33 -0
  71. data/lib/meshchat/version.rb +3 -2
  72. metadata +150 -80
  73. data/lib/meshchat/cli.rb +0 -188
  74. data/lib/meshchat/cli/base.rb +0 -13
  75. data/lib/meshchat/cli/input.rb +0 -37
  76. data/lib/meshchat/command/base.rb +0 -80
  77. data/lib/meshchat/command/bind.rb +0 -44
  78. data/lib/meshchat/command/chat.rb +0 -30
  79. data/lib/meshchat/command/config.rb +0 -34
  80. data/lib/meshchat/command/emote.rb +0 -20
  81. data/lib/meshchat/command/exit.rb +0 -13
  82. data/lib/meshchat/command/help.rb +0 -17
  83. data/lib/meshchat/command/identity.rb +0 -13
  84. data/lib/meshchat/command/import.rb +0 -41
  85. data/lib/meshchat/command/init.rb +0 -34
  86. data/lib/meshchat/command/irb.rb +0 -23
  87. data/lib/meshchat/command/listen.rb +0 -13
  88. data/lib/meshchat/command/offline.rb +0 -20
  89. data/lib/meshchat/command/online.rb +0 -15
  90. data/lib/meshchat/command/ping.rb +0 -65
  91. data/lib/meshchat/command/ping_all.rb +0 -15
  92. data/lib/meshchat/command/send_disconnect.rb +0 -15
  93. data/lib/meshchat/command/server.rb +0 -20
  94. data/lib/meshchat/command/share.rb +0 -13
  95. data/lib/meshchat/command/stop_listening.rb +0 -13
  96. data/lib/meshchat/command/whisper.rb +0 -38
  97. data/lib/meshchat/database.rb +0 -30
  98. data/lib/meshchat/display.rb +0 -33
  99. data/lib/meshchat/display/base.rb +0 -60
  100. data/lib/meshchat/display/manager.rb +0 -55
  101. data/lib/meshchat/instance.rb +0 -40
  102. data/lib/meshchat/message.rb +0 -41
  103. data/lib/meshchat/message/base.rb +0 -97
  104. data/lib/meshchat/message/chat.rb +0 -19
  105. data/lib/meshchat/message/disconnect.rb +0 -13
  106. data/lib/meshchat/message/emote.rb +0 -9
  107. data/lib/meshchat/message/node_list.rb +0 -63
  108. data/lib/meshchat/message/node_list_diff.rb +0 -15
  109. data/lib/meshchat/message/node_list_hash.rb +0 -33
  110. data/lib/meshchat/message/ping.rb +0 -32
  111. data/lib/meshchat/message/ping_reply.rb +0 -9
  112. data/lib/meshchat/message/relay.rb +0 -43
  113. data/lib/meshchat/message/whisper.rb +0 -36
  114. data/lib/meshchat/models/entry.rb +0 -104
  115. data/lib/meshchat/net/client.rb +0 -83
  116. data/lib/meshchat/net/listener/errors.rb +0 -11
  117. data/lib/meshchat/net/listener/request.rb +0 -48
  118. data/lib/meshchat/net/listener/request_processor.rb +0 -50
  119. data/lib/meshchat/net/listener/server.rb +0 -114
  120. data/lib/meshchat/net/request.rb +0 -29
  121. data/lib/meshchat/notifier/base.rb +0 -31
@@ -1,32 +0,0 @@
1
- module MeshChat
2
- module Message
3
- class Ping < Base
4
-
5
- def display
6
- # we'll never display our own ping to someone else...
7
- # or shouldn't.... or there should be different output
8
- # TODO: display is a bad method name
9
- name = payload['sender']['alias']
10
- location = payload['sender']['location']
11
-
12
- "#{name}@#{location} pinged you."
13
- end
14
-
15
- def handle
16
- respond
17
- display
18
- end
19
-
20
- def respond
21
- location = payload['sender']['location']
22
-
23
- node = Node.find_by_location(location)
24
-
25
- MeshChat::Net::Client.send(
26
- node: node,
27
- message: PingReply.new
28
- )
29
- end
30
- end
31
- end
32
- end
@@ -1,9 +0,0 @@
1
- module MeshChat
2
- module Message
3
- class PingReply < Base
4
- def display
5
- 'ping successful'.freeze if Settings.debug?
6
- end
7
- end
8
- end
9
- end
@@ -1,43 +0,0 @@
1
- module MeshChat
2
- module Message
3
- class Relay < Base
4
- def initialize(
5
- message: nil,
6
- sender_name: nil,
7
- sender_location: nil,
8
- sender_uid: nil,
9
- time_recieved: nil,
10
- payload: nil,
11
- destination: '',
12
- hops: [])
13
-
14
- # package the original message
15
- message = {
16
- message: message,
17
- destination: destination,
18
- hops: hops
19
- }
20
-
21
- super(
22
- message: message,
23
- sender_name: sender_name,
24
- sender_location: sender_location,
25
- sender_uid: sender_uid,
26
- time_recieved: time_recieved,
27
- payload: payload)
28
- end
29
-
30
- def display; end
31
-
32
- def handle
33
- respond
34
- return
35
- end
36
-
37
- def respond
38
-
39
- end
40
-
41
- end
42
- end
43
- end
@@ -1,36 +0,0 @@
1
- module MeshChat
2
- module Message
3
- class Whisper < Base
4
- attr_accessor :_to
5
-
6
- def initialize(
7
- message: nil,
8
- sender_name: nil,
9
- sender_location: nil,
10
- sender_uid: nil,
11
- time_recieved: nil,
12
- payload: nil,
13
- to: '')
14
-
15
- super(
16
- message: message,
17
- sender_name: sender_name,
18
- sender_location: sender_location,
19
- sender_uid: sender_uid,
20
- time_recieved: time_recieved,
21
- payload: payload)
22
-
23
- self._to = to
24
- end
25
-
26
- def display
27
- time_sent = payload['time_sent'].to_s
28
- time = Date.parse(time_sent)
29
- time_recieved = time.strftime('%e/%m/%y %H:%I:%M')
30
-
31
- to = _to.present? ? "->#{_to}" : ''
32
- "#{time_recieved} #{sender_name}#{to} > #{message}"
33
- end
34
- end
35
- end
36
- end
@@ -1,104 +0,0 @@
1
- module MeshChat
2
- module Models
3
- class Entry < ActiveRecord::Base
4
- IPV4_WITH_PORT = /((?:(?:^|\.)(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])){4})(:\d*)?/
5
- # http://rubular.com/r/WYT09ptct3
6
- DOMAIN_WITH_PORT = /(https?:\/\/)?([\da-z\.-]+)\.?([a-z\.]{2,6})([\/\w \.-]*)*[^\/](:\d*)?/
7
-
8
- validates :alias_name,
9
- :location,
10
- :public_key, presence: true
11
-
12
- validates :uid, presence: true, uniqueness: true
13
-
14
- # ipv4 with port
15
- validates_format_of :location, with: ->(e){
16
- location = e.location || ''
17
- location.include?('//') || location.include?('localhost') ?
18
- DOMAIN_WITH_PORT :
19
- IPV4_WITH_PORT
20
- }
21
-
22
- scope :online, -> { where(online: true) }
23
- scope :offline, -> { where(online: false) }
24
-
25
- class << self
26
- def sha_preimage
27
- all.map(&:public_key).sort.join(',')
28
- end
29
-
30
- def as_sha512
31
- digest = Digest::SHA512.new
32
- digest.hexdigest sha_preimage
33
- end
34
-
35
- def as_json
36
- # must also include ourselves
37
- # so that we can pass our own public key
38
- # to those who don't have it
39
- others = all.map(&:as_json)
40
- me = Settings.identity_as_json
41
- others << me
42
- end
43
-
44
- def from_json(json)
45
- new(
46
- alias_name: json['alias'],
47
- location: json['location'],
48
- uid: json['uid'],
49
- public_key: json['publickey']
50
- )
51
- end
52
-
53
- def public_key_from_uid(uid)
54
- find_by_uid(uid).try(:public_key)
55
- end
56
-
57
- # @param [Array] theirs array of hashes representing node entries
58
- # @return [Array<-,+>] nodes only we have, and nodes only they have
59
- def diff(theirs)
60
- ours = as_json
61
- we_only_have = ours - theirs
62
- they_only_have = theirs - ours
63
-
64
- [we_only_have, they_only_have]
65
- end
66
-
67
- def import_from_file(filename)
68
- begin
69
- f = File.read(filename)
70
- hash = JSON.parse(f)
71
- n = from_json(hash)
72
- n.save
73
- n
74
- rescue => e
75
- Display.alert e.message
76
- end
77
- end
78
- end
79
-
80
- def ==(other)
81
- result = false
82
-
83
- if other.is_a?(Hash)
84
- result = as_json.values_at(*other.keys) == other.values
85
- end
86
-
87
- result || super
88
- end
89
-
90
- def as_json
91
- {
92
- 'alias' => alias_name,
93
- 'location' => location,
94
- 'uid' => uid,
95
- 'publickey' => public_key
96
- }
97
- end
98
-
99
- def as_info
100
- "#{alias_name}@#{location}"
101
- end
102
- end
103
- end
104
- end
@@ -1,83 +0,0 @@
1
- module MeshChat
2
- module Net
3
- module Client
4
- module_function
5
-
6
- # @note Either the location, node, or uid should be present
7
- #
8
- # @param [String] location (Optional) location of target
9
- # @param [String] uid (Optional) uid of target
10
- # @param [Node] node (Optional) target
11
- # @param [Message] message (Required) what to send to the target
12
- def send(location: nil, uid: nil, node: nil, message: nil)
13
- # verify node is valid
14
- node = self.node_for(location: location, uid: uid, node: node)
15
- # don't proceed if we don't have a node
16
- return unless node
17
-
18
- if Settings['uid'] == node.uid
19
- Display.debug 'avoiding sending to self'
20
- return
21
- end
22
-
23
- Thread.new(node, message) do |node, message|
24
- begin
25
- payload = Client.payload_for(node, message)
26
-
27
- Display.debug('SENDING: ' + message.type)
28
- Display.debug('SENDING: ' + message.sender.to_s)
29
- Display.debug('SENDING: ' + message.message.to_s)
30
-
31
- # TODO: this is horrible, come up with something better / abstract it
32
- begin
33
- Curl::Easy.http_post(node.location, payload.to_json) do |c|
34
- c.headers['Accept'] = 'application/json'
35
- c.headers['Content-Type'] = 'application/json'
36
- if MeshChat::Settings.debug?
37
- puts message.render
38
- c.verbose = true
39
- c.on_debug do |type, data|
40
- puts data
41
- end
42
- end
43
- end
44
- rescue => e
45
- node.update(online: false)
46
- Display.info "#{node.alias_name} has ventured offline"
47
- Display.debug("#{message.class.name}: Issue connectiong to #{node.alias_name}@#{node.location}")
48
- Display.debug(e.message)
49
- end
50
-
51
- rescue => e
52
- Display.info "Public key encryption for #{node.try(:alias_name) || 'unknown'} failed"
53
- end
54
- end
55
- end
56
-
57
- # creates a json payload
58
- def self.payload_for(node, message)
59
- request = MeshChat::Net::Request.new(node, message)
60
- { message: request.payload }
61
- end
62
-
63
- # private
64
-
65
- # @return [Node]
66
- def node_for(location: nil, uid: nil, node: nil)
67
- unless node
68
- node = Models::Entry.find_by_location(location) if location
69
- node = Models::Entry.find_by_uid(uid) if uid && !node
70
- end
71
-
72
- # TODO: also check for public key?
73
- # without the public key, the message is sent in cleartext. :-\
74
- if !(node && node.location)
75
- Display.alert "Node not found, or does not have a location. Have you imported #{location || ""}?"
76
- return
77
- end
78
-
79
- node
80
- end
81
- end
82
- end
83
- end
@@ -1,11 +0,0 @@
1
- module MeshChat
2
- module Net
3
- module Listener
4
- module Errors
5
- class NotAuthorized < StandardError; end
6
- class Forbidden < StandardError; end
7
- class BadRequest < StandardError; end
8
- end
9
- end
10
- end
11
- end
@@ -1,48 +0,0 @@
1
- module MeshChat
2
- module Net
3
- module Listener
4
- class Request
5
- attr_accessor :json, :message
6
- attr_accessor :_input
7
-
8
- def initialize(input)
9
- self._input = try_decrypt(input)
10
-
11
- begin
12
- self.json = JSON.parse(_input)
13
- rescue => e
14
- Display.debug e.message
15
- Display.debug e.backtrace.join("\n")
16
- raise Errors::BadRequest.new
17
- end
18
- self.message = process_json
19
- end
20
-
21
- private
22
-
23
- def try_decrypt(input)
24
- begin
25
- decoded = Base64.decode64(input)
26
- input = Cipher.decrypt(decoded, Settings[:privatekey])
27
- rescue => e
28
- Display.debug e.message
29
- Display.debug e.backtrace.join("\n")
30
- Display.debug input
31
- raise Errors::NotAuthorized.new(e.message)
32
- end
33
-
34
- input
35
- end
36
-
37
- def process_json
38
- type = json['type']
39
- klass = Message::TYPES[type]
40
-
41
- raise Errors::BadRequest.new(type) unless klass
42
-
43
- klass.new(payload: json)
44
- end
45
- end
46
- end
47
- end
48
- end
@@ -1,50 +0,0 @@
1
- module MeshChat
2
- module Net
3
- module Listener
4
- module RequestProcessor
5
-
6
- module_function
7
-
8
- def process(raw)
9
- request = Request.new(raw)
10
- message = request.message
11
-
12
- # TODO: wrap in debug if check
13
- Display.debug('RECEIVING: ' + message.type)
14
- Display.debug('RECEIVING: ' + message.sender.to_s)
15
- Display.debug('RECEIVING: ' + message.message.to_s)
16
-
17
- # handle the message
18
- Display.present_message message
19
-
20
- # then update the sender info in the db
21
- update_sender_info(request.json)
22
- end
23
-
24
- def update_sender_info(json)
25
- sender = json['sender']
26
-
27
- # if the sender isn't currently marked as active,
28
- # perform the server list exchange
29
- node = Node.find_by_uid(sender['uid'])
30
- raise Errors::Forbidden.new if node.nil?
31
-
32
- unless node.online?
33
- node.update(online: true)
34
- payload = Message::NodeListHash.new
35
- Client.send(
36
- location: sender['location'],
37
- message: payload)
38
- end
39
-
40
- # update the node's location/alias
41
- # as they can change this info willy nilly
42
- node.update(
43
- location: sender['location'],
44
- alias_name: sender['alias']
45
- )
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,114 +0,0 @@
1
- require 'sinatra/base'
2
- require 'meshchat/net/listener/errors'
3
-
4
- module MeshChat
5
- module Net
6
- module Listener
7
- class Server < Sinatra::Base
8
- OK = 200
9
- BAD_REQUEST = 400
10
- NOT_AUTHORIZED = 401
11
- FORBIDDEN = 403
12
-
13
- configure :development do
14
- # only shows resulting status
15
- disable :logging
16
- # enable :logging
17
- set :bind, '0.0.0.0' #['10.10.2.29','127.0.0.1', 'localhost']
18
- set :show_exceptions, :after_handler
19
- set :threaded, true
20
- end
21
- # TODO: do we want to return an error if
22
- # we can't decrypt?
23
-
24
- def self.run!(*)
25
- # any code that should be ran
26
- # before sintra starts should go here
27
-
28
- # start the server
29
- super
30
- # code after here will not run
31
- end
32
-
33
- get '/' do
34
- process_request
35
- end
36
-
37
- post '/' do
38
- process_request
39
- end
40
-
41
- def process_request
42
- begin
43
- # form params should override
44
- # raw body
45
- raw = get_message
46
- process(raw)
47
- rescue => e
48
- Display.error e.message
49
- Display.error e.backtrace.join("\n")
50
- body e.message
51
- status 500
52
- end
53
- end
54
-
55
- def get_message
56
- # if received as form data
57
- return params[:message] if params[:message]
58
-
59
- # if received as json
60
- request_body = request.body.read
61
- json_body = JSON.parse(request_body)
62
- json_body['message']
63
- end
64
-
65
- def process(raw)
66
- # decode, etc
67
- begin
68
- RequestProcessor.process(raw)
69
- rescue Errors::NotAuthorized
70
- status_of NOT_AUTHORIZED
71
- rescue Errors::Forbidden
72
- status_of FORBIDDEN
73
- rescue Errors::BadRequest
74
- status_of BAD_REQUEST
75
- end
76
- end
77
-
78
- def status_of(s)
79
- status s
80
- Display.debug('SERVER: ' + s.inspect)
81
- body ''
82
- end
83
-
84
- # Hack away our problems
85
- # don't show the Sinatra has taken the stage message
86
- #
87
- # https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1504
88
- def self.start_server(handler, server_settings, handler_name)
89
- handler.run(self, server_settings) do |server|
90
-
91
- setup_traps
92
- set :running_server, server
93
- set :handler_name, handler_name
94
- server.threaded = settings.threaded if server.respond_to? :threaded=
95
-
96
- yield server if block_given?
97
- end
98
- end
99
-
100
- # Hack away our problems
101
- # Don't show the Sinatra quit message
102
- #
103
- # https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1420
104
- def self.quit!
105
- return unless running?
106
- # Use Thin's hard #stop! if available, otherwise just #stop.
107
- running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
108
- set :running_server, nil
109
- set :handler_name, nil
110
- end
111
- end
112
- end
113
- end
114
- end