sugoi-mail 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. data/README +35 -0
  2. data/Rakefile +10 -0
  3. data/app/apis/mailservice_api.rb +178 -0
  4. data/app/controllers/.sugoi_admin_controller.rb.swp +0 -0
  5. data/app/controllers/account_controller.rb +39 -0
  6. data/app/controllers/address_controller.rb +49 -0
  7. data/app/controllers/application.rb +4 -0
  8. data/app/controllers/commandline_controller.rb +15 -0
  9. data/app/controllers/domain_controller.rb +37 -0
  10. data/app/controllers/mailinglist_controller.rb +51 -0
  11. data/app/controllers/mailservice_controller.rb +497 -0
  12. data/app/controllers/sugoi_admin_controller.rb +93 -0
  13. data/app/helpers/account_helper.rb +2 -0
  14. data/app/helpers/address_helper.rb +2 -0
  15. data/app/helpers/application_helper.rb +3 -0
  16. data/app/helpers/domain_helper.rb +2 -0
  17. data/app/helpers/mailservice_helper.rb +2 -0
  18. data/app/helpers/sugoi_admin_helper.rb +2 -0
  19. data/app/models/address.rb +110 -0
  20. data/app/models/admin_message.rb +45 -0
  21. data/app/models/confirmationcode.rb +59 -0
  22. data/app/models/domain.rb +76 -0
  23. data/app/models/mailinglist.rb +336 -0
  24. data/app/models/mailinglist_class.rb +39 -0
  25. data/app/models/message.rb +293 -0
  26. data/app/models/proxy_link.rb +25 -0
  27. data/app/models/user.rb +150 -0
  28. data/app/views/account/login.rhtml +22 -0
  29. data/app/views/account/logout.rhtml +10 -0
  30. data/app/views/account/signup.rhtml +17 -0
  31. data/app/views/account/welcome.rhtml +13 -0
  32. data/app/views/address/_form.rhtml +7 -0
  33. data/app/views/address/edit.rhtml +10 -0
  34. data/app/views/address/list.rhtml +27 -0
  35. data/app/views/address/new.rhtml +8 -0
  36. data/app/views/address/show.rhtml +8 -0
  37. data/app/views/domain/login.rhtml +22 -0
  38. data/app/views/domain/logout.rhtml +10 -0
  39. data/app/views/domain/signup.rhtml +17 -0
  40. data/app/views/domain/welcome.rhtml +13 -0
  41. data/app/views/layouts/address.rhtml +13 -0
  42. data/app/views/layouts/scaffold.rhtml +13 -0
  43. data/app/views/mailinglist/_form.rhtml +28 -0
  44. data/app/views/mailinglist/edit.rhtml +10 -0
  45. data/app/views/mailinglist/list.rhtml +27 -0
  46. data/app/views/mailinglist/new.rhtml +8 -0
  47. data/app/views/mailinglist/show.rhtml +16 -0
  48. data/app/views/sugoi_admin/create_domain.rhtml +1 -0
  49. data/app/views/sugoi_admin/list_addresses.rhtml +1 -0
  50. data/app/views/sugoi_admin/list_domains.rhtml +2 -0
  51. data/app/views/sugoi_admin/list_mailinglists.rhtml +1 -0
  52. data/bin/mailc +32 -0
  53. data/bin/maild +133 -0
  54. data/bin/sugoi-admin +21 -0
  55. data/bin/sugoi-mail +20 -0
  56. data/config/boot.rb +44 -0
  57. data/config/environment.rb +54 -0
  58. data/config/environments/bench.rb +21 -0
  59. data/config/environments/coverage.rb +21 -0
  60. data/config/environments/development.rb +21 -0
  61. data/config/environments/production.rb +18 -0
  62. data/config/environments/test.rb +19 -0
  63. data/config/lighttpd.conf +46 -0
  64. data/config/routes.rb +29 -0
  65. data/db/migrate/001_mailproxy.rb +7 -0
  66. data/db/migrate/002_create_users.rb +13 -0
  67. data/db/migrate/003_create_mailinglists.rb +13 -0
  68. data/db/migrate/004_create_addresses.rb +12 -0
  69. data/db/migrate/005_create_addresses_mailinglists.rb +13 -0
  70. data/db/migrate/006_alter_mailinglists.rb +9 -0
  71. data/db/migrate/007_create_messages.rb +25 -0
  72. data/db/migrate/008_add_mailinglistid_to_users.rb +14 -0
  73. data/db/migrate/009_add_domainadmin_to_users.rb +9 -0
  74. data/db/migrate/010_add_domain_to_users.rb +16 -0
  75. data/db/migrate/011_add_active_to_addresses.rb +14 -0
  76. data/db/migrate/012_create_confirmationcodes.rb +14 -0
  77. data/db/migrate/013_add_description_to_mailinglists.rb +9 -0
  78. data/db/migrate/014_create_admin_messages.rb +69 -0
  79. data/db/migrate/015_add_messages_to_mailinglists.rb +26 -0
  80. data/db/migrate/016_add_mailinglist_admin_to_users.rb +9 -0
  81. data/db/migrate/017_add_mailinglist_types.rb +94 -0
  82. data/db/migrate/018_add_bounciness_to_addresses.rb +20 -0
  83. data/db/migrate/019_add_archived_to_mailinglist_classes.rb +25 -0
  84. data/db/migrate/020_add_envelope_data_to_messages.rb +11 -0
  85. data/db/migrate/021_add_addresses_users.rb +14 -0
  86. data/db/migrate/022_add_virtual_to_users.rb +14 -0
  87. data/db/migrate/023_drop_openposting_from_mailinglists.rb +9 -0
  88. data/db/migrate/024_add_proxy_links.rb +21 -0
  89. data/db/migrate/025_add_proxify_to_mailinglist_classes.rb +25 -0
  90. data/db/schema.mysql.sql +104 -0
  91. data/db/schema.postgresql.sql +104 -0
  92. data/db/schema.rb +85 -0
  93. data/db/schema.sqlite.sql +104 -0
  94. data/db/schema.sqlserver.sql +113 -0
  95. data/db/schema_version +1 -0
  96. data/doc/README_FOR_APP +179 -0
  97. data/doc/mailinglist_classes description.txt +28 -0
  98. data/installer/rails_installer_defaults.yml +5 -0
  99. data/lib/daemonize.rb +56 -0
  100. data/lib/domain_system.rb +87 -0
  101. data/lib/gurgitate-rules.rb +69 -0
  102. data/lib/limitedfork.rb +66 -0
  103. data/lib/login_system.rb +87 -0
  104. data/public/.htaccess +40 -0
  105. data/public/404.html +8 -0
  106. data/public/500.html +8 -0
  107. data/public/dispatch.cgi +10 -0
  108. data/public/dispatch.fcgi +24 -0
  109. data/public/dispatch.rb +10 -0
  110. data/public/favicon.ico +0 -0
  111. data/public/images/rails.png +0 -0
  112. data/public/javascripts/application.js +2 -0
  113. data/public/javascripts/controls.js +815 -0
  114. data/public/javascripts/dragdrop.js +913 -0
  115. data/public/javascripts/effects.js +958 -0
  116. data/public/javascripts/prototype.js +2006 -0
  117. data/public/robots.txt +1 -0
  118. data/public/stylesheets/scaffold.css +74 -0
  119. data/public/stylesheets/trestle.css +74 -0
  120. data/script/about +3 -0
  121. data/script/breakpointer +3 -0
  122. data/script/console +3 -0
  123. data/script/destroy +3 -0
  124. data/script/fakedeliver +19 -0
  125. data/script/generate +3 -0
  126. data/script/performance/benchmarker +3 -0
  127. data/script/performance/profiler +3 -0
  128. data/script/plugin +3 -0
  129. data/script/process/reaper +3 -0
  130. data/script/process/spawner +3 -0
  131. data/script/runner +3 -0
  132. data/script/server +3 -0
  133. data/sugoi-mail.gemspec +36 -0
  134. data/test/fixtures/addresses.yml +70 -0
  135. data/test/fixtures/addresses_mailinglists.yml +20 -0
  136. data/test/fixtures/admin_messages.yml +65 -0
  137. data/test/fixtures/confirmationcodes.yml +13 -0
  138. data/test/fixtures/domains.yml +9 -0
  139. data/test/fixtures/mailinglist_classes.yml +45 -0
  140. data/test/fixtures/mailinglists.yml +80 -0
  141. data/test/fixtures/messages.yml +154 -0
  142. data/test/fixtures/proxy_links.yml +5 -0
  143. data/test/fixtures/users.yml +50 -0
  144. data/test/functional/domain_controller_test.rb +74 -0
  145. data/test/functional/mailservice_controller_test.rb +546 -0
  146. data/test/integration/test_soap.rb +413 -0
  147. data/test/integration/test_xmlrpc.rb +198 -0
  148. data/test/mocks/test/net-smtp-stub.rb +24 -0
  149. data/test/test_helper.rb +28 -0
  150. data/test/unit/address_test.rb +44 -0
  151. data/test/unit/admin_message_test.rb +41 -0
  152. data/test/unit/confirmationcode_test.rb +64 -0
  153. data/test/unit/domain_test.rb +128 -0
  154. data/test/unit/mailinglist_class_test.rb +82 -0
  155. data/test/unit/mailinglist_test.rb +145 -0
  156. data/test/unit/message_test.rb +151 -0
  157. data/test/unit/user_test.rb +126 -0
  158. data/test/units.rb +4 -0
  159. data/vendor/plugins/active_command/init.rb +1 -0
  160. data/vendor/plugins/active_command/lib/active_command/request.rb +137 -0
  161. data/vendor/plugins/active_command/lib/active_command/response.rb +132 -0
  162. data/vendor/plugins/active_command/lib/active_command.rb +2 -0
  163. data/vendor/plugins/ar_fixtures/CHANGELOG +10 -0
  164. data/vendor/plugins/ar_fixtures/MIT-LICENSE +20 -0
  165. data/vendor/plugins/ar_fixtures/README +19 -0
  166. data/vendor/plugins/ar_fixtures/Rakefile +54 -0
  167. data/vendor/plugins/ar_fixtures/about.yml +7 -0
  168. data/vendor/plugins/ar_fixtures/init.rb +1 -0
  169. data/vendor/plugins/ar_fixtures/lib/ar_fixtures.rb +102 -0
  170. data/vendor/plugins/ar_fixtures/tasks/ar_fixtures.rake +39 -0
  171. data/vendor/plugins/ar_fixtures/test/ar_fixtures_test.rb +53 -0
  172. data/vendor/plugins/ar_fixtures/test/database.yml +18 -0
  173. data/vendor/plugins/ar_fixtures/test/fixtures/beer.rb +5 -0
  174. data/vendor/plugins/ar_fixtures/test/fixtures/beers.yml +9 -0
  175. data/vendor/plugins/ar_fixtures/test/fixtures/beers_drunkards.yml +8 -0
  176. data/vendor/plugins/ar_fixtures/test/fixtures/drunkard.rb +6 -0
  177. data/vendor/plugins/ar_fixtures/test/fixtures/drunkards.yml +8 -0
  178. data/vendor/plugins/ar_fixtures/test/fixtures/glass.rb +2 -0
  179. data/vendor/plugins/ar_fixtures/test/fixtures/glasses.yml +9 -0
  180. data/vendor/plugins/ar_fixtures/test/schema.rb +21 -0
  181. data/vendor/plugins/ar_fixtures/test/test_helper.rb +37 -0
  182. 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
