autumn 3.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/AUTHORS +11 -0
  2. data/CHANGELOG +567 -0
  3. data/MANIFEST +110 -0
  4. data/README +1114 -0
  5. data/README.textile +1153 -0
  6. data/Rakefile +75 -0
  7. data/autumn.gemspec +44 -0
  8. data/bin/autumn +11 -0
  9. data/lib/autumn.rb +8 -0
  10. data/lib/autumn/authentication.rb +238 -0
  11. data/lib/autumn/channel_leaf.rb +107 -0
  12. data/lib/autumn/coder.rb +166 -0
  13. data/lib/autumn/console_boot.rb +10 -0
  14. data/lib/autumn/ctcp.rb +250 -0
  15. data/lib/autumn/daemon.rb +207 -0
  16. data/lib/autumn/datamapper_hacks.rb +290 -0
  17. data/lib/autumn/foliater.rb +231 -0
  18. data/lib/autumn/formatting.rb +236 -0
  19. data/lib/autumn/generator.rb +231 -0
  20. data/lib/autumn/genesis.rb +190 -0
  21. data/lib/autumn/inheritable_attributes.rb +162 -0
  22. data/lib/autumn/leaf.rb +738 -0
  23. data/lib/autumn/log_facade.rb +49 -0
  24. data/lib/autumn/misc.rb +87 -0
  25. data/lib/autumn/resources/daemons/Anothernet.yml +3 -0
  26. data/lib/autumn/resources/daemons/AustHex.yml +29 -0
  27. data/lib/autumn/resources/daemons/Bahamut.yml +67 -0
  28. data/lib/autumn/resources/daemons/Dancer.yml +3 -0
  29. data/lib/autumn/resources/daemons/GameSurge.yml +3 -0
  30. data/lib/autumn/resources/daemons/IRCnet.yml +3 -0
  31. data/lib/autumn/resources/daemons/Ithildin.yml +7 -0
  32. data/lib/autumn/resources/daemons/KineIRCd.yml +56 -0
  33. data/lib/autumn/resources/daemons/PTlink.yml +6 -0
  34. data/lib/autumn/resources/daemons/QuakeNet.yml +20 -0
  35. data/lib/autumn/resources/daemons/RFC1459.yml +158 -0
  36. data/lib/autumn/resources/daemons/RFC2811.yml +16 -0
  37. data/lib/autumn/resources/daemons/RFC2812.yml +36 -0
  38. data/lib/autumn/resources/daemons/RatBox.yml +25 -0
  39. data/lib/autumn/resources/daemons/Ultimate.yml +24 -0
  40. data/lib/autumn/resources/daemons/Undernet.yml +6 -0
  41. data/lib/autumn/resources/daemons/Unreal.yml +110 -0
  42. data/lib/autumn/resources/daemons/_Other.yml +7 -0
  43. data/lib/autumn/resources/daemons/aircd.yml +33 -0
  44. data/lib/autumn/resources/daemons/bdq-ircd.yml +3 -0
  45. data/lib/autumn/resources/daemons/hybrid.yml +38 -0
  46. data/lib/autumn/resources/daemons/ircu.yml +67 -0
  47. data/lib/autumn/resources/daemons/tr-ircd.yml +8 -0
  48. data/lib/autumn/script.rb +74 -0
  49. data/lib/autumn/speciator.rb +165 -0
  50. data/lib/autumn/stem.rb +919 -0
  51. data/lib/autumn/stem_facade.rb +176 -0
  52. data/lib/autumn/tool/bin.rb +301 -0
  53. data/lib/autumn/tool/create.rb +48 -0
  54. data/lib/autumn/tool/project_creator.rb +110 -0
  55. data/lib/autumn/version.rb +3 -0
  56. data/lib/skel/Rakefile +163 -0
  57. data/lib/skel/config/global.yml +2 -0
  58. data/lib/skel/config/seasons/testing/database.yml +4 -0
  59. data/lib/skel/config/seasons/testing/leaves.yml +9 -0
  60. data/lib/skel/config/seasons/testing/season.yml +2 -0
  61. data/lib/skel/config/seasons/testing/stems.yml +10 -0
  62. data/lib/skel/leaves/administrator/README +20 -0
  63. data/lib/skel/leaves/administrator/controller.rb +67 -0
  64. data/lib/skel/leaves/administrator/views/autumn.txt.erb +1 -0
  65. data/lib/skel/leaves/administrator/views/reload.txt.erb +11 -0
  66. data/lib/skel/leaves/insulter/README +17 -0
  67. data/lib/skel/leaves/insulter/controller.rb +65 -0
  68. data/lib/skel/leaves/insulter/views/about.txt.erb +1 -0
  69. data/lib/skel/leaves/insulter/views/help.txt.erb +1 -0
  70. data/lib/skel/leaves/insulter/views/insult.txt.erb +1 -0
  71. data/lib/skel/leaves/scorekeeper/README +34 -0
  72. data/lib/skel/leaves/scorekeeper/config.yml +2 -0
  73. data/lib/skel/leaves/scorekeeper/controller.rb +104 -0
  74. data/lib/skel/leaves/scorekeeper/helpers/general.rb +64 -0
  75. data/lib/skel/leaves/scorekeeper/models/channel.rb +12 -0
  76. data/lib/skel/leaves/scorekeeper/models/person.rb +14 -0
  77. data/lib/skel/leaves/scorekeeper/models/pseudonym.rb +11 -0
  78. data/lib/skel/leaves/scorekeeper/models/score.rb +14 -0
  79. data/lib/skel/leaves/scorekeeper/tasks/stats.rake +17 -0
  80. data/lib/skel/leaves/scorekeeper/views/about.txt.erb +1 -0
  81. data/lib/skel/leaves/scorekeeper/views/change.txt.erb +5 -0
  82. data/lib/skel/leaves/scorekeeper/views/history.txt.erb +11 -0
  83. data/lib/skel/leaves/scorekeeper/views/points.txt.erb +5 -0
  84. data/lib/skel/leaves/scorekeeper/views/usage.txt.erb +1 -0
  85. data/lib/skel/log/README +1 -0
  86. data/lib/skel/script/console +28 -0
  87. data/lib/skel/script/destroy +48 -0
  88. data/lib/skel/script/generate +48 -0
  89. data/lib/skel/shared/README +1 -0
  90. data/lib/skel/tmp/README +1 -0
  91. data/spec/authentication_spec.rb +328 -0
  92. data/spec/channel_leaf_spec.rb +142 -0
  93. data/spec/coder_spec.rb +146 -0
  94. data/spec/ctcp_spec.rb +222 -0
  95. data/spec/daemon_spec.rb +202 -0
  96. data/spec/datamapper_hacks_spec.rb +164 -0
  97. data/tasks/authors.rake +30 -0
  98. data/tasks/changelog.rake +18 -0
  99. data/tasks/copyright.rake +21 -0
  100. data/tasks/doc.rake +7 -0
  101. data/tasks/gem.rake +23 -0
  102. data/tasks/gem_installer.rake +76 -0
  103. data/tasks/install_dependencies.rake +6 -0
  104. data/tasks/manifest.rake +4 -0
  105. data/tasks/rcov.rake +23 -0
  106. data/tasks/release.rake +52 -0
  107. data/tasks/reversion.rake +8 -0
  108. data/tasks/setup.rake +24 -0
  109. data/tasks/spec.rake +7 -0
  110. data/tasks/yard.rake +4 -0
  111. metadata +188 -0
