konn-rupircd 0.6.0 → 0.6.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.
@@ -0,0 +1,677 @@
1
+ =begin
2
+ = rupircd -- Ruby Pseudo IRC Deamon
3
+
4
+ ver 0.6 2009-08-11T23:45:52+09:00
5
+
6
+ Copyright (c) 2009 Hiromi Ishii
7
+
8
+ You can redistribute it and/or modify it under the same term as Ruby.
9
+ =end
10
+
11
+ require 'webrick/server'
12
+ require 'monitor'
13
+ require 'thread'
14
+ require 'resolv'
15
+ require 'digest/md5'
16
+
17
+ require 'rupircd/channel.rb'
18
+ require 'rupircd/user'
19
+ require 'rupircd/utils.rb'
20
+ require 'rupircd/message.rb'
21
+ require 'rupircd/charcode'
22
+ require 'rupircd/conf'
23
+
24
+ module IRCd
25
+ class IRCServer < WEBrick::GenericServer
26
+ include Utils
27
+ include Reply
28
+ include Command
29
+
30
+ include MonitorMixin
31
+
32
+ def initialize(fconf, *args)
33
+ @_init_args = [fconf, *args]
34
+ @fconf = fconf
35
+ super(fconf.load, *args)
36
+ @used = Hash.new{|h, k| h[k.upcase] = [0,0,0]}
37
+ init
38
+ end
39
+
40
+ def init
41
+ @operators = []
42
+ @channels = {}
43
+ @users = []
44
+ @users.extend(MonitorMixin)
45
+ @ping_threads = {}
46
+ @ping_threads.extend(MonitorMixin)
47
+ @old_nicks = Hash.new{|h, k| h[k] = []}
48
+ config.replace(@fconf.load)
49
+ cf = {}
50
+ config.fetch(:Opers,{}).each{|nick, pass|
51
+ cf[mask_to_regex(nick)] = pass
52
+ }
53
+ config[:Opers] = cf
54
+ config[:ServerName] = "127.0.0.1"
55
+ end
56
+
57
+ def user_from_nick(nick)
58
+ u = @users.find{|u| u.nick == nick}
59
+ end
60
+
61
+ def start_ping(user)
62
+ @ping_threads[user] = Thread.new{
63
+ sleep(config[:PINGInterval])
64
+ send_client_message(user, nil, PING, config[:ServerName])
65
+ sleep(config[:PINGLimit])
66
+ unregister(user, "PING Timeout")
67
+ }
68
+ end
69
+
70
+ def run(socket)
71
+ socket.extend(MonitorMixin)
72
+ user = nil
73
+ nick = nil
74
+ pass = nil
75
+ begin
76
+ first = true
77
+ while line = socket.gets
78
+ line.chomp!
79
+ next if line.empty?
80
+ case msg = Message.parse(line)
81
+ when PASS
82
+ if first
83
+ pass = msg.params[-1]
84
+ first = false
85
+ else
86
+ end
87
+ when USER
88
+ user = msg.params
89
+ if msg.params.size < 4
90
+ socket.puts err_needmoreparams(msg.command, "Not enough parameters")
91
+ user = nil
92
+ end
93
+ when NICK
94
+ nick = msg.params[0]
95
+ if us = @users.find{|us| us.nick == nick}
96
+ socket.puts ERR_NICKNAMEINUSE.new(config[:ServerName], "433", [nick, "Nickname is already in use"])
97
+ nick = nil
98
+ else
99
+ end
100
+ end
101
+ break if nick && user
102
+ end
103
+ end
104
+ return unless nick && user
105
+ start_ping(user)
106
+ begin
107
+ host = Resolv.getname(socket.addr[-1])
108
+ rescue
109
+ host = socket.addr[-1]
110
+ end
111
+ user = User.new(nick, "~"+user[0].split("@")[0], host, user[-1], socket, user[1])
112
+ @old_nicks[nick].unshift user.to_a
113
+ @users << user
114
+ send_server_message(user, "001", "Welcome to the Internet Relay Network #{user}")
115
+ send_server_message(user, "002", "Your host is #{config[:ServerName]}, running version 0.1b")
116
+ send_server_message(user, "003", "This server was created #{Time.now}")
117
+ send_server_message(user, "004", "#{config[:ServerName]} 0.1b aiwroOs oavimnqsrtklObeI")
118
+ print_motd(user)
119
+ while !socket.closed? && line = socket.gets
120
+ recv_message(user, line)
121
+ end
122
+ unregister(user)
123
+ end
124
+
125
+ def unregister(user, msg="EOF From client")
126
+ synchronize do
127
+ socket = user.socket
128
+ @ping_threads[user].kill if @ping_threads.has_key?(socket)
129
+ sent = []
130
+ if @users.include?(user)
131
+ user.joined_channels.each{|ch|
132
+ ch.unregister(user)
133
+ ch.members.each{|mem|
134
+ unless sent.include?(mem)
135
+ sent << mem
136
+ send_client_message(mem, user, QUIT, msg)
137
+ end
138
+ }
139
+ @channels.reject!{|k,v|v==ch} if ch.members.empty?
140
+ }
141
+ @users.delete(user)
142
+ end
143
+ end
144
+ user.socket.close unless user.socket.closed?
145
+ end
146
+
147
+ def print_motd(user)
148
+ send_server_message(user, "375", "- #{config[:ServerName]} Message of the day - ")
149
+ config[:Motd].each_line{|line|
150
+ line.chomp!
151
+ send_server_message(user, "372", "- " + line)
152
+ }
153
+ send_server_message(user, "376", "End of MOTD command")
154
+ end
155
+
156
+ def oper_proc(usr, &pr)
157
+ if usr.operator || usr.local_operator
158
+ pr.call(usr)
159
+ else
160
+ send_server_message(usr, "481", "Permission Denied - You're not an IRC operator")
161
+ end
162
+ end
163
+
164
+ def recv_message(user, line)
165
+ line.chomp!
166
+ return if line.empty?
167
+ begin
168
+ msg = Message.parse(line)
169
+ params = msg.params
170
+ @used[msg.command.upcase][0] += 1
171
+ case msg
172
+ when MOTD
173
+ print_motd(user)
174
+ when LUSERS
175
+ send_server_message(user, "251", "There are #{@users.size} users and 0 services on 1 servers")
176
+ unless @operators.empty?
177
+ send_server_message(user, "252", @operators.size, "operator(s) online")
178
+ end
179
+ unless @channels.empty?
180
+ send_server_message(user, "254", @channels.size, "channels formed")
181
+ end
182
+ send_server_message(user, "255", "I have #{@users.size} clients and 0 servers")
183
+ when OPER
184
+ raise NotEnoughParameter if params.size < 2
185
+ nick, pass = params
186
+ unless opdic = config.fetch(:Opers,{}).find{|k, v| k =~ nick}
187
+ send_server_message(user, "491", "No O-lines for your host")
188
+ return
189
+ end
190
+ if Digest::MD5.hexdigest(pass) == opdic[1]
191
+ user.operator = true
192
+ user.local_operator = true
193
+ @operators.push user
194
+ send_server_message(user, "381", "You are now an IRC operator")
195
+ else
196
+ send_server_message(user, "464", "Password incorrect")
197
+ end
198
+ when CLOSE
199
+ @users.synchronize do
200
+ oper_proc(user) do
201
+ @users.each{|s| unregister(s) }
202
+ end
203
+ end
204
+ when DIE
205
+ oper_proc(user) do
206
+ shutdown
207
+ exit!
208
+ end
209
+ when REHASH
210
+ oper_proc(user) do |usr|
211
+ cnf = @fconf.load
212
+ if Hash === cnf
213
+ config.replace(cnf)
214
+ send_server_message(usr, "382", "#{@fconf.path} :Rehashing")
215
+ else
216
+ raise "Invalid Conf file"
217
+ end
218
+ end
219
+ when RESTART
220
+ oper_proc(user) do |usr|
221
+ @users.each{|s| unregister(s) }
222
+ init
223
+ end
224
+ when VERSION
225
+ send_server_message(user, "351", "rupircd-01b.0 #{config[:ServerName]} :Ruby Pseudo IRCD 0.1b")
226
+ when STATS
227
+ if params.empty?
228
+ raise NotEnoughParameter
229
+ return
230
+ end
231
+ c = params[0][0]
232
+ case c
233
+ when ?l
234
+
235
+ when ?m
236
+ @used.each_pair{|cmd, val|
237
+ send_server_message(user, "212", cmd, val[0])
238
+ }
239
+ when ?o
240
+
241
+ when ?u
242
+ vs = Time.now.to_i - @started.to_i
243
+ days = vs / (3600*24)
244
+ vs -= days * 3600 * 24
245
+ hours = vs / 3600
246
+ vs -= hours * 3600
247
+ minutes = vs/60
248
+ vs -= minutes * 60
249
+ send_server_message(user, "242", format("Server Up %d days %d:%02d:%02d",days,hours,minutes,vs))
250
+ end
251
+ send_server_message(user, "219", c.chr, "End of STATS report")
252
+ when JOIN
253
+ if params[0] == "0"
254
+ user.joined_channels.each{|ch|
255
+ channel(ch).part(user, "")
256
+ }
257
+ user.joined_channels = []
258
+ else
259
+ chs = params[0].split(",")
260
+ keys = msg.params[1].split(",") if params.size >= 2
261
+ keys ||= []
262
+ chs.each_with_index{|ch, i|
263
+ unless channame?(ch)
264
+ send_server_message(user, "403", ch, "No such channel")
265
+ next
266
+ end
267
+
268
+ chclass = case ch
269
+ when /^\+/
270
+ NoModeChannel
271
+ when /^#/
272
+ Channel
273
+ when /^!#/
274
+ SafeChannel
275
+ end
276
+ unless @channels.has_key?(ch.downcase)
277
+ set_channel(ch, chclass.new(self, user, ch) )
278
+ handle_reply(user, channel(ch).join(user, keys[i]))
279
+ else
280
+ rpl = channel(ch).join(user, keys[i])
281
+ handle_reply(user, rpl)
282
+ end
283
+ }
284
+ end
285
+ when PART
286
+ ch, msg = params
287
+ msg ||= ""
288
+ chs = ch.split(",")
289
+ chs.each{|chname|
290
+ ch = channel(chname)
291
+ ch.part(user, msg)
292
+ if ch.members.empty?
293
+ @channels.delete(chname.downcase)
294
+ end
295
+ user.joined_channels.delete(ch)
296
+ }
297
+ when PING
298
+ send_client_message(user, config[:ServerName], PONG, config[:ServerName], *params)
299
+ when INFO
300
+ config[:Info].each_line{|line|
301
+ line.chomp!
302
+ send_server_message(user, "371", line)
303
+ }
304
+ send_server_message(user, "374", "End of INFO list")
305
+ when LINKS
306
+ send_server_message(user, "365")
307
+ when TIME
308
+ send_server_message(user, "391", config[:ServerName], Time.now.to_s)
309
+ when PONG
310
+ @ping_threads[user].kill
311
+ start_ping(user)
312
+ when INVITE
313
+ if params.size < 2
314
+ raise NotEnoughParameter
315
+ else
316
+ who, to = params
317
+ if target = @users.find{|v| v.nick == who}
318
+ if target.away?
319
+ msg = target.away
320
+ send_server_message(user, "301", who, msg)
321
+ return
322
+ end
323
+ if ch = channel(to)
324
+ handle_reply(user, ch.invite(user, target))
325
+ else
326
+ send_server_message(user, "401", who, "No such nick/channel")
327
+ end
328
+ else
329
+ send_server_message(user, "401", who, "No such nick/channel")
330
+ end
331
+ end
332
+ when LIST
333
+ if params.empty?
334
+ chs = @channels.find_all{|k, v| v.visible?(nil)}
335
+ chs.each{|k, v|
336
+ case (tpc = v.get_topic(user, true))[0]
337
+ when "331"
338
+ tpc = ""
339
+ else
340
+ tpc = tpc[-1]
341
+ end
342
+ send_server_message(user, "322", k, v.members.size.to_s, tpc)
343
+ }
344
+ else
345
+ chs = params[0].split(",").find_all{|name| channel(name) && channel(n).visible?(user)}
346
+ chs.each{|k|
347
+ v = channel(k)
348
+ uss = v.members.find_all{|m| !m.invisible}
349
+ case (tpc = v.get_topic(user, true))[0]
350
+ when "331"
351
+ tpc = ""
352
+ else
353
+ tpc = tpc[-1]
354
+ end
355
+ send_server_message(user, "322", k, v.members.size.to_s, tpc)
356
+ }
357
+ end
358
+ send_server_message(user, "323", "End of LIST")
359
+ when TOPIC
360
+ chn, topic = params
361
+ unless ch = channel(chn)
362
+ send_server_message(user, "403", chn, "No such channel")
363
+ return
364
+ end
365
+ if params.size < 1
366
+ raise NotEnoughParameter
367
+ elsif params.size == 1
368
+ send_server_message(user, *ch.get_topic(user))
369
+ else
370
+ handle_reply(user, ch.set_topic(user, topic))
371
+ end
372
+ when PRIVMSG
373
+ if params.size < 2
374
+ raise NotEnoughParameter
375
+ end
376
+ to, msg = params
377
+ if ch = channel(to)
378
+ if msg.empty?
379
+ send_server_message(user, "412", "No text to send")
380
+ else
381
+ handle_reply(user, ch.privmsg(user, msg))
382
+ end
383
+ elsif who = @users.find{|v| v.nick == to}
384
+ if who.away?
385
+ away = who.away
386
+ send_server_message(user, "301", to, away)
387
+ else
388
+ send_client_message(who, user, PRIVMSG, who.nick, msg)
389
+ end
390
+ else
391
+ send_server_message(user, "401", to, "No such nick/channel")
392
+ end
393
+ when NAMES
394
+ if params.size < 1
395
+ raise NotEnoughParameter
396
+ else
397
+ chs = params[0].split(",")
398
+ chs.each{|ch|
399
+ handle_reply(user, channel(ch).names(user))
400
+ }
401
+ end
402
+ when NOTICE
403
+ if params.size < 2
404
+ raise NotEnoughParameter
405
+ return
406
+ end
407
+ to, msg = params
408
+ if ch = channel(to)
409
+ if msg.empty?
410
+ send_server_message(user, "412", "No text to send")
411
+ else
412
+ handle_reply(user, ch.notice(user, msg))
413
+ end
414
+ elsif who = @users.find{|v| v.nick == to}
415
+ send_client_message(who, user, NOTICE, user.nick, msg)
416
+ else
417
+ send_server_message(user, "401", to, "No such nick/channel")
418
+ end
419
+ when AWAY
420
+ if params.empty? || params[0].empty?
421
+ user.away = ""
422
+ send_server_message(user, "305", "You are no longer marked as being away")
423
+ else
424
+ user.away = params.shift
425
+ send_server_message(user, "306", "You have been marked as being away")
426
+ end
427
+ when MODE
428
+ if params.size < 1
429
+ raise NotEnoughParameter
430
+ return
431
+ end
432
+ to = params.shift
433
+ if ch = channel(to)
434
+ handle_reply(user, ch.handle_mode(user, params))
435
+ elsif who = @users.find{|v| v.nick == to}
436
+ if who == user
437
+ if params.empty?
438
+ send_server_message(user, "221", who.get_mode_flags)
439
+ else
440
+ rls = who.set_flags(params[0])
441
+ send_client_message(user, user, MODE, user.nick, rls.join(" "))
442
+ end
443
+ else
444
+ send_server_message(user, "502", "Cannot change mode for other users")
445
+ end
446
+ else
447
+ send_server_message(user, "401", to, "No such nick/channel")
448
+ end
449
+ when NICK
450
+ if params.empty?
451
+ send_server_message(user, "431", "No nickname given")
452
+ else
453
+ nick = params[0]
454
+ if nick == "0"
455
+ nick = user.identifier.to_s
456
+ elsif !correct_nick?(nick)
457
+ send_server_message(user, "432", nick, "Erroneous nickname")
458
+ return
459
+ end
460
+ if us = user_from_nick(nick)
461
+ return if us == user
462
+ send_server_message(user, "433", nick, "Nickname is already in use")
463
+ elsif user.restriction
464
+ send_server_message(user, "484", "Your connection is restricted!")
465
+ else
466
+ send_client_message(user, user, NICK, nick)
467
+ sent = []
468
+ user.joined_channels.each{|ch|
469
+ ch.members.each{|mem|
470
+ if !sent.include?(mem) && mem != user
471
+ sent << mem
472
+ send_client_message(mem, user, NICK, nick)
473
+ end
474
+ }
475
+ }
476
+ @old_nicks[nick].unshift user.to_a
477
+ user.nick = nick
478
+ end
479
+ end
480
+ when USER, PASS
481
+ puts_socket(user, ERR_ALREADYREGISTRED.new(config[:ServerName], "462", [user.nick, "Unauthorized command (already registered)"]))
482
+ when WHOIS
483
+ raise NotEnoughParameter if params.empty?
484
+ params[0].split(",").each{|mask|
485
+ reg = mask_to_regex(mask)
486
+ matched = @users.find_all{|usr| usr.nick =~ reg}
487
+ if matched.empty?
488
+ send_server_message(user, "401", mask, "No such nick/channel")
489
+ else
490
+ matched.each{|target|
491
+ sc = target.socket
492
+ send_server_message(user, "311", target.nick, target.user, target.host, "*", target.real)
493
+ send_server_message(user, "312", target.nick, config[:ServerName], "this server.")
494
+ send_server_message(user, "313", target.nick, "is an IRC operator") if @operators.include?(target)
495
+ chs = []
496
+ target.joined_channels.each{|ch|
497
+ if ch.visible?(user)
498
+ pr = ""
499
+ if ch.mode.op?(target)
500
+ pr = "@"
501
+ else ch.mode.voiced?(target)
502
+ pr = "+"
503
+ end
504
+ chs << pr+ch.name
505
+ end
506
+ }
507
+ send_server_message(user, "319", target.nick, chs.join(" "))
508
+ if target.away?
509
+ away = target.away
510
+ send_server_message(user, "301", target.nick, away)
511
+ end
512
+ send_server_message(user, "318", target.nick, "End of WHOIS list")
513
+ }
514
+ end
515
+ }
516
+ when WHOWAS
517
+ raise NotEnoughParameter if params.empty?
518
+ params[0].split(",").each{|nick|
519
+ if @users.any?{|us| us.nick == nick}
520
+ send_server_message(user, "312", nick, config[:ServerName], "this server.")
521
+ elsif @old_nicks[nick].empty?
522
+ send_server_message(user, "406", nick, "There was no such nickname")
523
+ else
524
+ @old_nicks[nick].each{|nms|
525
+ send_server_message(user, "314", *nms)
526
+ }
527
+ end
528
+ send_server_message(user, "369", nick, "End of WHOWAS")
529
+ }
530
+ when WHO
531
+ raise NotEnoughParameter if params.empty?
532
+ mask = mask_to_regex(params[0])
533
+ chs = @channels.find_all{|name, ch| name =~ mask && ch.visible?(user)}
534
+ unless chs.empty?
535
+ chs.each{|name, ch|
536
+ ch.members.each{|usr|
537
+ sym = usr.away? ? "G" : "H"
538
+ sym += @operators.include?(usr) ? "*" : ""
539
+ sym += ch.mode.op?(usr) ? "@" : (ch.mode.voiced?(usr) ? "+" : "")
540
+ send_server_message(user, "352", usr.name, usr.user, usr.host, config[:ServerName], usr.nick, sym, "0 #{real}")
541
+ }
542
+ send_server_message(user, "315", name, "End of WHO list")
543
+ }
544
+ else
545
+ usrs = @users.find_all{|us| us.nick =~ mask || us.user =~ mask || us.host =~ mask}
546
+ usrs.each{|usr|
547
+ send_server_message(user, "352", "*", usr.user, usr.host, config[:ServerName], usr.nick, "H", "0 #{usr.real}")
548
+ send_server_message(user, "315", usr.nick, "End of WHO list")
549
+ }
550
+ end
551
+ when KICK
552
+ raise NotEnoughParameter if params.size < 2
553
+ chs = params[0].split(",")
554
+ whos = params[1].split(",")
555
+ msg = params[2].to_s
556
+ if chs.size == 1
557
+ if ch = channel(chs[0])
558
+ whos.each{|who|
559
+ if usr = @users.find{|us|us.nick==who}
560
+ handle_reply user, ch.kick(usr, user, msg)
561
+ else
562
+ send_server_message(user, "401", who, "No such nick/channel")
563
+ end
564
+ }
565
+ else
566
+ send_server_message(user, "403", chs[0], "No such channel")
567
+ end
568
+ elsif chs.size == whos.size
569
+ chs.each_with_index{|chn, i|
570
+ if chn = channel(chn)
571
+ if usr = user_from_nick[whos[i]]
572
+ handle_reply user, ch.kick(usr, user, msg)
573
+ else
574
+ send_server_message(user, "401", whos[i], "No such nick/channel")
575
+ end
576
+ else
577
+ send_server_message(user, "403", chn, "No such channel")
578
+ end
579
+ }
580
+ end
581
+ when ISON
582
+ raise NotEnoughParameter if params.empty?
583
+ a = params.find_all{|nick| @users.any?{|us| us.nick == nick}}
584
+ send_server_message(user, "303", a.join(" "))
585
+ when USERHOST
586
+ raise NotEnoughParameter if params.empty?
587
+ targs = params[0..4]
588
+ targs.map!{|nick|
589
+ if tg = @users.find{|u| u.nick == nick}
590
+ suf = @operators.include?(tg.socket) ? "*" : ""
591
+ pre = tg.away? ? "-" : "+"
592
+ usr = tg.user
593
+ host = tg.host
594
+ nick + suf + "=" + pre + usr + "@" + host + " "
595
+ else
596
+ ""
597
+ end
598
+ }
599
+ send_server_message(user, "302", targs.join(" "))
600
+ when QUIT
601
+ send_client_message(user, nil, ERROR, "Closing", "Link:", "#{user.nick}[#{user.user}@#{user.host}]", %Q!("#{params[0]}")!)
602
+ unregister(user, params[0])
603
+ else
604
+ raise UnknownCommand.new(msg.command)
605
+ end
606
+ rescue NotEnoughParameter => e
607
+ send_server_message(user, "461", msg.command, "Not enough parameters")
608
+ rescue UnknownCommand => e
609
+ send_server_message(user, "421", e.cmd, "Unknown command")
610
+ rescue
611
+ puts $!, $@
612
+ end
613
+ end
614
+
615
+ def handle_reply(user, rpl)
616
+ return unless rpl
617
+ case rpl[0]
618
+ when Array
619
+ rpl.each{|rp|
620
+ handle_reply(user, rp)
621
+ }
622
+ when Command
623
+ send_client_message(user, user, *rpl)
624
+ else
625
+ send_server_message(user, *rpl)
626
+ end
627
+ end
628
+
629
+ def send_message(to, from, msg, *args)
630
+ case msg
631
+ when Command
632
+ send_client_message(to, from, msg, *args)
633
+ when Reply
634
+ send_server_message(to, msg, *args)
635
+ end
636
+ end
637
+
638
+ def puts_socket(user, *args)
639
+ user.socket.synchronize do
640
+ if user.socket.closed?
641
+ unregister(user, "Connection reset by peer")
642
+ else
643
+ begin
644
+ args.map!{|a| a.to_s.encode config().fetch(:Encoding, "ISO-2022-JP")}
645
+ user.socket.puts(*args)
646
+ rescue Errno::EPIPE => e
647
+ unregister(user, "Connection reset by peer")
648
+ end
649
+ end
650
+ end
651
+ end
652
+
653
+ def send_client_message(to, from, cmd, *args)
654
+ msg = cmd.new(from.to_s, cmd.name.split('::').last, args)
655
+ puts_socket(to, msg)
656
+ end
657
+
658
+ def send_server_message(to, msg, *args)
659
+ args.unshift to.nick
660
+ puts_socket(to, Message.build(config[:ServerName], msg, args) )
661
+ end
662
+
663
+ def channel(chname)
664
+ @channels[chname.downcase]
665
+ end
666
+
667
+ def set_channel(cn, val)
668
+ @channels[cn.downcase] = val
669
+ end
670
+
671
+ def start(*args)
672
+ @started = Time.now
673
+ super
674
+ end
675
+
676
+ end
677
+ end