@@ -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 &#187;" class="primary" />
18
+
19
+ </div>
20
+
21
+ <%= end_form_tag %>
22
+
@@ -0,0 +1,10 @@
1
+
2
+ <div class="memo">
3
+ <h3>Logoff</h3>
4
+
5
+ <p>You are now logged out of the system...</p>
6
+
7
+ <%= link_to "&#171; login", :action=>"login"%>
8
+
9
+ </div>
10
+
@@ -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 &#187;" 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 "&#171; logout", :action=>"logout"%>
12
+
13
+ </div>
@@ -0,0 +1,7 @@
1
+ <%= error_messages_for 'address' %>
2
+
3
+ <!--[form:address]-->
4
+ <p><label for="address_address">Address</label><br/>
5
+ <%= text_area 'address', 'address' %></p>
6
+ <!--[eoform:address]-->
7
+
@@ -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,8 @@
1
+ <h1>New address</h1>
2
+
3
+ <%= start_form_tag :action => 'new' %>
4
+ <%= render :partial => 'form' %>
5
+ <%= submit_tag 'Save' %>
6
+ <%= end_form_tag %>
7
+
8
+ <%= link_to 'Back to list', :action => 'list' %>
@@ -0,0 +1,8 @@
1
+ <% for column in Address.content_columns %>
2
+ <p>
3
+ <b><%= column.human_name %>:</b> <%= h @address.send(column.name) %>
4
+ </p>
5
+ <% end %>
6
+
7
+ <%= link_to 'Edit', :action => 'edit', :id => @address %> |
8
+ <%= link_to 'Back to list', :action => 'list' %>
@@ -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 &#187;" class="primary" />
18
+
19
+ </div>
20
+
21
+ <%= end_form_tag %>
22
+
@@ -0,0 +1,10 @@
1
+
2
+ <div class="memo">
3
+ <h3>Logoff</h3>
4
+
5
+ <p>You are now logged out of the system...</p>
6
+
7
+ <%= link_to "&#171; login", :action=>"login"%>
8
+
9
+ </div>
10
+
@@ -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 &#187;" 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 "&#171; logout", :action=>"logout"%>
12
+
13
+ </div>