Class: Mailinglist

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/mailinglist.rb

Overview

Mailing lists are what make Sugoi-Mail go. Which makes sense, I suppose, since Sugoi-Mail is a mailing list manager.

A Mailing List has the following structure:

(from the schema)

name: The name of the mailing list. This will

become the local part of the list's email
address.
description

The description of the mailing list. This go into the “real name” portion of the mailing list’s email address listed in the “From:” header in messages that are sent out to subscribers.

user

The Sugoi-Mail user who owns and manages the mailing list–this is a User object. The domain of the mailing list is the domain that the user is a member of.

mailinglist_class

The kind of mailing list that this is. This is a MailinglistClass object.

welcome_admin_message

The message that is sent out to a new member when a subscription is received. This is an AdminMessage object.

confirmed_admin_message

The message that is sent out to a new member when the subscription confirmation message is confirmed. This is also an AdminMessage object.

sayonara_admin_message_id

The final AdminMessage object (as of the moment anyway–couriermlm mailing lists have many more administrative messages, and I suspect I may have to implement all those at some point). This is the message that is sent out to a subscriber when she unsubscribes from the mailing list.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.find_by_address(addr) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
# File 'app/models/mailinglist.rb', line 132

def self.find_by_address addr
    if ml = find_by_basic_address(addr) then
        return [ml,:mail]
    elsif info = find_by_proxy_address(addr) then
        ml, address_id = info
        return [ml, :proxy, address_id]
    elsif(ml = find_by_bounces_address(addr)) then
        return [ml, :bounces]
    elsif(ml = find_by_request_address(addr)) then
        return [ml, :request]
    end
end

.find_by_basic_address(addr) ⇒ Object



145
146
147
148
149
150
151
152
153
# File 'app/models/mailinglist.rb', line 145

def self.find_by_basic_address addr
    (ml_name, domain_name) = addr.split "@"
    domain=Domain.find_by_name domain_name
    if domain
        ml=Mailinglist.find_all_by_name(ml_name).find do |ml|
            ml.domain.id == domain.id
        end
    end
end

.find_by_bounces_address(bounces_addr) ⇒ Object



169
170
171
# File 'app/models/mailinglist.rb', line 169

def self.find_by_bounces_address bounces_addr
    find_by_basic_address bounces_addr.sub(/-bounces\@/,"@")
end

.find_by_proxy_address(addr) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'app/models/mailinglist.rb', line 155

