torchat 0.0.1.rc.1

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 (43) hide show
  1. data/README.md +43 -0
  2. data/bin/torchatd +537 -0
  3. data/doc/protocol/broadcast.md +26 -0
  4. data/doc/protocol/groupchat.md +140 -0
  5. data/doc/protocol/latency.md +30 -0
  6. data/doc/protocol/standard.md +279 -0
  7. data/doc/protocol/typing.md +41 -0
  8. data/etc/torchat.yml +12 -0
  9. data/lib/torchat.rb +239 -0
  10. data/lib/torchat/protocol.rb +256 -0
  11. data/lib/torchat/protocol/broadcast.rb +40 -0
  12. data/lib/torchat/protocol/groupchat.rb +197 -0
  13. data/lib/torchat/protocol/latency.rb +44 -0
  14. data/lib/torchat/protocol/standard.rb +367 -0
  15. data/lib/torchat/protocol/typing.rb +36 -0
  16. data/lib/torchat/session.rb +603 -0
  17. data/lib/torchat/session/broadcast/message.rb +50 -0
  18. data/lib/torchat/session/broadcasts.rb +72 -0
  19. data/lib/torchat/session/buddies.rb +152 -0
  20. data/lib/torchat/session/buddy.rb +343 -0
  21. data/lib/torchat/session/buddy/joined_group_chat.rb +42 -0
  22. data/lib/torchat/session/buddy/joined_group_chats.rb +46 -0
  23. data/lib/torchat/session/buddy/latency.rb +54 -0
  24. data/lib/torchat/session/event.rb +79 -0
  25. data/lib/torchat/session/file_transfer.rb +173 -0
  26. data/lib/torchat/session/file_transfer/block.rb +51 -0
  27. data/lib/torchat/session/file_transfers.rb +89 -0
  28. data/lib/torchat/session/group_chat.rb +123 -0
  29. data/lib/torchat/session/group_chat/participant.rb +38 -0
  30. data/lib/torchat/session/group_chat/participants.rb +52 -0
  31. data/lib/torchat/session/group_chats.rb +74 -0
  32. data/lib/torchat/session/incoming.rb +187 -0
  33. data/lib/torchat/session/outgoing.rb +102 -0
  34. data/lib/torchat/tor.rb +107 -0
  35. data/lib/torchat/utils.rb +87 -0
  36. data/lib/torchat/version.rb +24 -0
  37. data/test/file_transfer/receiver.rb +41 -0
  38. data/test/file_transfer/sender.rb +45 -0
  39. data/test/group_chat/a.rb +37 -0
  40. data/test/group_chat/b.rb +37 -0
  41. data/test/group_chat/c.rb +57 -0
  42. data/torchat.gemspec +21 -0
  43. metadata +140 -0
