sugoi-mail 0.1.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/INSTALL CHANGED
@@ -19,7 +19,10 @@ My recommendation:
19
19
  # wget http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.5-p12.tar.gz
20
20
  # tar zxvf ruby-1.8.5-p12.tar.gz
21
21
  # cd ruby-1.8.5-p12
22
- # ./configure --prefix=/usr/local && make && make check && make install
22
+ # ./configure --prefix=/usr/local
23
+ # make
24
+ # make check
25
+ # make install
23
26
  # cd /usr/local/src
24
27
  # wget http://rubyforge.org/frs/download.php/11289/rubygems-0.9.0.tgz
25
28
  # tar zxvf rubygems-0.9.0.tgz
@@ -94,7 +97,7 @@ b) Create a file called "transport" in /etc/postfix/, to assign the domains for
94
97
 
95
98
  where my.domain.jp can be changed to your liking, and "sugoi" is the name specified in step 2a. This would mean that all mail to xxx@my.domain.jp will be handled by sugoi mail's mailc client. You may add more domains on separate lines in the same format.
96
99
 
97
- c) Postfix reads transport configuration files in binary format, text. This means you need to convert the file you just created into the proper format. Run the following to create 'transport.db', a postfix-readable file:
100
+ c) Postfix reads transport configuration files in binary format, not text. This means you need to convert the file you just created into the proper format. Run the following to create 'transport.db', a postfix-readable file:
98
101
 
99
102
  "sudo postmap transport"
100
103
 
@@ -1,7 +1,10 @@
1
1
  require 'highline/import'
2
2
 
3
+ class UnknownAction < RuntimeError; end
4
+
3
5
  # Controller for command line thingies
4
6
  class CommandlineController < ApplicationController
7
+
5
8
  def perform_action
6
9
  if self.class.action_methods.include?(action_name.to_s) ||
7
10
  self.class.action_methods.include?('method_missing')
@@ -13,4 +16,17 @@ class CommandlineController < ApplicationController
13
16
  raise UnknownAction, "No action responded to #{action_name}", caller
14
17
  end
15
18
  end