@@ -0,0 +1,75 @@
1
+ begin; require 'rubygems'; rescue LoadError; end
2
+
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/gempackagetask'
6
+ require 'time'
7
+ require 'date'
8
+
9
+ PROJECT_SPECS = FileList[
10
+ 'spec/*.rb',
11
+ 'lib/skel/spec/*.rb'
12
+ ]
13
+
14
+ PROJECT_MODULE = 'Autumn'
15
+ PROJECT_README = 'README.markdown'
16
+ #PROJECT_RUBYFORGE_GROUP_ID = 3034
17
+ PROJECT_COPYRIGHT = [
18
+ "# Copyright (c) #{Time.now.year} Tim Morgan riscfuture@gmail.com",
19
+ "# Distributed under the same terms as the Ruby license.",
20
+ "# Portions of this code are copyright ©2004 David Heinemeier Hansson; ",
21
+ "# please see libs/inheritable_attributes.rb for more information.",
22
+ "# Autumn's package tasks are copyright 2009 Michael Fellinger m.fellinger@gmail.com;",
23
+ "# Distributed under the terms of the Ramaze license"
24
+ ]
25
+
26
+ # To release the monthly version do:
27
+ # $ PROJECT_VERSION=2009.03 rake release
28
+ IGNORE_FILES = [/\.gitignore/]
29
+
30
+ GEMSPEC = Gem::Specification.new{|s|
31
+ s.name = 'autumn'
32
+ s.author = "Tim 'riscfuture' Morgan"
33
+ s.summary = "Autumn is a simple and modular irc framework"
34
+ s.description = s.summary
35
+ s.email = 'bougy.man@gmail.com'
36
+ s.homepage = 'http://github.com/bougyman/autumn'
37
+ s.platform = Gem::Platform::RUBY
38
+ s.version = (ENV['PROJECT_VERSION'] || Date.today.strftime("%Y.%m.%d"))
39
+ s.files = `git ls-files`.split("\n").sort.reject { |f| IGNORE_FILES.detect { |exp| f.match(exp) } }
40
+ s.has_rdoc = true
41
+ s.require_path = 'lib'
42
+ s.bindir = "bin"
43
+ s.executables = ["autumn"]
44
+ s.rubyforge_project = "pastr"
45
+
46
+ s.add_dependency('facets')
47
+ s.add_dependency('anise')
48
+
49
+ s.post_install_message = <<MESSAGE.strip
50
+ ============================================================
51
+
52
+ Thank you for installing Autumn!
53
+ You can now create a new bot:
54
+ # autumn create yourbot
55
+
56
+ ============================================================
57
+ MESSAGE
58
+ }
59
+
60
+ Dir['tasks/*.rake'].each{|f| import(f) }
61
+
62
+ task :default => [:spec]
63
+
64
+ CLEAN.include %w[
65
+ **/.*.sw?
66
+ *.gem
67
+ .config
68
+ **/*~
69
+ **/{data.db,cache.yaml}
70
+ *.yaml
71
+ pkg
72
+ rdoc
73
+ ydoc
74
+ *coverage*
75
+ ]
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{autumn}
5
+ s.version = "3.1.8"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Tim 'riscfuture' Morgan"]
9
+ s.date = %q{2009-05-03}
10
+ s.default_executable = %q{autumn}
11
+ s.description = %q{Autumn is a simple and modular irc framework}
12
+ s.email = %q{bougy.man@gmail.com}
13
+ s.executables = ["autumn"]
14
+ s.files = ["AUTHORS", "CHANGELOG", "MANIFEST", "README", "README.textile", "Rakefile", "autumn.gemspec", "bin/autumn", "lib/autumn.rb", "lib/autumn/authentication.rb", "lib/autumn/channel_leaf.rb", "lib/autumn/coder.rb", "lib/autumn/console_boot.rb", "lib/autumn/ctcp.rb", "lib/autumn/daemon.rb", "lib/autumn/datamapper_hacks.rb", "lib/autumn/foliater.rb", "lib/autumn/formatting.rb", "lib/autumn/generator.rb", "lib/autumn/genesis.rb", "lib/autumn/inheritable_attributes.rb", "lib/autumn/leaf.rb", "lib/autumn/log_facade.rb", "lib/autumn/misc.rb", "lib/autumn/resources/daemons/Anothernet.yml", "lib/autumn/resources/daemons/AustHex.yml", "lib/autumn/resources/daemons/Bahamut.yml", "lib/autumn/resources/daemons/Dancer.yml", "lib/autumn/resources/daemons/GameSurge.yml", "lib/autumn/resources/daemons/IRCnet.yml", "lib/autumn/resources/daemons/Ithildin.yml", "lib/autumn/resources/daemons/KineIRCd.yml", "lib/autumn/resources/daemons/PTlink.yml", "lib/autumn/resources/daemons/QuakeNet.yml", "lib/autumn/resources/daemons/RFC1459.yml", "lib/autumn/resources/daemons/RFC2811.yml", "lib/autumn/resources/daemons/RFC2812.yml", "lib/autumn/resources/daemons/RatBox.yml", "lib/autumn/resources/daemons/Ultimate.yml", "lib/autumn/resources/daemons/Undernet.yml", "lib/autumn/resources/daemons/Unreal.yml", "lib/autumn/resources/daemons/_Other.yml", "lib/autumn/resources/daemons/aircd.yml", "lib/autumn/resources/daemons/bdq-ircd.yml", "lib/autumn/resources/daemons/hybrid.yml", "lib/autumn/resources/daemons/ircu.yml", "lib/autumn/resources/daemons/tr-ircd.yml", "lib/autumn/script.rb", "lib/autumn/speciator.rb", "lib/autumn/stem.rb", "lib/autumn/stem_facade.rb", "lib/autumn/tool/bin.rb", "lib/autumn/tool/create.rb", "lib/autumn/tool/project_creator.rb", "lib/autumn/version.rb", "lib/skel/Rakefile", "lib/skel/config/global.yml", "lib/skel/config/seasons/testing/database.yml", "lib/skel/config/seasons/testing/leaves.yml", "lib/skel/config/seasons/testing/season.yml", "lib/skel/config/seasons/testing/stems.yml", "lib/skel/leaves/administrator/README", "lib/skel/leaves/administrator/controller.rb", "lib/skel/leaves/administrator/views/autumn.txt.erb", "lib/skel/leaves/administrator/views/reload.txt.erb", "lib/skel/leaves/insulter/README", "lib/skel/leaves/insulter/controller.rb", "lib/skel/leaves/insulter/views/about.txt.erb", "lib/skel/leaves/insulter/views/help.txt.erb", "lib/skel/leaves/insulter/views/insult.txt.erb", "lib/skel/leaves/scorekeeper/README", "lib/skel/leaves/scorekeeper/config.yml", "lib/skel/leaves/scorekeeper/controller.rb", "lib/skel/leaves/scorekeeper/helpers/general.rb", "lib/skel/leaves/scorekeeper/models/channel.rb", "lib/skel/leaves/scorekeeper/models/person.rb", "lib/skel/leaves/scorekeeper/models/pseudonym.rb", "lib/skel/leaves/scorekeeper/models/score.rb", "lib/skel/leaves/scorekeeper/tasks/stats.rake", "lib/skel/leaves/scorekeeper/views/about.txt.erb", "lib/skel/leaves/scorekeeper/views/change.txt.erb", "lib/skel/leaves/scorekeeper/views/history.txt.erb", "lib/skel/leaves/scorekeeper/views/points.txt.erb", "lib/skel/leaves/scorekeeper/views/usage.txt.erb", "lib/skel/log/README", "lib/skel/script/console", "lib/skel/script/destroy", "lib/skel/script/generate", "lib/skel/shared/README", "lib/skel/tmp/README", "spec/authentication_spec.rb", "spec/channel_leaf_spec.rb", "spec/coder_spec.rb", "spec/ctcp_spec.rb", "spec/daemon_spec.rb", "spec/datamapper_hacks_spec.rb", "tasks/authors.rake", "tasks/changelog.rake", "tasks/copyright.rake", "tasks/doc.rake", "tasks/gem.rake", "tasks/gem_installer.rake", "tasks/install_dependencies.rake", "tasks/manifest.rake", "tasks/rcov.rake", "tasks/release.rake", "tasks/reversion.rake", "tasks/setup.rake", "tasks/spec.rake", "tasks/yard.rake"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://github.com/bougyman/autumn}
17
+ s.post_install_message = %q{============================================================
18
+
19
+ Thank you for installing Autumn!
20
+ You can now create a new bot:
21
+ # autumn create yourbot
22
+
23
+ ============================================================}
24
+ s.require_paths = ["lib"]
25
+ s.rubyforge_project = %q{pastr}
26
+ s.rubygems_version = %q{1.3.1}
27
+ s.summary = %q{Autumn is a simple and modular irc framework}
28
+
29
+ if s.respond_to? :specification_version then
30
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
31
+ s.specification_version = 2
32
+
33
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
34
+ s.add_runtime_dependency(%q<facets>, [">= 0"])
35
+ s.add_runtime_dependency(%q<anise>, [">= 0"])
36
+ else
37
+ s.add_dependency(%q<facets>, [">= 0"])
38
+ s.add_dependency(%q<anise>, [">= 0"])
39
+ end
40
+ else
41
+ s.add_dependency(%q<facets>, [">= 0"])
42
+ s.add_dependency(%q<anise>, [">= 0"])
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ require "pathname"
4
+ require Pathname.new(__FILE__).expand_path.dirname.join("..", "lib", "autumn")
5
+ require "autumn/tool/bin"
6
+ rescue LoadError
7
+ require "rubygems"
8
+ require "autumn"
9
+ require "autumn/tool/bin"
10
+ end
11
+ Autumn::Tool::Bin::Cmd.run(ARGV)
@@ -0,0 +1,8 @@
1
+ require "pathname"
2
+ module Autumn
3
+ unless Autumn.const_defined?("ROOT")
4
+ ROOT = Pathname.new(__FILE__).dirname.expand_path
5
+ end
6
+ end
7
+ $LOAD_PATH.unshift Autumn::ROOT
8
+ require "autumn/version"
@@ -0,0 +1,238 @@
1
+ # Defines the Autumn::Authentication class, which includes different
2
+ # authentication strategies available to leaves.
3
+
4
+ module Autumn
5
+
6
+ # Defines classes which each encapsulate a different strategy for
7
+ # authentication. When the +authentication+ option is specified (see the
8
+ # Autumn::Leaf class), the options given are used to choose the correct class
9
+ # within this module to serve as the authenticator for that leaf.
10
+ #
11
+ # These authentication strategies are used to ensure only authorized users
12
+ # have access to protected commands. Leaf authors can designate certain
13
+ # commands as protected.
14
+ #
15
+ # = Writing Your Own Authenticators
16
+ #
17
+ # When the Autumn::Leaf#authenticate method is called, it converts the symbol
18
+ # from snake_case to CamelCase, and looks for a class in this model. Thus, a
19
+ # call to <tt>authenticate :hostname</tt> would look for a class
20
+ # Autumn::Authentication::Hostname.
21
+ #
22
+ # To define your own authenticator, subclass Autumn::Authentication::Base as a
23
+ # new class in the Autumn::Authentication module. Implement the methods
24
+ # defined in the Autumn::Authentication::Base class docs, then adjust your
25
+ # configuration to use your new authenticator.
26
+
27
+ module Authentication
28
+
29
+ # The basic subclass for all authenticators. If you wish to write your own
30
+ # authenticator, you must subclass this class. You must at a minimum
31
+ # override the authenticate method. You should also override the initialize
32
+ # method if you need to store any options or other data for later use.
33
+ #
34
+ # The authentication module will become a stem listener, so see
35
+ # Autumn::Stem#add_listener for information on other methods you can
36
+ # implement.
37
+
38
+ class Base
39
+
40
+ # Stores the options for this authenticator and configures it for use.
41
+
42
+ def initialize(options={})
43
+ raise "You can only instantiate subclasses of this class."
44
+ end
45
+
46
+ # Returns true if the user is authorized, false if not. +sender+ is a
47
+ # sender hash as defined in the Autumn::Stem docs.
48
+
49
+ def authenticate(stem, channel, sender, leaf)
50
+ raise "Subclasses must override the Autumn::Authentication::Base#authenticate method."
51
+ end
52
+
53
+ # Returns a string to be displayed to a user who is not authorized to
54
+ # perform a command. Override this method to provide more specific hints
55
+ # to a user on what he can do to authorize himself (e.g., "Tell me your
56
+ # password").
57
+
58
+ def unauthorized
59
+ "You must be an administrator for this bot to do that."
60
+ end
61
+ end
62
+
63
+ # Authenticates users by their privilege level in the channel they ran the
64
+ # command in.
65
+ #
66
+ # This is a quick, configuration-free way of protecting your leaf, so long
67
+ # as you trust the ops in your channel.
68
+
69
+ class Op < Base
70
+
71
+ # Creates a new authenticator. Pass a list of allowed privileges (as
72
+ # symbols) for the +privileges+ option. By default this class accepts ops,
73
+ # admins, and channel owners/founders as authorized.
74
+
75
+ def initialize(options={})
76
+ @privileges = options[:privileges]
77
+ @privileges ||= [ :operator, :oper, :op, :admin, :founder, :channel_owner ]
78
+ end
79
+
80
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
81
+ # Returns true if the sender has any of the privileges listed below
82
+ not (@privileges & [ stem.privilege(channel, sender) ].flatten).empty?
83
+ end
84
+
85
+ def unauthorized # :nodoc:
86
+ "You must be an op to do that."
87
+ end
88
+ end
89
+
90
+ # Authenticates by IRC nick. A list of allowed nicks is built on
91
+ # initialization, and anyone with that nick is allowed to use restricted
92
+ # commands.
93
+ #
94
+ # This is the most obvious approach to authentication, but it is hardly
95
+ # secure. Anyone can change their nick to an admin's nick once that admin
96
+ # logs out.
97
+
98
+ class Nick < Base
99
+
100
+ # Creates a new authenticator. Pass a single nick for the +nick+ option or
101
+ # an array of allowed nicks for the +nicks+ option. If neither option is
102
+ # set, an exception is raised.
103
+
104
+ def initialize(options={})
105
+ @nicks = options[:nick]
106
+ @nicks ||= options[:nicks]
107
+ raise "You must give the nick of an administrator to use nick-based authentication." if @nicks.nil?
108
+ @nicks = [ @nicks ] if @nicks.kind_of? String
109
+ end
110
+
111
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
112
+ @nicks.include? sender[:nick]
113
+ end
114
+ end
115
+
116
+ # Authenticates by the host portion of an IRC message. A hostmask is used to
117
+ # match the relevant portion of the address with a whitelist of accepted
118
+ # host addresses.
119
+ #
120
+ # This method can be a secure way of preventing unauthorized access if you
121
+ # choose an appropriately narrow hostmask. However, you must configure in
122
+ # advance the computers you may want to administrate your leaves from.
123
+
124
+ class Hostname < Base
125
+
126
+ # Creates a new authenticator. You provide a hostmask via the +hostmask+
127
+ # option -- either a Regexp with one capture (that captures the portion of
128
+ # the hostmask you are interested in), or a Proc, which takes a host as an
129
+ # argument and returns true if the host is authorized, false if not. If
130
+ # the +hostmask+ option is not provided, a standard hostmask regexp will
131
+ # be used. This regexp strips everything left of the first period; for the
132
+ # example hostmask "wsd1.ca.widgetcom.net", it would return
133
+ # "ca.widgetcom.net" to be used for comparison.
134
+ #
135
+ # You also provide an authorized host with the +host+ option, or a list of
136
+ # such hosts with the +hosts+ option. If neither is given, an exception is
137
+ # raised.
138
+
139
+ def initialize(options={})
140
+ @hostmask = options[:hostmask]
141
+ @hostmask ||= /^.+?\.(.+)$/
142
+ @hostmask = @hostmask.to_rx(false) if @hostmask.kind_of? String
143
+ if @hostmask.kind_of? Regexp then
144
+ mask = @hostmask
145
+ @hostmask = lambda do |host|
146
+ if matches = host.match(mask) then matches[1] else nil end
147
+ end
148
+ end
149
+
150
+ @hosts = options[:host]
151
+ @hosts ||= options[:hosts]
152
+ raise "You must give the host address of an administrator to use nick-based authentication." unless @hosts
153
+ @hosts = [ @hosts ] unless @hosts.kind_of? Array
154
+ end
155
+
156
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
157
+ @hosts.include? @hostmask.call(sender[:host])
158
+ end
159
+ end
160
+
161
+ # Authenticates by a password provided in secret. When a user PRIVMSG's the
162
+ # leaf the correct password, the leaf adds that user's nick to a list of
163
+ # authorized nicks. These credentials expire when the person changes his
164
+ # nick, logs out, leaves the channel, etc. They also expire if a certain
165
+ # amount of time passes without running any protected commands.
166
+
167
+ class Password < Base
168
+ # The default period of time that must occur with no use of protected
169
+ # commands after which a user's credentials expire.
170
+ DEFAULT_EXPIRE_TIME = 5*60
171
+
172
+ # Creates a new authenticator. You provide a valid password with the
173
+ # +password+ option. If that option is not provided, an exception is
174
+ # raised. You can pass a number of seconds to the +expire_time+ option;
175
+ # this is the amount of time that must pass with no protected commands for
176
+ # a nick's authorization to expire. If the +expire_time+ option is not
177
+ # given, a default value of five minutes is used.
178
+
179
+ def initialize(options={})
180
+ @password = options[:password]
181
+ @expire_time = options[:expire_time]
182
+ @expire_time ||= DEFAULT_EXPIRE_TIME
183
+ raise "You must provide a password to use password-based authentication" unless @password
184
+ @authorized_nicks = Hash.new { |hsh, key| hsh[key] = Set.new }
185
+ @last_protected_action = Hash.new { |hsh, key| hsh[key] = Hash.new(Time.at(0)) }
186
+ @an_lock = Mutex.new
187
+ end
188
+
189
+ def irc_privmsg_event(stem, sender, arguments) # :nodoc:
190
+ if arguments[:recipient] and arguments[:message] == @password then
191
+ @an_lock.synchronize do
192
+ @authorized_nicks[stem] << sender[:nick]
193
+ @last_protected_action[stem][sender[:nick]] = Time.now
194
+ #TODO values are not always deleted; this hash has the possibility to slowly grow and consume more memory
195
+ end
196
+ stem.message "Your password has been accepted, and you are now authorized.", sender[:nick]
197
+ end
198
+ end
199
+
200
+ def irc_nick_event(stem, sender, arguments) # :nodoc:
201
+ @an_lock.synchronize do
202
+ revoke stem, sender[:nick]
203
+ revoke stem, arguments[:nick]
204
+ end
205
+ end
206
+
207
+ def irc_kick_event(stem, sender, arguments) # :nodoc:
208
+ @an_lock.synchronize { revoke stem, arguments[:nick] }
209
+ end
210
+
211
+ def irc_quit_event(stem, sender, arguments) # :nodoc:
212
+ @an_lock.synchronize { revoke stem, sender[:nick] }
213
+ end
214
+
215
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
216
+ @an_lock.synchronize do
217
+ if Time.now - @last_protected_action[stem][sender[:nick]] > @expire_time then
218
+ revoke stem, sender[:nick]
219
+ else
220
+ @last_protected_action[stem][sender[:nick]] = Time.now
221
+ end
222
+ @authorized_nicks[stem].include? sender[:nick]
223
+ end
224
+ end
225
+
226
+ def unauthorized # :nodoc:
227
+ "You must authenticate with an administrator password to do that."
228
+ end
229
+
230
+ private
231
+
232
+ def revoke(stem, nick)
233
+ @authorized_nicks[stem].delete nick
234
+ @last_protected_action[stem].delete nick
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,107 @@
1
+ # Defines the Autumn::ChannelLeaf class, a subclass of Autumn::Leaf that
2
+ # selectively ignores channels.
3
+
4
+ module Autumn
5
+
6
+ # A special kind of leaf that only responds to messages sent to certain
7
+ # channels. Leaves that subclass ChannelLeaf can, in their config, specify a
8
+ # +channels+ option that narrows down which channels the leaf listens to. The
9
+ # leaf will not invoke the hook methods nor the <tt>*_command</tt> methods for
10
+ # IRC events that are not associated with those channels. It will respond to
11
+ # global, non-channel-specific events as well.
12
+ #
13
+ # You can combine multiple ChannelLeaf subclasses in one Stem to allow you to
14
+ # run two leaves off of one nick, but have the nick running different leaves
15
+ # in different channels.
16
+ #
17
+ # The +channels+ option should be a list of stems, and for each stem, a valid
18
+ # channel. For example, if you ran your leaf on two servers, your stems.yml
19
+ # file might look like:
20
+ #
21
+ # GamingServer:
22
+ # channels: fishinggames, games, drivinggames
23
+ # [...]
24
+ # FishingServer:
25
+ # channels: fishinggames, flyfishing
26
+ # [...]
27
+ #
28
+ # Now let's say you had a trivia leaf that asked questions about fishing
29
+ # games. You'd want to run that leaf on the "#fishinggames" channel of each
30
+ # server, and the "#games" channel of the GamingServer, but not the other
31
+ # channels. (Perhaps your Stem was also running other leaves relevant to those
32
+ # channels.) You'd set up your leaves.yml file like so:
33
+ #
34
+ # FishingGamesTrivia:
35
+ # channels:
36
+ # GamingServer:
37
+ # - fishinggames
38
+ # - games
39
+ # FishingServer: fishinggames
40
+ # [...]
41
+ #
42
+ # Now your leaf will only respond to messages relevant to the specified server
43
+ # channels (as well as global messages).
44
+ #
45
+ # Interception and filtering of messages is done at the _leaf_ level, not the
46
+ # _stem_ level. Therefore, for instance, if you override
47
+ # +someone_did_join_channel+, it will only be called for the appropriate
48
+ # channels; however, if you implement +irc_join_event+, it will still be
49
+ # called for all channels the stem is in.
50
+
51
+ class ChannelLeaf < Leaf
52
+ # The IRC channels that this leaf is responding to, mapped to server names.
53
+ attr :channels
54
+
55
+ # Creates a new instance. (See the Leaf class for more information.)
56
+
57
+ def will_start_up
58
+ @channels = Hash.new
59
+ @options[:channels] ||= Hash.new
60
+ @options[:channels].each do |server, chans|
61
+ stem = Foliater.instance.stems[server]
62
+ raise "Unknown stem #{server}" unless stem
63
+ chans = [ chans ] if chans.kind_of? String
64
+ @channels[stem] = chans.map { |chan| stem.normalized_channel_name chan }
65
+ end
66
+ super
67
+ end
68
+
69
+ def irc_privmsg_event(stem, sender, arguments) # :nodoc:
70
+ super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
71
+ end
72
+
73
+ def irc_join_event(stem, sender, arguments) # :nodoc:
74
+ super if listening?(stem, arguments[:channel])
75
+ end
76
+
77
+ def irc_part_event(stem, sender, arguments) # :nodoc:
78
+ super if listening?(stem, arguments[:channel])
79
+ end
80
+
81
+ def irc_mode_event(stem, sender, arguments) # :nodoc:
82
+ super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
83
+ end
84
+
85
+ def irc_topic_event(stem, sender, arguments) # :nodoc:
86
+ super if listening?(stem, arguments[:channel])
87
+ end
88
+
89
+ def irc_invite_event(stem, sender, arguments) # :nodoc:
90
+ super if listening?(stem, arguments[:channel]) or not stem.channels.include? arguments[:channel]
91
+ end
92
+
93
+ def irc_kick_event(stem, sender, arguments) # :nodoc:
94
+ super if listening?(stem, arguments[:channel])
95
+ end
96
+
97
+ def irc_notice_event(stem, sender, arguments) # :nodoc:
98
+ super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
99
+ end
100
+
101
+ private
102
+
103
+ def listening?(stem, channel)
104
+ @channels.include? stem and @channels[stem].include? channel
105
+ end
106
+ end
107
+ end