@@ -0,0 +1,42 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of torchat for ruby.
5
+ #
6
+ # torchat for ruby is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # torchat for ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'delegate'
21
+
22
+ require 'torchat/session/group_chat'
23
+
24
+ class Torchat; class Session; class Buddy
25
+
26
+ class JoinedGroupChat < DelegateClass(GroupChat)
27
+ def initialize (group_chat, buddy)
28
+ super(group_chat)
29
+
30
+ @buddy = buddy
31
+ end
32
+
33
+ def leave (reason = nil)
34
+ delete(@buddy)
35
+
36
+ session.fire :group_chat_leave, group_chat: group_chat, buddy: @buddy, reason: reason
37
+ end
38
+
39
+ alias ~ __getobj__
40
+ end
41
+
42
+ end; end; end
@@ -0,0 +1,46 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of torchat for ruby.
5
+ #
6
+ # torchat for ruby is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # torchat for ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'torchat/session/buddy/joined_group_chat'
21
+
22
+ class Torchat; class Session; class Buddy
23
+
24
+ class JoinedGroupChats < Hash
25
+ attr_reader :buddy
26
+
27
+ def initialize (buddy)
28
+ @buddy = buddy
29
+ end
30
+
31
+ def add (id)
32
+ group_chat = id.is_a?(GroupChat) ? id : buddy.session.group_chats[id]
33
+
34
+ if group_chat
35
+ self[group_chat.id] = JoinedGroupChat.new(group_chat, buddy)
36
+ end
37
+ end
38
+
39
+ def delete (id)
40
+ super(id.respond_to?(:id) ? id.id : id)
41
+ end
42
+
43
+ private :[]=
44
+ end
45
+
46
+ end; end; end
@@ -0,0 +1,54 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of torchat for ruby.
5
+ #
6
+ # torchat for ruby is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # torchat for ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ class Torchat; class Session; class Buddy
21
+
22
+ class Latency
23
+ attr_reader :buddy
24
+
25
+ def initialize (buddy)
26
+ @buddy = buddy
27
+ end
28
+
29
+ def ping!
30
+ @last_check = buddy.send_packet [:latency, :ping]
31
+ end
32
+
33
+ def pong (id)
34
+ return unless id == @last_check.id
35
+
36
+ @internal = Time.now - @last_check.at
37
+ end
38
+
39
+ def to_f
40
+ unless @internal
41
+ ping! unless @last_check
42
+
43
+ return 0
44
+ end
45
+
46
+ @internal
47
+ end
48
+
49
+ def to_i
50
+ to_f.to_i
51
+ end
52
+ end
53
+
54
+ end; end; end
@@ -0,0 +1,79 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of torchat for ruby.
5
+ #
6
+ # torchat for ruby is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # torchat for ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'ostruct'
21
+
22
+ class Torchat; class Session
23
+
24
+ class Event
25
+ class Removable
26
+ attr_reader :session, :name, :chain, :block
27
+
28
+ def initialize (session, name, chain = nil, &block)
29
+ @session = session
30
+ @name = name
31
+ @chain = chain
32
+ @block = block
33
+ end
34
+
35
+ def removed?; @removed; end
36
+
37
+ def remove!
38
+ return if removed?
39
+
40
+ @removed = true
41
+
42
+ session.remove_callback(chain, name, block)
43
+ end
44
+ end
45
+
46
+ attr_reader :session, :name
47
+
48
+ def initialize (session, name, data = nil, &block)
49
+ @session = session
50
+ @name = name
51
+
52
+ @data = OpenStruct.new(data).tap(&block || proc {}).marshal_dump
53
+ end
54
+
55
+ def method_missing (id, *args)
56
+ unless args.empty?
57
+ raise ArgumentError, 'tried to pass parameters to an Event attribute'
58
+ end
59
+
60
+ @data[id] if @data.has_key? id
61
+ end
62
+
63
+ def respond_to_missing? (*)
64
+ true
65
+ end
66
+
67
+ def remove!; @remove = true; end
68
+ def removed!; @remove = false; end
69
+ def remove?; @remove; end
70
+
71
+ def stop!; @stop = true; end
72
+ def stopped?; @stop; end
73
+
74
+ def inspect
75
+ "#<Torchat::Event(#{name}): #{@data.inspect}>"
76
+ end
77
+ end
78
+
79
+ end; end
@@ -0,0 +1,173 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of torchat for ruby.
5
+ #
6
+ # torchat for ruby is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # torchat for ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'digest/md5'
21
+ require 'forwardable'
22
+
23
+ require 'torchat/session/file_transfer/block'
24
+
25
+ class Torchat
26
+
27
+ class FileTransfer
28
+ extend Forwardable
29
+
30
+ attr_reader :file_transfers, :id, :name, :size, :output, :input
31
+ attr_accessor :from, :to, :block_size
32
+
33
+ alias length size
34
+
35
+ def initialize (file_transfers = nil, name, size)
36
+ @file_transfers = file_transfers
37
+ @id = id || Torchat.new_cookie
38
+ @name = name
39
+ @size = size
40
+ @block_size = 4096
41
+ end
42
+
43
+ def outgoing?
44
+ !!to
45
+ end
46
+
47
+ def incoming?
48
+ !!from
49
+ end
50
+
51
+ def input= (io)
52
+ raise ArgumentError, 'you cannot change input' if @input && @input != io
53
+
54
+ @input = io
55
+ end
56
+
57
+ def output= (io)
58
+ raise ArgumentError, 'you cannot change output' if @output && @output != io
59
+
60
+ @output = io
61
+
62
+ @cache.each {|block|
63
+ output.seek(block.offset)
64
+ output.write(block.data)
65
+ }
66
+
67
+ @cache = nil
68
+ end
69
+
70
+ def tell
71
+ if @input
72
+ @input.tell
73
+ elsif @output
74
+ @output.size
75
+ elsif @cache
76
+ block = @cache.max { |a, b| a.offset <=> b.offset }
77
+
78
+ block.offset + block.size
79
+ else
80
+ 0
81
+ end
82
+ end
83
+
84
+ def completion
85
+ tell.to_f / size * 100
86
+ end
87
+
88
+ def finished?
89
+ io.size >= size
90
+ end
91
+
92
+ def add_block (offset, data, md5 = nil)
93
+ Block.new(self, offset, data, md5).tap {|block|
94
+ unless block.valid?
95
+ from.send_packet :filedata_error, id, offset
96
+
97
+ return false
98
+ end
99
+
100
+ if output
101
+ output.seek(offset)
102
+ output.write(data)
103
+ else
104
+ (@cache ||= []) << block
105
+ end
106
+
107
+ fire :file_transfer_activity, file_transfer: self
108
+
109
+ if completion == 100
110
+ fire :file_transfer_complete, file_transfer: self
111
+ end
112
+ }
113
+ end
114
+
115
+ def next_block
116
+ raise 'there is no input' unless input
117
+
118
+ return if input.tell >= input.size
119
+
120
+ @last = Block.new(self, input.tell, input.read(block_size))
121
+ end
122
+
123
+ def last_block
124
+ raise 'there is no input' unless input
125
+
126
+ @last = Block.new(self, input.tell, input.read(block_size))
127
+ end
128
+
129
+ def send_next_block
130
+ if block = next_block
131
+ to.send_packet :filedata, file_transfer.id, block.offset, block.data, block.md5
132
+
133
+ fire :file_transfer_activity, file_transfer: self
134
+ else
135
+ fire :file_transfer_complete, file_transfer: self
136
+ end
137
+ end
138
+
139
+ def send_last_block
140
+ if block = file_transfer.last_block
141
+ to.send_packet :filedata, file_transfer.id, block.offset, block.data, block.md5
142
+ end
143
+ end
144
+
145
+ def stopped?; @stopped; end
146
+
147
+ def stop (interrupted_by_other = false)
148
+ unless interrupted_by_other
149
+ if incoming?
150
+ from.send_packet :file_stop_sending, id
151
+ elsif outgoing?
152
+ to.send_packet :file_stop_receiving, id
153
+ else
154
+ raise ArgumentError, 'the file transfer is unstoppable, call Denzel Washington'
155
+ end
156
+ end
157
+
158
+ @stopped = true
159
+
160
+ if file_transfers
161
+ file_transfers.delete(id)
162
+ file_transfers.session.fire :file_transfer_stop, file_transfer: self
163
+ end
164
+
165
+ self
166
+ end
167
+
168
+ def inspect
169
+ "#<Torchat::FileTransfer(#{id}): #{name} #{tell}/#{size} (#{'%.2f' % completion}%)>"
170
+ end
171
+ end
172
+
173
+ end
@@ -0,0 +1,51 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of torchat for ruby.
5
+ #
6
+ # torchat for ruby is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # torchat for ruby is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'digest/md5'
21
+
22
+ class Torchat; class FileTransfer
23
+
24
+ class Block
25
+ attr_reader :owner, :offset, :data, :md5
26
+
27
+ def initialize (owner, offset, data, md5 = nil)
28
+ @owner = owner
29
+ @offset = offset
30
+ @data = data.force_encoding('BINARY')
31
+ @md5 = md5 || Digest::MD5.hexdigest(data)
32
+ end
33
+
34
+ def size
35
+ @size || @data.bytesize
36
+ end
37
+
38
+ alias length size
39
+
40
+ def valid?
41
+ Digest::MD5.hexdigest(data) == md5
42
+ end
43
+
44
+ def to_s
45
+ @data
46
+ end
47
+
48
+ alias to_str to_s
49
+ end
50
+
51
+ end; end