sugoi-mail 0.0.0
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.
- data/README +35 -0
- data/Rakefile +10 -0
- data/app/apis/mailservice_api.rb +178 -0
- data/app/controllers/.sugoi_admin_controller.rb.swp +0 -0
- data/app/controllers/account_controller.rb +39 -0
- data/app/controllers/address_controller.rb +49 -0
- data/app/controllers/application.rb +4 -0
- data/app/controllers/commandline_controller.rb +15 -0
- data/app/controllers/domain_controller.rb +37 -0
- data/app/controllers/mailinglist_controller.rb +51 -0
- data/app/controllers/mailservice_controller.rb +497 -0
- data/app/controllers/sugoi_admin_controller.rb +93 -0
- data/app/helpers/account_helper.rb +2 -0
- data/app/helpers/address_helper.rb +2 -0
- data/app/helpers/application_helper.rb +3 -0
- data/app/helpers/domain_helper.rb +2 -0
- data/app/helpers/mailservice_helper.rb +2 -0
- data/app/helpers/sugoi_admin_helper.rb +2 -0
- data/app/models/address.rb +110 -0
- data/app/models/admin_message.rb +45 -0
- data/app/models/confirmationcode.rb +59 -0
- data/app/models/domain.rb +76 -0
- data/app/models/mailinglist.rb +336 -0
- data/app/models/mailinglist_class.rb +39 -0
- data/app/models/message.rb +293 -0
- data/app/models/proxy_link.rb +25 -0
- data/app/models/user.rb +150 -0
- data/app/views/account/login.rhtml +22 -0
- data/app/views/account/logout.rhtml +10 -0
- data/app/views/account/signup.rhtml +17 -0
- data/app/views/account/welcome.rhtml +13 -0
- data/app/views/address/_form.rhtml +7 -0
- data/app/views/address/edit.rhtml +10 -0
- data/app/views/address/list.rhtml +27 -0
- data/app/views/address/new.rhtml +8 -0
- data/app/views/address/show.rhtml +8 -0
- data/app/views/domain/login.rhtml +22 -0
- data/app/views/domain/logout.rhtml +10 -0
- data/app/views/domain/signup.rhtml +17 -0
- data/app/views/domain/welcome.rhtml +13 -0
- data/app/views/layouts/address.rhtml +13 -0
- data/app/views/layouts/scaffold.rhtml +13 -0
- data/app/views/mailinglist/_form.rhtml +28 -0
- data/app/views/mailinglist/edit.rhtml +10 -0
- data/app/views/mailinglist/list.rhtml +27 -0
- data/app/views/mailinglist/new.rhtml +8 -0
- data/app/views/mailinglist/show.rhtml +16 -0
- data/app/views/sugoi_admin/create_domain.rhtml +1 -0
- data/app/views/sugoi_admin/list_addresses.rhtml +1 -0
- data/app/views/sugoi_admin/list_domains.rhtml +2 -0
- data/app/views/sugoi_admin/list_mailinglists.rhtml +1 -0
- data/bin/mailc +32 -0
- data/bin/maild +133 -0
- data/bin/sugoi-admin +21 -0
- data/bin/sugoi-mail +20 -0
- data/config/boot.rb +44 -0
- data/config/environment.rb +54 -0
- data/config/environments/bench.rb +21 -0
- data/config/environments/coverage.rb +21 -0
- data/config/environments/development.rb +21 -0
- data/config/environments/production.rb +18 -0
- data/config/environments/test.rb +19 -0
- data/config/lighttpd.conf +46 -0
- data/config/routes.rb +29 -0
- data/db/migrate/001_mailproxy.rb +7 -0
- data/db/migrate/002_create_users.rb +13 -0
- data/db/migrate/003_create_mailinglists.rb +13 -0
- data/db/migrate/004_create_addresses.rb +12 -0
- data/db/migrate/005_create_addresses_mailinglists.rb +13 -0
- data/db/migrate/006_alter_mailinglists.rb +9 -0
- data/db/migrate/007_create_messages.rb +25 -0
- data/db/migrate/008_add_mailinglistid_to_users.rb +14 -0
- data/db/migrate/009_add_domainadmin_to_users.rb +9 -0
- data/db/migrate/010_add_domain_to_users.rb +16 -0
- data/db/migrate/011_add_active_to_addresses.rb +14 -0
- data/db/migrate/012_create_confirmationcodes.rb +14 -0
- data/db/migrate/013_add_description_to_mailinglists.rb +9 -0
- data/db/migrate/014_create_admin_messages.rb +69 -0
- data/db/migrate/015_add_messages_to_mailinglists.rb +26 -0
- data/db/migrate/016_add_mailinglist_admin_to_users.rb +9 -0
- data/db/migrate/017_add_mailinglist_types.rb +94 -0
- data/db/migrate/018_add_bounciness_to_addresses.rb +20 -0
- data/db/migrate/019_add_archived_to_mailinglist_classes.rb +25 -0
- data/db/migrate/020_add_envelope_data_to_messages.rb +11 -0
- data/db/migrate/021_add_addresses_users.rb +14 -0
- data/db/migrate/022_add_virtual_to_users.rb +14 -0
- data/db/migrate/023_drop_openposting_from_mailinglists.rb +9 -0
- data/db/migrate/024_add_proxy_links.rb +21 -0
- data/db/migrate/025_add_proxify_to_mailinglist_classes.rb +25 -0
- data/db/schema.mysql.sql +104 -0
- data/db/schema.postgresql.sql +104 -0
- data/db/schema.rb +85 -0
- data/db/schema.sqlite.sql +104 -0
- data/db/schema.sqlserver.sql +113 -0
- data/db/schema_version +1 -0
- data/doc/README_FOR_APP +179 -0
- data/doc/mailinglist_classes description.txt +28 -0
- data/installer/rails_installer_defaults.yml +5 -0
- data/lib/daemonize.rb +56 -0
- data/lib/domain_system.rb +87 -0
- data/lib/gurgitate-rules.rb +69 -0
- data/lib/limitedfork.rb +66 -0
- data/lib/login_system.rb +87 -0
- data/public/.htaccess +40 -0
- data/public/404.html +8 -0
- data/public/500.html +8 -0
- data/public/dispatch.cgi +10 -0
- data/public/dispatch.fcgi +24 -0
- data/public/dispatch.rb +10 -0
- data/public/favicon.ico +0 -0
- data/public/images/rails.png +0 -0
- data/public/javascripts/application.js +2 -0
- data/public/javascripts/controls.js +815 -0
- data/public/javascripts/dragdrop.js +913 -0
- data/public/javascripts/effects.js +958 -0
- data/public/javascripts/prototype.js +2006 -0
- data/public/robots.txt +1 -0
- data/public/stylesheets/scaffold.css +74 -0
- data/public/stylesheets/trestle.css +74 -0
- data/script/about +3 -0
- data/script/breakpointer +3 -0
- data/script/console +3 -0
- data/script/destroy +3 -0
- data/script/fakedeliver +19 -0
- data/script/generate +3 -0
- data/script/performance/benchmarker +3 -0
- data/script/performance/profiler +3 -0
- data/script/plugin +3 -0
- data/script/process/reaper +3 -0
- data/script/process/spawner +3 -0
- data/script/runner +3 -0
- data/script/server +3 -0
- data/sugoi-mail.gemspec +36 -0
- data/test/fixtures/addresses.yml +70 -0
- data/test/fixtures/addresses_mailinglists.yml +20 -0
- data/test/fixtures/admin_messages.yml +65 -0
- data/test/fixtures/confirmationcodes.yml +13 -0
- data/test/fixtures/domains.yml +9 -0
- data/test/fixtures/mailinglist_classes.yml +45 -0
- data/test/fixtures/mailinglists.yml +80 -0
- data/test/fixtures/messages.yml +154 -0
- data/test/fixtures/proxy_links.yml +5 -0
- data/test/fixtures/users.yml +50 -0
- data/test/functional/domain_controller_test.rb +74 -0
- data/test/functional/mailservice_controller_test.rb +546 -0
- data/test/integration/test_soap.rb +413 -0
- data/test/integration/test_xmlrpc.rb +198 -0
- data/test/mocks/test/net-smtp-stub.rb +24 -0
- data/test/test_helper.rb +28 -0
- data/test/unit/address_test.rb +44 -0
- data/test/unit/admin_message_test.rb +41 -0
- data/test/unit/confirmationcode_test.rb +64 -0
- data/test/unit/domain_test.rb +128 -0
- data/test/unit/mailinglist_class_test.rb +82 -0
- data/test/unit/mailinglist_test.rb +145 -0
- data/test/unit/message_test.rb +151 -0
- data/test/unit/user_test.rb +126 -0
- data/test/units.rb +4 -0
- data/vendor/plugins/active_command/init.rb +1 -0
- data/vendor/plugins/active_command/lib/active_command/request.rb +137 -0
- data/vendor/plugins/active_command/lib/active_command/response.rb +132 -0
- data/vendor/plugins/active_command/lib/active_command.rb +2 -0
- data/vendor/plugins/ar_fixtures/CHANGELOG +10 -0
- data/vendor/plugins/ar_fixtures/MIT-LICENSE +20 -0
- data/vendor/plugins/ar_fixtures/README +19 -0
- data/vendor/plugins/ar_fixtures/Rakefile +54 -0
- data/vendor/plugins/ar_fixtures/about.yml +7 -0
- data/vendor/plugins/ar_fixtures/init.rb +1 -0
- data/vendor/plugins/ar_fixtures/lib/ar_fixtures.rb +102 -0
- data/vendor/plugins/ar_fixtures/tasks/ar_fixtures.rake +39 -0
- data/vendor/plugins/ar_fixtures/test/ar_fixtures_test.rb +53 -0
- data/vendor/plugins/ar_fixtures/test/database.yml +18 -0
- data/vendor/plugins/ar_fixtures/test/fixtures/beer.rb +5 -0
- data/vendor/plugins/ar_fixtures/test/fixtures/beers.yml +9 -0
- data/vendor/plugins/ar_fixtures/test/fixtures/beers_drunkards.yml +8 -0
- data/vendor/plugins/ar_fixtures/test/fixtures/drunkard.rb +6 -0
- data/vendor/plugins/ar_fixtures/test/fixtures/drunkards.yml +8 -0
- data/vendor/plugins/ar_fixtures/test/fixtures/glass.rb +2 -0
- data/vendor/plugins/ar_fixtures/test/fixtures/glasses.yml +9 -0
- data/vendor/plugins/ar_fixtures/test/schema.rb +21 -0
- data/vendor/plugins/ar_fixtures/test/test_helper.rb +37 -0
- metadata +320 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
require "date"
|
2
|
+
require "gurgitate/mailmessage"
|
3
|
+
require "net/smtp"
|
4
|
+
require "socket"
|
5
|
+
# require "pp"
|
6
|
+
|
7
|
+
# The Message class is one of the most important classes in
|
8
|
+
# sugoi-mail--mainly because it *does* so much. But anyway. Herewith a
|
9
|
+
# Model With Some Controller-like Things In It.
|
10
|
+
#
|
11
|
+
# This is the class that handles what a "message" is, and how it
|
12
|
+
# behaves. It makes considerable use of gurgitate-mail's mail-parsing
|
13
|
+
# library, so it might not be a bad idea to familiarize yourself with
|
14
|
+
# that if trying to work on this.
|
15
|
+
class Message < ActiveRecord::Base
|
16
|
+
belongs_to :mailinglist
|
17
|
+
belongs_to :address
|
18
|
+
acts_as_tree :order => "timestamp"
|
19
|
+
# has_and_belongs_to_many :mailinglists
|
20
|
+
|
21
|
+
class << self
|
22
|
+
|
23
|
+
private
|
24
|
+
def get_or_create_messageid mess
|
25
|
+
unless mess.headers["Message-Id"]
|
26
|
+
mess.headers["Message-Id"] = "<"+`uuidgen || uuid`.chomp +
|
27
|
+
"@" + Socket::gethostbyname("localhost")[0] + ">"
|
28
|
+
end
|
29
|
+
mess.headers["Message-Id"][0].contents
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_or_create_timestamp mess
|
33
|
+
if mess.headers["Date"] then
|
34
|
+
DateTime.parse(mess.headers["Date"][0].contents)
|
35
|
+
else
|
36
|
+
Time.now
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_parent mess
|
41
|
+
referenced=nil
|
42
|
+
if mess.headers["In-Reply-To"] then
|
43
|
+
referenced=mess.headers["In-Reply-To"][0].contents .
|
44
|
+
match(/(\<[^>]+\>)/)
|
45
|
+
if referenced then
|
46
|
+
referenced=referenced[1]
|
47
|
+
else
|
48
|
+
if mess.headers["References"] then
|
49
|
+
referenced=mess.headers["References"][0].contents.
|
50
|
+
split.last
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
Message.find_by_messageid referenced
|
55
|
+
end
|
56
|
+
|
57
|
+
public
|
58
|
+
# Processes a Gurgitate::Mailmessage object or the text of a
|
59
|
+
# message and creates a new Message object.
|
60
|
+
#
|
61
|
+
# +mess+:: Either a Gurgitate::Mailmessage object (with envelope
|
62
|
+
# information intact), or the text of a mail message (in which
|
63
|
+
# case the envelope information must be supplied)
|
64
|
+
# +recipient+:: The envelope recipient (SMTP's RCPT To:) of the mail
|
65
|
+
# message.
|
66
|
+
# +sender+:: The envelope sender (SMTP's MAIL From:) of the mail
|
67
|
+
# message.
|
68
|
+
def from_message(mess, recipient=nil, sender=nil)
|
69
|
+
# This method is way too long, sorry
|
70
|
+
if Gurgitate::Mailmessage === mess then
|
71
|
+
message = self.new
|
72
|
+
message.messageid = get_or_create_messageid mess
|
73
|
+
message.timestamp = get_or_create_timestamp mess
|
74
|
+
addr = Address.find_or_create_by_address mess.from
|
75
|
+
if addr.new_record? then addr.save end
|
76
|
+
|
77
|
+
message.address = addr
|
78
|
+
message.headers = mess.headers.to_s
|
79
|
+
message.body = mess.body.to_s
|
80
|
+
message.parent = find_parent mess
|
81
|
+
message.envelope_from = mess.from
|
82
|
+
message.envelope_to = mess.to.map { |t| t.contents }
|
83
|
+
|
84
|
+
message.address.save if message.address.new_record?
|
85
|
+
|
86
|
+
ml, type = Mailinglist.find_by_address(message.envelope_to[0])
|
87
|
+
|
88
|
+
if type == :mail and ml then
|
89
|
+
message.mailinglist = ml
|
90
|
+
end
|
91
|
+
|
92
|
+
return message
|
93
|
+
|
94
|
+
elsif String === mess then
|
95
|
+
from_message Gurgitate::Mailmessage.new(mess),recipient,sender
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
alias :parse :from_message
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# Extracts the bare email addresses from a header, without the extra
|
105
|
+
# commentary like the real names.
|
106
|
+
def pull_out_addresses header
|
107
|
+
if header
|
108
|
+
header.contents.split(/,/).map do |addr|
|
109
|
+
Address.parse(addr)[0]
|
110
|
+
end
|
111
|
+
else
|
112
|
+
[ ]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the proxified replacement for a header, with regular email
|
117
|
+
# addresses replaced by proxy addresses.
|
118
|
+
def proxify_header header, addr
|
119
|
+
unless Mailinglist.find_by_address addr
|
120
|
+
if proxy_address = Address.proxyaddress(addr)
|
121
|
+
header[0] . contents . sub( Regexp.new(Regexp.escape(addr) ),
|
122
|
+
proxy_address )
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def proxified_plain_addr header, addr
|
128
|
+
matches = addr.match(/(.*?)#(.*?)@(.*)/)
|
129
|
+
if matches then
|
130
|
+
localpart, destdomain, msgs_domain = matches[1..3]
|
131
|
+
to_address = "#{localpart}@#{destdomain}"
|
132
|
+
if Domain.find_by_name(msgs_domain) then
|
133
|
+
header[0] . contents .
|
134
|
+
sub(Regexp.new(Regexp.escape(addr)), to_address)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def proxify_mail_to_virtual_users
|
140
|
+
# special-case closed mail to mailing lists owned by virtual users
|
141
|
+
if mailinglist
|
142
|
+
if mailinglist.closed?
|
143
|
+
if Mailinglist.find_by_address(envelope_to)[0].user.
|
144
|
+
mailinglist.has_address? envelope_from then
|
145
|
+
if mailinglist.user.description then
|
146
|
+
@mess.headers["From"] = "=?UTF-8?B?" +
|
147
|
+
[ mailinglist.user.description ].pack("m").chomp +
|
148
|
+
"?= <" + mailinglist.user.address + ">"
|
149
|
+
else
|
150
|
+
@mess.headers["From"] = mailinglist.user.address
|
151
|
+
end
|
152
|
+
# @mess.headers.instance_variable_set("@headers_changed",true)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def proxify_proxifiable_mailing_list
|
159
|
+
addr=Address.find_or_create_by_address @mess.from
|
160
|
+
if addr.new_record? then addr.save end
|
161
|
+
|
162
|
+
if addr.outside? then
|
163
|
+
if mailinglist
|
164
|
+
if mailinglist.proxify?
|
165
|
+
if @mess.headers["From"] then
|
166
|
+
@mess.headers["Reply-To"] = addr.proxified mailinglist
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def proxify_normal_addresses
|
174
|
+
%w{From To Cc}.each do |header_name|
|
175
|
+
if @mess.headers[header_name] then
|
176
|
+
@mess.headers[header_name].map do |header|
|
177
|
+
pull_out_addresses header
|
178
|
+
end.flatten.each do |addr|
|
179
|
+
if newhdr = proxify_header(@mess.headers[header_name], addr)
|
180
|
+
@mess.headers[header_name] = newhdr
|
181
|
+
end
|
182
|
+
|
183
|
+
if newhdr = proxified_plain_addr(@mess.headers[header_name],
|
184
|
+
addr)
|
185
|
+
@mess.headers[header_name] = newhdr
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# Rewrites the From:, To: and Cc: headers in the current message
|
194
|
+
# to reflect mail proxy addresses instead of "real" addresses.
|
195
|
+
def proxify_addresses
|
196
|
+
@mess = Gurgitate::Mailmessage.new to_s
|
197
|
+
|
198
|
+
proxify_normal_addresses
|
199
|
+
proxify_mail_to_virtual_users
|
200
|
+
proxify_proxifiable_mailing_list
|
201
|
+
|
202
|
+
@mess.to_s
|
203
|
+
end
|
204
|
+
|
205
|
+
public
|
206
|
+
|
207
|
+
# Returns the text of the message that would be sent out by the
|
208
|
+
# "deliver" message. This might have headers rewritten to reflect
|
209
|
+
# mail aliases.
|
210
|
+
def messagetext(smtpdetails={})
|
211
|
+
if smtpdetails.length == 1 and smtpdetails[0].has_key? :custom_message
|
212
|
+
smtpdetails[0][:custom_message]
|
213
|
+
end
|
214
|
+
|
215
|
+
if mailinglist.canonical_address? or mailinglist.closed? then
|
216
|
+
proxify_addresses
|
217
|
+
else
|
218
|
+
to_s
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def proxy_deliver(from_address, to_address)
|
223
|
+
text = proxify_addresses
|
224
|
+
|
225
|
+
smtpserver = "localhost"
|
226
|
+
smtpport = 25
|
227
|
+
|
228
|
+
Net::SMTP.start(smtpserver, smtpport, "localhost") do |smtp|
|
229
|
+
smtp.send_message text, from_address, to_address
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
# Delivers a message with the optional parameter hash +smtpdetails+.
|
235
|
+
#
|
236
|
+
#
|
237
|
+
def deliver(*smtpdetails)
|
238
|
+
if Hash === smtpdetails[0] then
|
239
|
+
smtpdetails = smtpdetails[0]
|
240
|
+
else
|
241
|
+
smtpdetails = {}
|
242
|
+
end
|
243
|
+
|
244
|
+
if mailinglist.closed?
|
245
|
+
if mailinglist.user.mailinglist.has_address? envelope_from or
|
246
|
+
mailinglist.user.address == envelope_from then
|
247
|
+
end
|
248
|
+
unless mailinglist.user.mailinglist.has_address? self.envelope_from
|
249
|
+
return bounce
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
if mailinglist.confirmation?
|
254
|
+
addresses = mailinglist.confirmed_addresses.map { |a| a.address }
|
255
|
+
else
|
256
|
+
addresses = mailinglist.expand_addresses.map { |a| a.address }
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
# Hm, not sure why this is *here* instead of somewhere
|
261
|
+
# else. Fix later.
|
262
|
+
#
|
263
|
+
# (This should actually be in the database somewhere, I'm thinking.)
|
264
|
+
smtpserver = "localhost"
|
265
|
+
smtpport = 25
|
266
|
+
|
267
|
+
if smtpdetails.has_key? :smtpserver then
|
268
|
+
smtpserver = smtpdetails[:smtpserver]
|
269
|
+
end
|
270
|
+
if smtpdetails.has_key? :smtpport then
|
271
|
+
smtpport = smtpdetails[:smtpport] || 25
|
272
|
+
end
|
273
|
+
|
274
|
+
fromaddress = "#{mailinglist.name}-bounces@#{mailinglist.user.domain.name}"
|
275
|
+
Net::SMTP.start(smtpserver, smtpport, "localhost") do |smtp|
|
276
|
+
# puts "Will send email to #{addresses.inspect}"
|
277
|
+
# puts "Message id is #{id}"
|
278
|
+
smtp.send_message messagetext, fromaddress, addresses
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def bounce
|
283
|
+
# TODO
|
284
|
+
nil
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns the text of the current message
|
288
|
+
def to_s
|
289
|
+
headers.to_s.chomp.chomp+"\n\n"+body.to_s
|
290
|
+
end
|
291
|
+
|
292
|
+
alias responses children # "children" comes from acts_as_tree
|
293
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class ProxyLink < ActiveRecord::Base
|
2
|
+
belongs_to :mailinglist
|
3
|
+
belongs_to :address
|
4
|
+
validates_presence_of :mailinglist
|
5
|
+
validates_presence_of :address
|
6
|
+
|
7
|
+
def proxy_address
|
8
|
+
"#{mailinglist.name}-#{id}@#{mailinglist.domain.name}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.real_address(addrstr)
|
12
|
+
matches=addrstr.match(/(.+)-([\d]+)@(.*)/)
|
13
|
+
if matches
|
14
|
+
mlname, proxyid, domainname = matches[1..3]
|
15
|
+
domain=Domain.find_by_name domainname
|
16
|
+
ml=Mailinglist.find_all_by_name(mlname).find do |m|
|
17
|
+
m.domain == domain
|
18
|
+
end
|
19
|
+
pl=self.find(proxyid.to_i)
|
20
|
+
if pl.mailinglist_id == ml.id then
|
21
|
+
return pl.address.address
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/app/models/user.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
# = The User class
|
5
|
+
#
|
6
|
+
# This is actually based on the user class that is generated
|
7
|
+
# automatically by the Login Generator, but I suspect only software
|
8
|
+
# historians need to know that. The Login Generator is pretty much
|
9
|
+
# universally adapted by anyone writing a Rails application that has
|
10
|
+
# users, and this one is no different.
|
11
|
+
#
|
12
|
+
# From the schema, the data elements of the User class (because
|
13
|
+
# otherwise, they wouldn't be described anywhere).
|
14
|
+
#
|
15
|
+
# +domain+:: The Domain that the user belongs to.
|
16
|
+
# +login+:: The user's username. This is also the name used
|
17
|
+
# for their Sugoi-Mail address.
|
18
|
+
# +password+:: A SHA1-encrypted password hash.
|
19
|
+
# +mailinglist+:: The user's Mailinglist. The addresses listed in
|
20
|
+
# this Mailinglist are the address that any message
|
21
|
+
# sent to +login@domain+ is then forwarded to.
|
22
|
+
# +domainadmin+:: Whether the user is a domain administrator or not.
|
23
|
+
# This mainly controls the ability to create new
|
24
|
+
# users.
|
25
|
+
# +mailinglistadmin+:: Whether they can create, delete, or modify
|
26
|
+
# mailing lists.
|
27
|
+
#
|
28
|
+
# The User's email address is actually provided by the Mailinglist that
|
29
|
+
# belongs to the user. By adding at least one Address to this Mailinglist,
|
30
|
+
# you can forward messages sent to user@sugoi-domain to their real email
|
31
|
+
# address. Any message sent from the user's real address via
|
32
|
+
# sugoi-domain has that real email address rewritten to be the
|
33
|
+
# sugoi-domain address.
|
34
|
+
|
35
|
+
class User < ActiveRecord::Base
|
36
|
+
|
37
|
+
#----------------------------------------
|
38
|
+
# First of all, email address stuff
|
39
|
+
#----------------------------------------
|
40
|
+
has_many :mailinglists
|
41
|
+
belongs_to :mailinglist
|
42
|
+
belongs_to :domain
|
43
|
+
|
44
|
+
# The list of addresses that mail sent to the user will be forwarded
|
45
|
+
# to.
|
46
|
+
def addresses; mailinglist.addresses end
|
47
|
+
|
48
|
+
# The mail proxy email address
|
49
|
+
def address; "#{mailinglist.name}@#{domain.name}" end
|
50
|
+
# def address=(addr); addresses[0]=addr end
|
51
|
+
|
52
|
+
# The user's "description"--if the user's a person, this would be
|
53
|
+
# their real name.
|
54
|
+
def description
|
55
|
+
mailinglist.description
|
56
|
+
end
|
57
|
+
|
58
|
+
# Change the user's description. (Actually changes the user's
|
59
|
+
# Mailinglist's description.)
|
60
|
+
def description=(new_description)
|
61
|
+
m=mailinglist
|
62
|
+
m.description = new_description
|
63
|
+
m.save
|
64
|
+
end
|
65
|
+
|
66
|
+
# Please change the salt to something else,
|
67
|
+
# Every application should use a different one
|
68
|
+
@@salt = 'thuchpog10.?guf'
|
69
|
+
cattr_accessor :salt
|
70
|
+
|
71
|
+
# Authenticate a user.
|
72
|
+
#
|
73
|
+
# Example:
|
74
|
+
# @user = User.authenticate('bob', 'bobpass')
|
75
|
+
#
|
76
|
+
def self.authenticate(login, pass)
|
77
|
+
find_first(["login = ? AND password = ?", login, sha1(pass)])
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
validates_uniqueness_of :login, :on => :create, :scope => :domain_id
|
82
|
+
|
83
|
+
# XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
|
84
|
+
# This really needs to be put *back*, with the proviso that I need to
|
85
|
+
# figure out whether it belongs or not (or whether the mailing list
|
86
|
+
# creation should handle it instead, which is likely the case.)
|
87
|
+
# XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
|
88
|
+
# validates_each :login, :on => :create do |model, attr, value|
|
89
|
+
# if Mailinglist.find(:first, :conditions => [ "name = ?", value ])
|
90
|
+
# model.errors.add(attr, "Address already exists: #{value}" )
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
|
94
|
+
validates_confirmation_of :password
|
95
|
+
validates_length_of :login, :within => 2..40
|
96
|
+
validates_length_of :password, :within => 5..40
|
97
|
+
validates_presence_of :login, :password, :password_confirmation, :domain,
|
98
|
+
:on => :create
|
99
|
+
|
100
|
+
def after_create
|
101
|
+
ml=Mailinglist.new
|
102
|
+
ml.name=self.login
|
103
|
+
ml.user_id=self.id
|
104
|
+
ml.mailinglist_class=MailinglistClass.find 1
|
105
|
+
if ml.save then
|
106
|
+
self.mailinglist=ml
|
107
|
+
return true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def before_destroy
|
112
|
+
ml=self.mailinglist
|
113
|
+
if ml
|
114
|
+
ml.destroy
|
115
|
+
end
|
116
|
+
self.mailinglist_id = nil
|
117
|
+
end
|
118
|
+
|
119
|
+
protected
|
120
|
+
|
121
|
+
# Apply SHA1 encryption to the supplied password.
|
122
|
+
# We will additionally surround the password with a salt
|
123
|
+
# for additional security.
|
124
|
+
def self.sha1(pass)
|
125
|
+
Digest::SHA1.hexdigest("#{salt}--#{pass}--")
|
126
|
+
end
|
127
|
+
|
128
|
+
before_create :crypt_password
|
129
|
+
|
130
|
+
# Before saving the record to database we will crypt the password
|
131
|
+
# using SHA1.
|
132
|
+
# We never store the actual password in the DB.
|
133
|
+
def crypt_password
|
134
|
+
write_attribute "password", self.class.sha1(password)
|
135
|
+
end
|
136
|
+
|
137
|
+
before_update :crypt_unless_empty
|
138
|
+
|
139
|
+
# If the record is updated we will check if the password is empty.
|
140
|
+
# If its empty we assume that the user didn't want to change his
|
141
|
+
# password and just reset it to the old value.
|
142
|
+
def crypt_unless_empty
|
143
|
+
if password.empty?
|
144
|
+
user = self.class.find(self.id)
|
145
|
+
self.password = user.password
|
146
|
+
else
|
147
|
+
write_attribute "password", self.class.sha1(password)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<%= start_form_tag :action=> "login" %>
|
2
|
+
|
3
|
+
<div title="Account login" id="loginform" class="form">
|
4
|
+
<h3>Please login</h3>
|
5
|
+
|
6
|
+
<% if @flash['notice'] %>
|
7
|
+
<div id="message"><%= @flash['notice'] %></div>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<label for="user_login">Login:</label><br/>
|
11
|
+
<input type="text" name="user_login" id="user_login" size="30" value=""/><br/>
|
12
|
+
|
13
|
+
<label for="user_password">Password:</label><br/>
|
14
|
+
<input type="password" name="user_password" id="user_password" size="30"/>
|
15
|
+
|
16
|
+
<br/>
|
17
|
+
<input type="submit" name="login" value="Login »" class="primary" />
|
18
|
+
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<%= end_form_tag %>
|
22
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%= start_form_tag :action=> "signup" %>
|
2
|
+
|
3
|
+
<div title="Account signup" id="signupform" class="form">
|
4
|
+
<h3>Signup</h3>
|
5
|
+
<%= error_messages_for 'user' %><br/>
|
6
|
+
|
7
|
+
<label for="user_login">Desired login:</label><br/>
|
8
|
+
<%= text_field "user", "login", :size => 30 %><br/>
|
9
|
+
<label for="user_password">Choose password:</label><br/>
|
10
|
+
<%= password_field "user", "password", :size => 30 %><br/>
|
11
|
+
<label for="user_password_confirmation">Confirm password:</label><br/>
|
12
|
+
<%= password_field "user", "password_confirmation", :size => 30 %><br/>
|
13
|
+
|
14
|
+
<input type="submit" value="Signup »" class="primary" />
|
15
|
+
|
16
|
+
<%= end_form_tag %>
|
17
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
<div class="memo">
|
3
|
+
<h3>Welcome</h3>
|
4
|
+
|
5
|
+
<p>You are now logged into the system...</p>
|
6
|
+
<p>
|
7
|
+
Since you are here it's safe to assume the application never called store_location, otherwise
|
8
|
+
you would have been redirected somewhere else after a successful login.
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<%= link_to "« logout", :action=>"logout"%>
|
12
|
+
|
13
|
+
</div>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<h1>Editing address</h1>
|
2
|
+
|
3
|
+
<%= start_form_tag :action => 'edit', :id => @address %>
|
4
|
+
<%= render :partial => 'form' %>
|
5
|
+
<%= submit_tag 'Save' %>
|
6
|
+
<%= end_form_tag %>
|
7
|
+
<%= button_to 'Destroy', { :action => 'destroy', :id => @address }, :confirm => 'Are you sure you want to destroy this address?' %>
|
8
|
+
|
9
|
+
<%= link_to 'Show', :action => 'show', :id => @address %> |
|
10
|
+
<%= link_to 'Back to list', :action => 'list' %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<h1>Listing addresses</h1>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<% for column in Address.content_columns %>
|
6
|
+
<th><%= column.human_name %></th>
|
7
|
+
<% end %>
|
8
|
+
</tr>
|
9
|
+
|
10
|
+
<% for address in @addresses %>
|
11
|
+
<tr>
|
12
|
+
<% for column in Address.content_columns %>
|
13
|
+
<td><%=h address.send(column.name) %></td>
|
14
|
+
<% end %>
|
15
|
+
<td><%= link_to 'Show', :action => 'show', :id => address %></td>
|
16
|
+
<td><%= link_to 'Edit', :action => 'edit', :id => address %></td>
|
17
|
+
<td><%= link_to 'Destroy', { :action => 'destroy', :id => address }, :post => true, :confirm => 'Are you sure you want to destroy this address?' %>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
</table>
|
21
|
+
|
22
|
+
<%= link_to 'Previous page', { :page => @address_pages.current.previous } if @address_pages.current.previous %>
|
23
|
+
<%= link_to 'Next page', { :page => @address_pages.current.next } if @address_pages.current.next %>
|
24
|
+
|
25
|
+
<br />
|
26
|
+
|
27
|
+
<%= link_to 'New address', :action => 'new' %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<%= start_form_tag :action=> "login" %>
|
2
|
+
|
3
|
+
<div title="Account login" id="loginform" class="form">
|
4
|
+
<h3>Please login</h3>
|
5
|
+
|
6
|
+
<% if @flash['notice'] %>
|
7
|
+
<div id="message"><%= @flash['notice'] %></div>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<label for="user_login">Login:</label><br/>
|
11
|
+
<input type="text" name="user_login" id="user_login" size="30" value=""/><br/>
|
12
|
+
|
13
|
+
<label for="user_password">Password:</label><br/>
|
14
|
+
<input type="password" name="user_password" id="user_password" size="30"/>
|
15
|
+
|
16
|
+
<br/>
|
17
|
+
<input type="submit" name="login" value="Login »" class="primary" />
|
18
|
+
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<%= end_form_tag %>
|
22
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%= start_form_tag :action=> "signup" %>
|
2
|
+
|
3
|
+
<div title="Account signup" id="signupform" class="form">
|
4
|
+
<h3>Signup</h3>
|
5
|
+
<%= error_messages_for 'domain' %><br/>
|
6
|
+
|
7
|
+
<label for="domain_name">Desired domain name:</label><br/>
|
8
|
+
<%= text_field "domain", "name", :size => 30 %><br/>
|
9
|
+
<label for="domain_password">Choose password:</label><br/>
|
10
|
+
<%= password_field "domain", "password", :size => 30 %><br/>
|
11
|
+
<label for="domain_password_confirmation">Confirm password:</label><br/>
|
12
|
+
<%= password_field "domain", "password_confirmation", :size => 30 %><br/>
|
13
|
+
|
14
|
+
<input type="submit" value="Signup »" class="primary" />
|
15
|
+
|
16
|
+
<%= end_form_tag %>
|
17
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
<div class="memo">
|
3
|
+
<h3>Welcome</h3>
|
4
|
+
|
5
|
+
<p>You are now logged into the system...</p>
|
6
|
+
<p>
|
7
|
+
Since you are here it's safe to assume the application never called store_location, otherwise
|
8
|
+
you would have been redirected somewhere else after a successful login.
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<%= link_to "« logout", :action=>"logout"%>
|
12
|
+
|
13
|
+
</div>
|