globalchat 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/server.rb ADDED
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gserver'
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'securerandom'
6
+ require 'pstore'
7
+
8
+ class GlobalChatServer < GServer
9
+
10
+ attr_accessor :handles, :buffer, :handle_keys, :sockets, :password, :socket_keys, :scrollback, :server_name
11
+
12
+ def initialize(port=9994, *args)
13
+ super(port, *args)
14
+ self.audit = true
15
+ self.debug = true
16
+ @pstore = PStore.new("gchat.pstore")
17
+ @handle_keys = {} # stores handle
18
+ @socket_keys = {} # stores chat_token
19
+ # @port_keys = {} # unnecessary in PING design
20
+ @handle_last_pinged = {} # used for clone removal
21
+ @handles = []
22
+ @sockets = []
23
+ @buffer = []
24
+ @server_name = "GlobalChatNet"
25
+ load_chat_log
26
+ @mutex = Mutex.new
27
+ end
28
+
29
+ def broadcast(message, sender=nil)
30
+ @mutex.synchronize do
31
+ @sockets.each do |socket|
32
+ begin
33
+ sock_send(socket, message) unless socket == sender
34
+ rescue
35
+ log "broadcast fail removal event"
36
+ remove_dead_socket socket
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def remove_dead_socket(socket)
43
+ @sockets.delete socket
44
+ ct = @socket_keys[socket]
45
+ handle = @handle_keys[ct]
46
+ @handles.delete handle
47
+ @handle_keys.delete ct
48
+ @socket_keys.delete socket
49
+ end
50
+
51
+ def remove_user_by_handle(handle)
52
+ ct = @handle_keys.key(handle)
53
+ handle = @handle_keys[ct]
54
+ socket = @socket_keys.key(ct)
55
+ @sockets.delete socket
56
+
57
+ @handles.delete handle
58
+ @handle_keys.delete ct
59
+ @socket_keys.delete socket
60
+ begin
61
+ broadcast_message(socket, "LEAVE", [handle])
62
+ rescue
63
+ log "failed to broadcast LEAVE for clone handle #{handle}"
64
+ end
65
+ end
66
+
67
+ def check_token(chat_token)
68
+ sender = @handle_keys[chat_token]
69
+ return !sender.nil?
70
+ end
71
+
72
+ def get_handle(chat_token)
73
+ sender = @handle_keys[chat_token]
74
+ return sender
75
+ end
76
+
77
+ # server tell a single socket
78
+ def send_message(io, opcode, args)
79
+ msg = opcode + "::!!::" + args.join("::!!::")
80
+ sock_send io, msg
81
+ end
82
+
83
+ def sock_send io, msg
84
+ msg = "#{msg}\0"
85
+ log msg
86
+ io.send msg, 0
87
+ end
88
+
89
+ # server tell all sockets except
90
+ # if sender is nil then everyone
91
+ def broadcast_message(sender, opcode, args)
92
+ msg = opcode + "::!!::" + args.join("::!!::")
93
+ broadcast msg, sender
94
+ end
95
+
96
+ def build_chat_log
97
+ return "" unless @scrollback
98
+ out = ""
99
+ @buffer.each do |msg|
100
+ out += "#{msg[0]}: #{msg[1]}\n"
101
+ end
102
+ out
103
+ end
104
+
105
+
106
+ def clean_handles
107
+ @handle_keys.each do |k, v|
108
+ if @handle_last_pinged[v] && @handle_last_pinged[v] < Time.now - 30
109
+ log "removed clone handle: #{v}"
110
+ remove_user_by_handle(v)
111
+ end
112
+ end
113
+ end
114
+
115
+ def build_handle_list
116
+ return @handles.join("\n")
117
+ end
118
+
119
+ # react to allowed commands
120
+ def parse_line(line, io)
121
+ parr = line.split("::!!::")
122
+ command = parr[0]
123
+ if command == "SIGNON"
124
+ handle = parr[1]
125
+ password = parr[2]
126
+
127
+ if !@handles.length == 0 && @handles.include?(handle)
128
+ send_message(io, "ALERT", ["Your handle is in use."])
129
+ io.close
130
+ return
131
+ end
132
+
133
+ if handle == nil || handle == ""
134
+ send_message(io, "ALERT", ["You cannot have a blank name."])
135
+ #remove_dead_socket io
136
+ io.close
137
+ return
138
+ end
139
+
140
+ if ((@password == password) || ((password === nil) && (@password == "")))
141
+ # uuid are guaranteed unique
142
+ chat_token = rand(36**8).to_s(36)
143
+ @mutex.synchronize do
144
+ @handle_keys[chat_token] = handle
145
+ @socket_keys[io] = chat_token
146
+ # @port_keys[io.peeraddr[1]] = chat_token
147
+ # not on list until pinged.
148
+ @handles << handle
149
+ @sockets << io
150
+ end
151
+ send_message(io, "TOKEN", [chat_token, handle, @server_name])
152
+ broadcast_message(io, "JOIN", [handle])
153
+ else
154
+
155
+ send_message(io, "ALERT", ["Password is incorrect."])
156
+ io.close
157
+
158
+ end
159
+
160
+ return
161
+
162
+ end
163
+
164
+ # auth
165
+ chat_token = parr.last
166
+
167
+ if check_token(chat_token)
168
+ handle = get_handle(chat_token)
169
+ if command == "GETHANDLES"
170
+ send_message(io, "HANDLES", [build_handle_list])
171
+ elsif command == "GETBUFFER"
172
+ buffer = build_chat_log
173
+ send_message(io, "BUFFER", [buffer])
174
+ elsif command == "MESSAGE"
175
+ msg = parr[1]
176
+ message = "#{handle}: #{msg}\n"
177
+ @buffer << [handle, msg]
178
+ broadcast_message(io, "SAY", [handle, msg])
179
+ elsif command == "PING"
180
+ unless @handles.include?(handle)
181
+ @handles << handle
182
+ end
183
+ @handle_last_pinged[handle] = Time.now
184
+ elsif command == "SIGNOFF"
185
+ broadcast_message(nil, "LEAVE", [handle])
186
+ end
187
+ end
188
+ end
189
+
190
+ def pong_everyone
191
+ #log "trying to pong"
192
+ unless @sockets.length == 0 && !self.stopped?
193
+ #log "ponging"
194
+ broadcast_message(nil, "PONG", [build_handle_list])
195
+ sleep 5
196
+ clean_handles
197
+ end
198
+ end
199
+
200
+ def start_pong_loop
201
+ Thread.new do
202
+ loop do
203
+ sleep 5
204
+ pong_everyone
205
+ end
206
+ end
207
+ end
208
+
209
+ # def disconnecting(clientPort)
210
+ # log "disconnect event"
211
+ # ct = @port_keys[clientPort]
212
+ # handle = @handle_keys[ct]
213
+ # if handle
214
+ # log "disconnect removal event"
215
+ # remove_dead_socket ct
216
+ # end
217
+ # super(clientPort)
218
+ # end
219
+ def starting
220
+ log("GlobalChat2 Server Running")
221
+ start_pong_loop
222
+ end
223
+
224
+ def serve(io)
225
+ loop do
226
+ data = ""
227
+ begin
228
+ while line = io.recv(1)
229
+ break if line == "\0"
230
+ data += line
231
+ end
232
+ rescue
233
+ log "recv break removal event"
234
+ remove_dead_socket io #, true
235
+ break
236
+ end
237
+ unless data == ""
238
+ log "#{data}"
239
+ parse_line(data, io)
240
+ end
241
+ end
242
+ end
243
+
244
+ def status
245
+ passworded = (self.password != "")
246
+ scrollback = self.scrollback
247
+ log "#{@server_name} running on GlobalChat2 3.0 platform Replay:#{scrollback} Passworded:#{passworded}"
248
+ end
249
+
250
+ def log(msg)
251
+ puts msg
252
+ end
253
+
254
+
255
+ def save_chat_log
256
+ log "saving chatlog"
257
+ @pstore.transaction do
258
+ @pstore[:log] = @buffer
259
+ #p @pstore[:log]
260
+ end
261
+
262
+ end
263
+
264
+ def load_chat_log
265
+ log "loading chatlog"
266
+ @pstore.transaction(true) do
267
+ @buffer = @pstore[:log] || []
268
+ #p @buffer
269
+ end
270
+ end
271
+
272
+ end
273
+
274
+
275
+ def ping_nexus(chatnet_name, host, port)
276
+ puts "Pinging NexusNet that I'm Online!!"
277
+ uri = URI.parse("http://nexusnet.herokuapp.com/online")
278
+ query = {:name => chatnet_name, :port => port, :host => host}
279
+ uri.query = URI.encode_www_form( query )
280
+ Net::HTTP.get(uri)
281
+ $published = true
282
+ end
283
+
284
+ def nexus_offline
285
+ puts "Informing NexusNet that I have exited!!!"
286
+ Net::HTTP.get_print("nexusnet.herokuapp.com", "/offline")
287
+ end
288
+
289
+ at_exit do
290
+ nexus_offline
291
+ $gc.save_chat_log
292
+ end
293
+
294
+ $gc = GlobalChatServer.new(9994, '0.0.0.0', 1000, $stderr, true)
295
+ $gc.password = "" # set a password here
296
+ $gc.scrollback = true
297
+ $gc.start
298
+
299
+ if ENV["RUBY_VERSION"] && ENV["RUBY_VERSION"].include?("1.9")
300
+ ping_nexus("GlobalChatNet2", "localhost", $gc.port)
301
+ end
302
+
303
+ $gc.status
304
+
305
+ $gc.join
306
+
307
+
308
+
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: globalchat
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jonathan "jsilver1er" Silverman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: qtbindings
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 4.8.3.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 4.8.3.0
30
+ description: GlobalChat 2 Pro Chat Client
31
+ email:
32
+ - jsilverone@me.com
33
+ executables:
34
+ - globalchat
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .DS_Store
39
+ - .gitignore
40
+ - .rvmrc
41
+ - Gemfile
42
+ - GlobalChat.ui
43
+ - LICENSE
44
+ - LICENSE.rtf
45
+ - README.md
46
+ - Rakefile
47
+ - ServerList.ui
48
+ - gc2-qtruby.gemspec
49
+ - gchat2pro.pstore
50
+ - global_chat_2_pro.rb
51
+ - globalchat2.ico
52
+ - lib/gc2-qtruby.rb
53
+ - lib/gc2-qtruby/version.rb
54
+ - server.rb
55
+ - bin/globalchat
56
+ homepage: http://globalchat2.net
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - .
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.24
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: QTRuby crossplatform version
80
+ test_files: []