19
+
20
+ def method_missing(meth, *params)
21
+ possibles=action_methods.grep(/^#{meth}/)
22
+ case possibles.length
23
+ when 0
24
+ raise UnknownAction, "No action responded to #{action_name}", caller
25
+ when 1
26
+ self.action_name = possibles[0]
27
+ send(possibles[0], *params)
28
+ else
29
+ raise UnknownAction, "More than one command starts with #{meth}"
30
+ end
31
+ end
16
32
  end
@@ -4,7 +4,7 @@ class RootAdminController < MailserviceController
4
4
 
5
5
  end
6
6
 
7
- # Returns true if the user is an administrator and false otherwise
7
+ # Returns true if the user is an administrator and false otherwise
8
8
  def is_admin
9
9
  user_admin? rescue false
10
10
  end
@@ -70,277 +70,4 @@ class RootAdminController < MailserviceController
70
70
  render :controller => 'mailservice', :action => 'admin_mailinglists_all'
71
71
  end
72
72
 
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
- # model :mailinglist
82
-
83
- # private
84
- # def get_password
85
- # password = ask "Password: " do |q| q.echo = false end
86
- # confirmation = ask "Confirm: " do |q| q.echo = false end
87
- #
88
- # return [ password, confirmation ]
89
- # end
90
- #
91
- # def format_errors errors
92
- # errors.map do |facility, text|
93
- # " #{facility}: #{text}\n"
94
- # end
95
- # end
96
- #
97
- # def error(error_text)
98
- # callername = caller[0].gsub(/.*\`(\w+)\'.*/, '\1')
99
- # @error = "#{callername}: #{error_text}"
100
- # nil
101
- # end
102
- #
103
- # def message(message_text)
104
- # callername = caller[0].gsub(/.*\`(\w+)\'.*/, '\1')
105
- # @messages ||= []
106
- # @messages << "#{callername}: #{message_text}"
107
- # end
108
- #
109
- # def ask_description(thing)
110
- # description = ask "Description for #{thing}: "
111
- # if description == "" then description = nil end
112
- # description
113
- # end
114
- #
115
- # public
116
- #
117
- # def init
118
- # @alreadythere = []
119
- # [ MailinglistClass, AdminMessage, SysConfig, Help ].each do |table|
120
- # fixture = YAML.load(File.read(File.join(RAILS_ROOT,
121
- # "test",
122
- # "fixtures",
123
- # table.name.tableize +
124
- # ".yml")))
125
- # fixture.values.sort_by do |values| values["id"].to_i end .
126
- # each do |values|
127
- # begin
128
- # table.find values["id"]
129
- # @alreadythere << "%s[%s]" % [ table.name, values["id"] ]
130
- # rescue ActiveRecord::RecordNotFound
131
- # configvar = table.new values
132
- # configvar.id = values["id"].to_i # hohoho
133
- # configvar.save
134
- # end
135
- # end
136
- # end
137
- # end
138
- #
139
- # def show_config(var_name=nil)
140
- # unless var_name
141
- # @config_vars = SysConfig.find_all
142
- # else
143
- # @config_vars = SysConfig.find_all_by_name(var_name)
144
- # end
145
- # end
146
- #
147
- # def set_config(var_name, new_value)
148
- # @config_var = SysConfig.find_by_name(var_name)
149
- # @config_var.value = new_value
150
- # @config_var.save
151
- # end
152
- #
153
- # def list_domains
154
- # @domains = Domain.find_all
155
- # end
156
- #
157
- # def list_mailinglists#(domain_name)
158
- # flash[:notice] = "List of Mailing Lists"
159
- ## domain = Domain.find_by_name(domain_name)
160
- ## if domain then
161
- ## @mailinglists = domain.mailinglists
162
- ## else
163
- ## error "#{domain_name}: not found"
164
- ## end
165
- # end
166
- #
167
- # def list_addresses#(mailinglist_address)
168
- ## @mailinglist = Mailinglist.find_by_address(mailinglist_address)
169
- ## if @mailinglist then
170
- ## @mailinglist=@mailinglist[0]
171
- ## @addresses = @mailinglist.addresses
172
- ## else
173
- ## error "#{mailinglist_address}: not found"
174
- ## end
175
- # end
176
- #
177
- # def add_user #domain_name, username,
178
- ## email_address = nil,
179
- ## description = nil
180
- ## domain = Domain.find_by_name domain_name
181
- ## if domain then
182
- ## password, password_confirmation = get_password
183
- ##
184
- ## if email_address == nil then
185
- ## email_address = ask "Address to forward #{username}'s email to: "
186
- ## if email_address == "" then email_address = nil end
187
- ## end
188
- ##
189
- ## description ||= ask_description(username)
190
- ##
191
- ## user = User.new
192
- ## user.domain = domain
193
- ## user.login = username
194
- ## user.password = password
195
- ## user.password_confirmation = password_confirmation
196
- ## user.description = description
197
- ## user.domainadmin = false
198
- ## user.mailadmin = false
199
- ##
200
- ## if user.save then
201
- ## message "User \"#{username}@#{domain_name}\" created successfully"
202
- ## if email_address then
203
- ## addr=Address.find_or_create_by_address(email_address)
204
- ## user.addresses << addr
205
- ## end
206
- ## else
207
- ## error "User \"#{username}@#{domain_name}\" was not created:\n" +
208
- ## format_errors(user.errors).join("\n")
209
- ## end
210
- ## else
211
- ## error "Domain \"#{domain_name}\" does not exist."
212
- ## end
213
- # end
214
- #
215
- # def list_users# domain_name
216
- # flash[:notice] = "List of users"
217
- ## domain = Domain.find_by_name domain_name
218
- ## if domain then
219
- ## @users = User.find_all_by_domain_id domain.id
220
- ## else
221
- ## error "Domain \"#{domain_name}\" does not exist."
222
- ## end
223
- # end
224
- #
225
- # def list_mailinglist_classes
226
- # @mailinglist_classes = MailinglistClass.find :all, :order => :id
227
- # end
228
- #
229
- # def list_mlclasses
230
- # list_mailinglist_classes
231
- # render "sugoi_admin/list_mailinglist_classes"
232
- # end
233
- #
234
- # def remove_user
235
- # end
236
- #
237
- # def unsubscribe_self
238
- # end
239
- #
240
- # def unsubscribe_user
241
- # end
242
- #
243
- # def edit_user_list
244
- # end
245
- #
246
- # def change_user_password
247
- # end
248
- #
249
- # def remove_mailinglist
250
- # end
251
- #
252
- # def edit_mailinglist
253
- # end
254
- #
255
- # def add_address
256
- # end
257
- #
258
- # def remove_address
259
- # end
260
- #
261
- # def edit_address
262
- # end
263
- #
264
- # def show_address
265
- # end
266
- #
267
- # def show_mailinglist
268
- # end
269
- #
270
- # def show_user
271
- # end
272
- #
273
- # def add_mailinglist mailinglist_name,
274
- #domain_name, user_name, description = nil, mailinglist_class_id = 2
275
- #
276
- # mlclass = begin
277
- # MailinglistClass.find mailinglist_class_id.to_i
278
- # rescue ActiveRecord::RecordNotFound
279
- # nil
280
- # end
281
- # unless mlclass
282
- # return error("Invalid mailing list class: #{mailinglist_class_id}")
283
- # end
284
- #
285
- # domain = Domain.find_by_name domain_name
286
- # unless domain
287
- # return error("Domain \"#{domain_name}\" not found.")
288
- # end
289
- #
290
- # user = User.find_by_login_and_domain_id user_name, domain.id
291
- # unless user
292
- # return error("User \"#{user_name}\" not found "+
293
- # "in domain \"#{domain_name}\".")
294
- # end
295
- #
296
- # ml=Mailinglist.new
297
- # ml.user = user
298
- # ml.description = description
299
- # ml.mailinglist_class = mlclass
300
- # ml.name = mailinglist_name
301
- #
302
- # if ml.save then
303
- # message "Mailing list \"#{ml.address}\" created successfully."
304
- # end
305
- # end
306
- #
307
- # def subscribe(mailinglist_address, new_address)
308
- # address=Address.find_or_create_by_address new_address
309
- # ml=Mailinglist.find_by_address(mailinglist_address)
310
- # unless ml
311
- # return error("Mailing list \"#{mailinglist_address}\" not found.")
312
- # end
313
- # ml=ml[0]
314
- # ml.addresses << address
315
- # message "Subscribed \"#{new_address}\" to \"#{mailinglist_address}\""
316
- # end
317
- #
318
- # def unsubscribe(mailinglist_address, address_to_remove)
319
- # address = Address.find_by_address address_to_remove
320
- # unless address
321
- # return error("Address \"#{address_to_remove}\" unknown.")
322
- # end
323
- #
324
- # mailinglist = Mailinglist.find_by_address mailinglist_address
325
- # unless mailinglist
326
- # return error("Mailing list \"#{mailinglist_address}\" unknown.")
327
- # end
328
- #
329
- # @mailinglist = mailinglist[0]
330
- #
331
- # unless @mailinglist.addresses.member? address
332
- # return error("Address \"#{address}\" not in " + "
333
- # mailing list \"#{mailinglist_address}\"")
334
- # end
335
- #
336
- # @removed_addresses=@mailinglist.remove_addresses address
337
- # end
338
- #
339
- # def help(command=nil)
340
- # if command
341
- # @help = Help.find_by_facility_and_command self.class.name, command
342
- # else
343
- # @help = Help.find_all_by_facility self.class.name
344
- # end
345
- # end
346
73
  end
@@ -144,7 +144,7 @@ class SugoiAdminController < CommandlineController
144
144
  message "User \"#{username}@#{domain_name}\" created successfully"
145
145
  if email_address then
146
146
  addr=Address.find_or_create_by_address(email_address)
147
- user.addresses << addr
147
+ user.mailinglist.subscribe(addr)
148
148
  end
149
149
  else
150
150
  error "User \"#{username}@#{domain_name}\" was not created:\n" +
@@ -34,11 +34,12 @@
34
34
  # subscriber when she unsubscribes from the
35
35
  # mailing list.
36
36
 
37
+ class ForwardingAlreadySubscribedError < Exception; end
37
38
  class AlreadySubscribedError < Exception; end
38
39
 
39
40
  class Mailinglist < ActiveRecord::Base
40
41
  belongs_to :user
41
- has_and_belongs_to_many :addresses, :before_add => :validate_unique_address
42
+ has_and_belongs_to_many :addresses, :before_add => [ :validate_unique_address, :validate_no_duplicate_forwarding ]
42
43
  has_many :messages
43
44
  has_many :proxy_links
44
45
  validates_each :name, :on => :create do |record, attr, value|
@@ -55,7 +56,15 @@ class Mailinglist < ActiveRecord::Base
55
56
  end
56
57
 
57
58
  def validate_unique_address address
58
- raise AlreadySubscribedError if addresses.member? address
59
+ if addresses.member? address
60
+ raise AlreadySubscribedError
61
+ end
62
+ end
63
+
64
+ def validate_no_duplicate_forwarding address
65
+ if Address.proxyaddress(address.address) and mailinglist_class.id == 1 #XXX MAGIC CONSTANT XXX
66
+ raise ForwardingAlreadySubscribedError
67
+ end
59
68
  end
60
69
 
61
70
  validates_associated :user, :on => :create
@@ -148,6 +157,10 @@ class Mailinglist < ActiveRecord::Base
148
157
  # "#{name}-request@#{user.domain.name}" # XXX MAGIC CONSTANT XXX
149
158
  end
150
159
 
160
+
161
+ #NOTE: THIS RETURNS AN ARRAY!!!!
162
+ # the ML object is the [0]'th index of the array
163
+
151
164
  def self.find_by_address addr
152
165
  if ml = find_by_basic_address(addr) then
153
166
  return [ml,:mail]
@@ -201,6 +214,24 @@ class Mailinglist < ActiveRecord::Base
201
214
  end
202
215
  end
203
216
 
217
+ #this function is for the GUI's search
218
+ def addresses_by_searchstring_and_filter(searchstring, filter)
219
+ addresses_filtered = addresses.find(:all, :conditions => ['address like ?', searchstring+'%'])
220
+ case filter
221
+ when 'confirmed'
222
+ addresses_filtered.each do |addr|
223
+ if addr.confirmed? then addr end
224
+ end
225
+ when 'unconfirmed'
226
+ addresses_filtered.each do |addr|
227
+ if not addr.confirmed? then addr end
228
+ end
229
+ else
230
+ #assume 'all'
231
+ addresses_filtered
232
+ end
233
+ end
234
+
204
235
  def confirmed_addresses
205
236
  addresses.find_all { |addr| confirmed? addr }
206
237
  end
@@ -239,8 +270,13 @@ class Mailinglist < ActiveRecord::Base
239
270
  end
240
271
 
241
272
  def subscribe(address,confirmationcode = nil)
242
- addr=Address.find_or_create_by_address(address) unless Address === address
243
- if confirmation? #used to be confirmation? but that method doesn't appear to exist!
273
+ if Address === address then
274
+ addr = address
275
+ else
276
+ addr = Address.find_or_create_by_address(address)
277
+ end
278
+
279
+ if confirmation?
244
280
  if confirmed_addresses.member? addr
245
281
  true
246
282
  else
@@ -252,6 +288,9 @@ class Mailinglist < ActiveRecord::Base
252
288
  send_welcome_message(addr)
253
289
  rescue AlreadySubscribedError
254
290
  true
291
+ rescue ForwardingAlreadySubscribedError
292
+ errors.add "address", "already has a forwarding address"
293
+ false
255
294
  end
256
295
  end
257
296
  end
@@ -260,6 +299,9 @@ class Mailinglist < ActiveRecord::Base
260
299
  addresses << addr
261
300
  rescue AlreadySubscribedError
262
301
  true
302
+ rescue ForwardingAlreadySubscribedError
303
+ errors.add "address", "already has a forwarding address"
304
+ false
263
305
  end
264
306
  end
265
307
  end
@@ -2,7 +2,6 @@ require "date"
2
2
  require "gurgitate/mailmessage"
3
3
  require "net/smtp"
4
4
  require "socket"
5
- # require "pp"
6
5
 
7
6
  # The Message class is one of the most important classes in
8
7
  # sugoi-mail--mainly because it *does* so much. But anyway. Herewith a
@@ -12,10 +11,14 @@ require "socket"
12
11
  # behaves. It makes considerable use of gurgitate-mail's mail-parsing
13
12
  # library, so it might not be a bad idea to familiarize yourself with
14
13
  # that if trying to work on this.
14
+
15
+ class UnregisteredUserException < Exception; end
16
+
15
17
  class Message < ActiveRecord::Base
16
18
  belongs_to :mailinglist
17
19
  belongs_to :address
18
20
  acts_as_tree :order => "timestamp"
21
+ serialize :envelope_to, Array
19
22
  # has_and_belongs_to_many :mailinglists
20
23
 
21
24
  class << self
@@ -86,13 +89,15 @@ class Message < ActiveRecord::Base
86
89
  t
87
90
  end
88
91
  end
89
-
90
92
  message.address.save if message.address.new_record?
91
93
 
92
94
  ml, type = Mailinglist.find_by_address(message.envelope_to[0])
93
-
94
- if type == :mail and ml then
95
- message.mailinglist = ml
95
+
96
+ if ml then
97
+ if type == :mail then
98
+ message.mailinglist = ml
99
+ message.save
100
+ end
96
101
  end
97
102
 
98
103
  return message
@@ -106,7 +111,6 @@ class Message < ActiveRecord::Base
106
111
  end
107
112
 
108
113
  private
109
-
110
114
  # Extracts the bare email addresses from a header, without the extra
111
115
  # commentary like the real names.
112
116
  def pull_out_addresses header
@@ -146,7 +150,8 @@ class Message < ActiveRecord::Base
146
150
  # special-case closed mail to mailing lists owned by virtual users
147
151
  if mailinglist
148
152
  if mailinglist.closed?
149
- if Mailinglist.find_by_address(envelope_to)[0].user.
153
+ if envelope_to[0] then
154
+ if Mailinglist.find_by_address(envelope_to[0])[0].user.
150
155
  mailinglist.has_address? envelope_from then
151
156
  if mailinglist.user.description then
152
157
  @mess.headers["From"] = "=?UTF-8?B?" +
@@ -157,6 +162,7 @@ class Message < ActiveRecord::Base
157
162
  end
158
163
  # @mess.headers.instance_variable_set("@headers_changed",true)
159
164
  end
165
+ end
160
166
  end
161
167
  end
162
168
  end
@@ -186,8 +192,7 @@ class Message < ActiveRecord::Base
186
192
  @mess.headers[header_name] = newhdr
187
193
  end
188
194
 
189
- if newhdr = proxified_plain_addr(@mess.headers[header_name],
190
- addr)
195
+ if newhdr = proxified_plain_addr(@mess.headers[header_name],addr)
191
196
  @mess.headers[header_name] = newhdr
192
197
  end
193
198
  end
@@ -195,7 +200,6 @@ class Message < ActiveRecord::Base
195
200
  end
196
201
  end
197
202
 
198
-
199
203
  # Rewrites the From:, To: and Cc: headers in the current message
200
204
  # to reflect mail proxy addresses instead of "real" addresses.
201
205
  def proxify_addresses
@@ -208,6 +212,38 @@ class Message < ActiveRecord::Base
208
212
  @mess.to_s
209
213
  end
210
214
 
215
+ def generate_envelope_data
216
+ if self.envelope_to and self.envelope_from then
217
+ matches = self.envelope_to[0].match(/(.*?)#(.*?)@(.*)/)
218
+
219
+ if matches then
220
+ localpart, destdomain, domain = matches[1..3]
221
+ to_address = "#{localpart}@#{destdomain}"
222
+ proxy_address=Address.proxyaddress(self.envelope_from)
223
+ if proxy_address
224
+ #the 'funky' address rewriting 'thingy' is found
225
+ self.envelope_to = [ to_address ]
226
+ self.envelope_from = proxy_address
227
+ return
228
+ else
229
+ #the sender of the email does not have an LTMA
230
+ raise UnregisteredUserException
231
+ return
232
+ end
233
+ end
234
+ end
235
+
236
+ #if no envelope data is found, or 'To:' doesn need rewriting..
237
+ #default to the standard mailinglist mode
238
+ if mailinglist.confirmation?
239
+ self.envelope_to = mailinglist.confirmed_addresses.map { |a| a.address }
240
+ else
241
+ self.envelope_to = mailinglist.expand_addresses.map { |a| a.address }
242
+ end
243
+ self.envelope_from = mailinglist.name + SysConfig.address_separator + SysConfig.bounce_suffix + "@" + mailinglist.user.domain.name
244
+
245
+ end
246
+
211
247
  public
212
248
 
213
249
  # Returns the text of the message that would be sent out by the
@@ -218,11 +254,10 @@ class Message < ActiveRecord::Base
218
254
  return smtpdetails[:custom_message]
219
255
  end
220
256
 
221
- if mailinglist and
222
- ( mailinglist.canonical_address? or mailinglist.closed? ) then
257
+ if mailinglist.canonical_address? or mailinglist.closed? then
223
258
  proxify_addresses
224
259
  else
225
- to_s
260
+ @mess.to_s
226
261
  end
227
262
  end
228
263
 
@@ -230,27 +265,17 @@ class Message < ActiveRecord::Base
230
265
  #
231
266
  #
232
267
  def deliver(*smtpdetails)
268
+
233
269
  if Hash === smtpdetails[0] then
234
270
  smtpdetails = smtpdetails[0]
235
271
  else
236
272
  smtpdetails = {}
237
273
  end
238
274
 
239
- if mailinglist.closed?
240
- if mailinglist.user.mailinglist.has_address? envelope_from or
241
- mailinglist.user.address == envelope_from then
242
- end
243
- unless mailinglist.user.mailinglist.has_address? self.envelope_from
244
- return bounce
245
- end
246
- end
247
-
248
- if mailinglist.confirmation?
249
- addresses = mailinglist.confirmed_addresses.map { |a| a.address }
250
- else
251
- addresses = mailinglist.expand_addresses.map { |a| a.address }
252
- end
253
-
275
+ # Hm, not sure why this is *here* instead of somewhere
276
+ # else. Fix later.
277
+ #
278
+ # (This should actually be in the database somewhere, I'm thinking.)
254
279
  smtpserver = SysConfig.smtpserver
255
280
  smtpport = SysConfig.smtpport
256
281
  mysmtpname = SysConfig.mysmtpname
@@ -262,10 +287,23 @@ class Message < ActiveRecord::Base
262
287
  smtpport = smtpdetails[:smtpport] || 25
263
288
  end
264
289
 
265
- fromaddress = mailinglist.name + SysConfig.address_separator +
266
- SysConfig.bounce_suffix + "@" + mailinglist.user.domain.name
290
+ if mailinglist.closed?
291
+ unless mailinglist.user.mailinglist.has_address? self.envelope_from or mailinglist.user.address == self.envelope_from then
292
+ return bounce
293
+ end
294
+ end
295
+
296
+ begin
297
+ generate_envelope_data
298
+ rescue UnregisteredUserException
299
+ return bounce
300
+ end
301
+
302
+ #Good debugging thingy:
303
+ #pp self
304
+
267
305
  Net::SMTP.start(smtpserver, smtpport, mysmtpname) do |smtp|
268
- smtp.send_message messagetext(smtpdetails), fromaddress, addresses
306
+ smtp.send_message messagetext(smtpdetails), self.envelope_from, self.envelope_to
269
307
  end
270
308
  end
271
309