def self.find_by_proxy_address addr
    if matchdata=addr.match(/#{Regexp.escape(SysConfig.address_separator)}([0-9]+)\@/) then
        proxy_id=matchdata[1].to_i
        if ml=find_by_basic_address(addr.sub(/-[0-9]+\@/,"@")) then
            begin
                proxy=ProxyLink.find(proxy_id)
                return ml, proxy.address
            rescue
                nil
            end
        end
    end
end

.find_by_request_address(request_addr) ⇒ Object



173
174
175
# File 'app/models/mailinglist.rb', line 173

def self.find_by_request_address request_addr
    find_by_basic_address request_addr.sub(/-request\@/,"@")
end

Instance Method Details

#addressObject


These are all to make it work with rumble (as a replacement for rumble’s mailing-list class).




115
116
117
118
# File 'app/models/mailinglist.rb', line 115

def address
    name + "@" + user.domain.name
    # "#{name}@#{user.domain.name}"
end

#before_saveObject



88
89
90
91
92
93
94
95
96
97
98
# File 'app/models/mailinglist.rb', line 88

def before_save
    welcome_admin_message   ||= 
        AdminMessage.find SysConfig.default_welcome_admin_message_id
    confirmed_admin_message ||=
        AdminMessage.find SysConfig.default_confirmed_admin_message_id
    sayonara_admin_message ||=
        AdminMessage.find SysConfig.default_farewell_admin_message_id
    # welcome_admin_message   ||= AdminMessage.find 1 # XXX MAGIC CONSTANT 
    # confirmed_admin_message ||= AdminMessage.find 2 # XXX MAGIC CONSTANT
    # sayonara_admin_message  ||= AdminMessage.find 3 # XXX MAGIC CONSTANT
end

#bounceaddressObject



120
121
122
123
124
# File 'app/models/mailinglist.rb', line 120

def bounceaddress
    name + SysConfig.address_separator + SysConfig.bounce_suffix + "@" +
        user.domain.name
    # "#{name}-bounce@#{user.domain.name}"   # XXX MAGIC CONSTANT XXX
end

#canonical_address?Boolean

Returns true if this mailing list’s address is the canonical address of a User.

Returns:

  • (Boolean)


79
80
81
# File 'app/models/mailinglist.rb', line 79

def canonical_address?
    id == user.mailinglist_id
end

#confirm(address, confirmationcode) ⇒ Object



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'app/models/mailinglist.rb', line 269

def confirm address,confirmationcode
    addr=nil

    if Address === address
        addr=address
    else
        addr=Address.find_by_address(address)
    end

    if addr then
        if confirmation? then
            if Confirmationcode.confirm(self, addr, confirmationcode) then
                save
                confirmed_admin_message.send_mail bounceaddress, 
                    addr.address,
                    :address => addr.address,
                    :domain => domain.name,
                    :name => name, 
                    :description => description,
                    :requestaddress => requestaddress
                return true
            else
                return false
            end
        else
            return true
        end
    end
end

#confirmed?(addr) ⇒ Boolean

Returns:

  • (Boolean)


177
178
179
180
181
182
183
# File 'app/models/mailinglist.rb', line 177

def confirmed? addr
    if confirmation? then
        Confirmationcode.confirmed? self.id, addr.id
    else 
        true
    end
end

#confirmed_addressesObject



185
186
187
# File 'app/models/mailinglist.rb', line 185

def confirmed_addresses
    addresses.find_all { |addr| confirmed? addr }
end

#domainObject



214
# File 'app/models/mailinglist.rb', line 214

def domain; user.domain; end

#expand_addressesObject



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'app/models/mailinglist.rb', line 195

def expand_addresses
    address_list = addresses
    expanded = false
    until expanded
        foundsome = false
        new_address_list = address_list.map do |addr|
            if ml = Mailinglist.find_by_address(addr.address) then
                foundsome = true
                ml[0].addresses
            else
                addr
            end
        end.flatten
        expanded = ! foundsome
        address_list = new_address_list
    end
    address_list
end

#handle_request(requestmessage) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'app/models/mailinglist.rb', line 322

def handle_request requestmessage
    subject=requestmessage.headers["Subject"][0].contents
    matches=requestmessage.body.match(/^\W*subscribe\s+(\S+@[a-z0-9.-]+)
                       (?:\s+([a-z0-9]{16}))?$/x)
    if matches then
        # puts "Found subscription request: #{matches[0]}"
        
        addresscandidate=matches[1]
        confirmationcode=matches[2] # returns nil if there isn'tone
        
        # Since you can have mailing lists that require confirmation, but
        # they're not joinable, check for the confirmationcode case first.
        if confirmationcode then
            return subscribe(addresscandidate, confirmationcode)
        else
            if joinable? then
                return subscribe(addresscandidate)
            end
        end
    end
    
    # TODO: actually implement confirmed unsubscribing down in the
    # model.  (This is, of course, to prevent pranksters from
    # unsubscribing you from a mailing list behind your back.)
    matches=requesmessage.body.match(/^\W*unsubscribe\s+(\S+@[a-z0-9.-]+)
                        (?:\s+([a-z0-9]{16}))?$/x)
    if matches then
        addresscandidate=matches[1]
        confirmationcode=matches[2]
        
        return unsubscribe(addresscandidate, confirmationcode)
    end
end

#has_address?(addr) ⇒ Boolean

Returns true if the address addr is subscribed to this mailing list.

Returns:

  • (Boolean)


84
85
86
# File 'app/models/mailinglist.rb', line 84

def has_address? addr
    expand_addresses.map { |a| a.address }.member? addr
end

#pending_addressesObject Also known as: unconfirmed_addresses



189
190
191
# File 'app/models/mailinglist.rb', line 189

def pending_addresses
    addresses.find_all { |addr| not confirmed? addr }
end

#requestaddressObject



126
127
128
129
130
# File 'app/models/mailinglist.rb', line 126

def requestaddress
    name + SysConfig.address_separator + SysConfig.request_suffix + "@" +
        user.domain.name
    # "#{name}-request@#{user.domain.name}"  # XXX MAGIC CONSTANT XXX
end

#send_message(message) ⇒ Object



218
219
220
# File 'app/models/mailinglist.rb', line 218

def send_message message
    message.deliver
end

#send_welcome_message(address) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'app/models/mailinglist.rb', line 244

def send_welcome_message(address)
    if Address === address then
        addr = address
    else
        addr=Address.find_or_create_by_address(address) 
    end
    c=Confirmationcode.find_by_address_id_and_mailinglist_id addr.id, id
    unless c
        c=Confirmationcode.new
        c.address_id=addr.id
        c.mailinglist_id=id
        c.save
    end

    unless c.confirmed?
        welcome_admin_message.send_mail bounceaddress, addr.address,
            :address => addr.address,
            :command => "subscribe #{addr.address} #{c.code}",
            :domain => domain.name,
            :name => name,
            :description => description,
            :requestaddress => requestaddress
        end
end

#store_message(message) ⇒ Object



216
# File 'app/models/mailinglist.rb', line 216

def store_message message; messages << message; end

#subscribe(address, confirmationcode = nil) ⇒ Object



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'app/models/mailinglist.rb', line 222

def subscribe(address,confirmationcode = nil)
    addr=Address.find_or_create_by_address(address)
    if confirmation?
        if confirmed_addresses.member? addr
            true
        else
            if confirmationcode then
                confirm addr, confirmationcode
            else
                send_welcome_message(addr)
                addresses << addr
            end
        end
    else
        if addresses.member? addr
            true
        else
            addresses << addr
        end
    end
end

#unsubscribe(address, send_mail = true) ⇒ Object



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'app/models/mailinglist.rb', line 299

def unsubscribe(address, send_mail = true)
    addr=nil

    unless Address === address
        addr=Address.find_by_address address
    else
        addr=address
    end

    if addresses.member? addr then
        if send_mail
            sayonara_admin_message.send_mail bounceaddress, 
                addr.address,
                :address => addr.address,
                :domain => domain.name,
                :name => name, 
                :description => description,
                :requestaddress => requestaddress
        end
        remove_addresses addr
    end
end