hector 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ module Hector
2
+ class << self
3
+ attr_accessor :server_name
4
+
5
+ def start_server(address = "0.0.0.0", port = 6767, ssl_port = 6868)
6
+ EventMachine.start_server(address, port, Connection)
7
+ EventMachine.start_server(address, ssl_port, SSLConnection)
8
+ logger.info("Hector running on #{address}:#{port}")
9
+ logger.info("Secure Hector running on #{address}:#{ssl_port}")
10
+ end
11
+ end
12
+
13
+ self.server_name = "hector.irc"
14
+ end
@@ -0,0 +1,30 @@
1
+ module Hector
2
+ class Service < Session
3
+ protected
4
+ def deliver_message_from_origin(text)
5
+ deliver_message_from_session(origin, text)
6
+ end
7
+
8
+ def deliver_message_from_service(text)
9
+ deliver_message_from_session(self, text)
10
+ end
11
+
12
+ def deliver_message_from_session(session, text)
13
+ command, destination = response.command, find(response.args.first)
14
+ Hector.defer do
15
+ destination.deliver(command, session, :source => session.source, :text => text)
16
+ end
17
+ end
18
+
19
+ def intercept(pattern)
20
+ if response.text =~ pattern
21
+ yield *$~
22
+ throw :stop
23
+ end
24
+ end
25
+
26
+ def origin
27
+ find(response.source[/^([^!]+)!/, 1])
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,185 @@
1
+ module Hector
2
+ class Session
3
+ include Concerns::KeepAlive
4
+ include Concerns::Presence
5
+
6
+ include Commands::Join
7
+ include Commands::Mode
8
+ include Commands::Names
9
+ include Commands::Nick
10
+ include Commands::Notice
11
+ include Commands::Part
12
+ include Commands::Ping
13
+ include Commands::Pong
14
+ include Commands::Privmsg
15
+ include Commands::Quit
16
+ include Commands::Realname
17
+ include Commands::Topic
18
+ include Commands::Who
19
+ include Commands::Whois
20
+ include Commands::Away
21
+
22
+ attr_reader :nickname, :request, :response, :away_message
23
+
24
+ SESSIONS = {}
25
+
26
+ class << self
27
+ def all
28
+ sessions.values.grep(self)
29
+ end
30
+
31
+ def nicknames
32
+ sessions.keys
33
+ end
34
+
35
+ def find(nickname)
36
+ sessions[normalize(nickname)]
37
+ end
38
+
39
+ def rename(from, to)
40
+ if find(to)
41
+ raise NicknameInUse, to
42
+ else
43
+ find(from).tap do |session|
44
+ delete(from)
45
+ sessions[normalize(to)] = session
46
+ end
47
+ end
48
+ end
49
+
50
+ def delete(nickname)
51
+ sessions.delete(normalize(nickname))
52
+ end
53
+
54
+ def broadcast_to(sessions, command, *args)
55
+ except = args.last.delete(:except) if args.last.is_a?(Hash)
56
+ sessions.each do |session|
57
+ session.respond_with(command, *args) unless session == except
58
+ end
59
+ end
60
+
61
+ def normalize(nickname)
62
+ if nickname =~ /^\w[\w-]{0,15}$/u
63
+ nickname.downcase
64
+ else
65
+ raise ErroneousNickname, nickname
66
+ end
67
+ end
68
+
69
+ def register(session)
70
+ sessions[normalize(session.nickname)] = session
71
+ session
72
+ end
73
+
74
+ def reset!
75
+ sessions.clear
76
+ end
77
+
78
+ protected
79
+ def sessions
80
+ SESSIONS
81
+ end
82
+ end
83
+
84
+ def initialize(nickname)
85
+ @nickname = nickname
86
+ end
87
+
88
+ def broadcast(command, *args)
89
+ Session.broadcast_to(peer_sessions, command, *args)
90
+ end
91
+
92
+ def channel?
93
+ false
94
+ end
95
+
96
+ def deliver(message_type, session, options)
97
+ respond_with(message_type, nickname, options)
98
+ end
99
+
100
+ def destroy
101
+ self.class.delete(nickname)
102
+ end
103
+
104
+ def find(name)
105
+ destination_klass_for(name).find(name).tap do |destination|
106
+ raise NoSuchNickOrChannel, name unless destination
107
+ end
108
+ end
109
+
110
+ def hostname
111
+ Hector.server_name
112
+ end
113
+
114
+ def name
115
+ nickname
116
+ end
117
+
118
+ def realname
119
+ nickname
120
+ end
121
+
122
+ def receive(request)
123
+ @request = request
124
+ if respond_to?(@request.event_name)
125
+ send(@request.event_name)
126
+ end
127
+ ensure
128
+ @request = nil
129
+ end
130
+
131
+ def rename(new_nickname)
132
+ Session.rename(nickname, new_nickname)
133
+ @nickname = new_nickname
134
+ end
135
+
136
+ def respond_with(command, *args)
137
+ @response = command.is_a?(Response) ? command : Response.new(command, *preprocess_args(args))
138
+ if respond_to?(@response.event_name)
139
+ send(@response.event_name)
140
+ end
141
+ @response
142
+ ensure
143
+ @response = nil
144
+ end
145
+
146
+ def away(away_message)
147
+ @away_message = away_message
148
+ end
149
+
150
+ def away?
151
+ !@away_message.nil?
152
+ end
153
+
154
+ def back
155
+ @away_message = nil
156
+ end
157
+
158
+ def source
159
+ "#{nickname}!#{username}@#{hostname}"
160
+ end
161
+
162
+ def username
163
+ "~#{nickname}"
164
+ end
165
+
166
+ def who
167
+ "#{identity.username} #{Hector.server_name} #{Hector.server_name} #{nickname} H :0 #{realname}"
168
+ end
169
+
170
+ protected
171
+ def destination_klass_for(name)
172
+ name =~ /^#/ ? Channel : Session
173
+ end
174
+
175
+ def preprocess_args(args)
176
+ args.map do |arg|
177
+ if arg.is_a?(Symbol) && arg.to_s[0, 1] == "$"
178
+ send(arg.to_s[1..-1])
179
+ else
180
+ arg
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,38 @@
1
+ module Hector
2
+ class UserSession < Session
3
+ attr_reader :connection, :identity, :realname
4
+
5
+ class << self
6
+ def create(nickname, connection, identity, realname)
7
+ if find(nickname)
8
+ raise NicknameInUse, nickname
9
+ else
10
+ register UserSession.new(nickname, connection, identity, realname)
11
+ end
12
+ end
13
+ end
14
+
15
+ def initialize(nickname, connection, identity, realname)
16
+ super(nickname)
17
+ @connection = connection
18
+ @identity = identity
19
+ @realname = realname
20
+ initialize_keep_alive
21
+ initialize_presence
22
+ end
23
+
24
+ def destroy
25
+ super
26
+ destroy_presence
27
+ destroy_keep_alive
28
+ end
29
+
30
+ def respond_with(*)
31
+ connection.respond_with(super)
32
+ end
33
+
34
+ def username
35
+ identity.username
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Hector
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,56 @@
1
+ module Hector
2
+ # Identity adapters must implement the following public methods:
3
+ # - authenticate(username, password)
4
+ # - remember(username, password)
5
+ # - forget(username)
6
+ # - normalize(username)
7
+ #
8
+ class YamlIdentityAdapter
9
+ attr_reader :filename
10
+
11
+ def initialize(filename)
12
+ @filename = File.expand_path(filename)
13
+ end
14
+
15
+ def authenticate(username, password)
16
+ yield load_identities[normalize(username)] == hash(normalize(username), password)
17
+ end
18
+
19
+ def remember(username, password)
20
+ identities = load_identities
21
+ identities[normalize(username)] = hash(normalize(username), password)
22
+ store_identities(identities)
23
+ end
24
+
25
+ def forget(username)
26
+ identities = load_identities
27
+ identities.delete(normalize(username))
28
+ store_identities(identities)
29
+ end
30
+
31
+ def normalize(username)
32
+ username.strip.downcase
33
+ end
34
+
35
+ protected
36
+ def load_identities
37
+ ensure_file_exists
38
+ YAML.load_file(filename) || {}
39
+ end
40
+
41
+ def store_identities(identities)
42
+ File.open(filename, "w") do |file|
43
+ file.puts(identities.to_yaml)
44
+ end
45
+ end
46
+
47
+ def hash(username, password)
48
+ Digest::SHA1.hexdigest(Digest::SHA1.hexdigest(username) + password)
49
+ end
50
+
51
+ def ensure_file_exists
52
+ FileUtils.mkdir_p(File.dirname(filename))
53
+ FileUtils.touch(filename)
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hector
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Sam Stephenson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-04 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: eventmachine
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 59
30
+ segments:
31
+ - 0
32
+ - 12
33
+ - 10
34
+ version: 0.12.10
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: mocha
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 41
46
+ segments:
47
+ - 0
48
+ - 9
49
+ - 9
50
+ version: 0.9.9
51
+ type: :development
52
+ version_requirements: *id002
53
+ description: A private group chat server for people you trust. Implements a limited subset of the IRC protocol.
54
+ email:
55
+ - sstephenson@gmail.com
56
+ executables: []
57
+
58
+ extensions: []
59
+
60
+ extra_rdoc_files: []
61
+
62
+ files:
63
+ - lib/hector/boot.rb
64
+ - lib/hector/channel.rb
65
+ - lib/hector/commands/away.rb
66
+ - lib/hector/commands/join.rb
67
+ - lib/hector/commands/mode.rb
68
+ - lib/hector/commands/names.rb
69
+ - lib/hector/commands/nick.rb
70
+ - lib/hector/commands/notice.rb
71
+ - lib/hector/commands/part.rb
72
+ - lib/hector/commands/ping.rb
73
+ - lib/hector/commands/pong.rb
74
+ - lib/hector/commands/privmsg.rb
75
+ - lib/hector/commands/quit.rb
76
+ - lib/hector/commands/realname.rb
77
+ - lib/hector/commands/topic.rb
78
+ - lib/hector/commands/who.rb
79
+ - lib/hector/commands/whois.rb
80
+ - lib/hector/concerns/authentication.rb
81
+ - lib/hector/concerns/keep_alive.rb
82
+ - lib/hector/concerns/presence.rb
83
+ - lib/hector/connection.rb
84
+ - lib/hector/deference.rb
85
+ - lib/hector/errors.rb
86
+ - lib/hector/heartbeat.rb
87
+ - lib/hector/identity.rb
88
+ - lib/hector/logging.rb
89
+ - lib/hector/request.rb
90
+ - lib/hector/response.rb
91
+ - lib/hector/server.rb
92
+ - lib/hector/service.rb
93
+ - lib/hector/session.rb
94
+ - lib/hector/user_session.rb
95
+ - lib/hector/version.rb
96
+ - lib/hector/yaml_identity_adapter.rb
97
+ - lib/hector.rb
98
+ has_rdoc: true
99
+ homepage: http://github.com/sstephenson/hector
100
+ licenses: []
101
+
102
+ post_install_message:
103
+ rdoc_options: []
104
+
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 3
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ hash: 3
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ requirements: []
126
+
127
+ rubyforge_project:
128
+ rubygems_version: 1.3.7
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Private group chat server
132
+ test_files: []
133
+