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.
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>