33
33
#2)The case when we declare one object say subscriber = Subscriber(mlist) and then use this subscriber object many times to
34
34
#call different decorated member functions of Subscriber class.Such kind of case will always satisfy self == classObject,
35
35
#here we want store.close() to take place as if it does not,it will leave the store connection always open that can possibly be a bug.
39
def inner(self, *args):
41
if self == classObject:
42
if self.store == None:#for the case when same object is used for calling more than one functions
43
self.store = Store(self.database)
44
result = f(self,*args)
48
else:#To handle the nesting issue
49
result = f(self,*args)
51
else:#for the case whenever a new object calls its decorated member function
53
self.store = Store(self.database)
54
result = f(self,*args)
38
def inner(self, *args):
40
if self == classObject:
41
if self.store == None:#for the case when same object is used for calling more than one functions
42
self.store = Store(self.database)
43
result = f(self,*args)
47
else:#To handle the nesting issue
48
result = f(self,*args)
50
else:#for the case whenever a new object calls its decorated member function
52
self.store = Store(self.database)
53
result = f(self,*args)
61
60
#Define all the classes corresponding to the tables in the database
62
61
class Subscriber(object):
63
__storm_table__ = "subscriber"
64
subscriber_id = Int(primary = True,default = AutoReload)
65
mailman_key = Unicode()
66
preference = Int(default = 1)
67
format = Int(default = 3)
68
deleted = Bool(default = False)
69
suppress = Int(default = 0)
71
def __init__(self,mlist):
73
self.database = getConn(mlist)
76
def getSubscriber_id_raw(self, addr):
80
command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),'utf-8'))\na = [(subscriber.subscriber_id) for subscriber in result]\n"
82
syslog('info', "DlistUtils:(getSubbscriber_id_raw)executing query:\n%s", command)
83
#storm recognizes unicode only
84
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),"utf-8"))
85
a = [(subscriber.subscriber_id) for subscriber in result]
87
syslog('info', 'value of a is: %s\n', a)
89
#The ResultSet "a" obtained in above storm command and later on in similar ones is always a list
95
def getSubscriber_id_raw_or_die(self, addr):
96
result = self.getSubscriber_id_raw(addr)
98
#syslog('error', 'getSubscriber_id_raw_or_die /msg nickserv register <your-password> <your-email>unable to find address /%s/ for mailing list /%s/', addr, self.mlist.internal_name())
99
raise ErrorsDlist.InternalError
103
def getSubscriber_id(self, msg, msgdata, safe=0, loose=0):
104
"""Returns the subscriber_id of the sender of a message and sets the 'subscriber_id' field in the msg object."""
105
fromAddr = email.Utils.parseaddr(msg['From'])[1]
107
subscriber_id = msgdata['subscriber_id']
110
subscriber_id = self.getSubscriber_id_raw(fromAddr)
111
if subscriber_id == None:
112
alias = Alias(self.mlist)
113
bestAddr = alias.canonicalize_sender(msg.get_senders())
114
subscriber_id = self.getSubscriber_id_raw(bestAddr)
115
if subscriber_id == None:
117
#syslog('info', "DlistUtils.getSubscriber_id: subscriber_id is None and safe is %d", safe)
119
# This could happen if a non-member is given permission to post
123
#syslog('info', "Raising ErrorsDlist.NotMemberError")
124
raise ErrorsDlist.NotMemberError("Your request could not be processed because %s is not subscribed to %s. Perhaps you are subscribed with a different email address, which forwards to %s. If so, please log into %s and add this as an 'other incoming email address'." % (fromAddr, self.mlist.internal_name(), fromAddr, self.mlist.GetScriptURL('options', 1)))
126
syslog('info', 'subscriber_id = %d\n', subscriber_id)
127
msgdata['subscriber_id'] = subscriber_id
131
def get_format(self, subscriber_id):
132
command = "return int(self.store.get(Subscriber,subscriber_id).format)\n"
134
syslog('info', 'DlistUtils(get_format):Executing query:\n%s', command)
135
return int(self.store.get(Subscriber,subscriber_id).format)
138
def setDisable(self, member, flag):
139
"""Disable/enable delivery based on mm_cfg.DisableDelivery"""
140
command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
142
syslog('info', 'DlistUtils(setDisable):Executing query:\n%s\n Member whoes suppress value is to be found \n %s', command,member)
143
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))
144
oldval = [(subscriber.suppress) for subscriber in result]
146
syslog('info','the value of oldval is %s:',oldval)
149
newval = oldval | 1 # Disable delivery
151
newval = oldval & ~1 # Enable delivery
153
syslog('info','the value of newval is %s:',newval)
154
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"
156
syslog('info', 'DlistUtils(setDisable):Executing query:\n%s', command)
157
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(suppress = newval)
160
def setDigest(self, member, flag):
161
"""Disable/enable delivery based on user digest status"""
163
command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
165
syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
166
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))
167
oldval = [(subscriber.suppress) for subscriber in result]
170
syslog('info','value of oldval %s:',oldval)
173
newval = oldval | 2 # Suppress delivery (in favor of digests)
175
newval = oldval & ~2 # Enable delivery (instead of digests)
177
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"
179
syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
180
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(suppress = newval)
183
def changePreference(self, member, preference):
184
"""Change a user's default preference for new threads."""
185
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(preference = preference)\n"
187
syslog('info', 'DlistUtils(changePreference):Executing query:\n%s', command)
188
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(preference = preference)
191
def changeFormat(self, member, format):
192
"""Change a user's preferred delivery format (plain text and/or html)"""
193
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(format = format)\n"
195
syslog('info', 'DlistUtils(changeFormat):Executing query:\n%s', command)
196
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(format = format)
198
### We have to watch out for a tricky case (that actually happened):
199
### User foo@bar.com changes her address to something already in the
200
### subscriber database (possibly deleted).
202
def changeAddress(self, oldaddr, newaddr):
203
"""Change email address in SQL database"""
205
#syslog('info', "Changing email address on %s from '%s' to '%s'", self.mlist.internal_name(), oldaddr, newaddr)
206
## Check if newaddr is in sql database
207
num_matches = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).count()
210
#syslog('error', 'Multiple users with same key (%s): %s', self.mlist.internal_name(), newaddr)
213
self.mergeSubscribers(oldaddr, newaddr)
215
command = "num_matches = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,'utf-8')).count()\nself.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,'utf-8')).set(mailman_key = unicode(newaddr,'utf-8'))\nself.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,'utf-8')).set(deleted = False)\n"
217
syslog('info', 'DlistUtils(changeAddress):Executing query:\n%s', command)
218
self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8")).set(mailman_key = unicode(newaddr,"utf-8"))
220
self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).set(deleted = False)
223
def unsubscribeFromList(self, key):
224
"""Indicate that a user has unsubscribed by setting the deleted flag in the subscriber table."""
226
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8')).set(deleted = True)\nself.store.find(Alias,Alias.subscriber_id == subscriber_id).remove()\n"
228
syslog('info', 'DlisUtils(unsubscribeFromList):Executing query:\n%s', command)
229
self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = True)
230
# get the subscriber id from the mailman_key & delete all aliases
231
subscriber_id = self.getSubscriber_id_raw(key)
233
if subscriber_id == None:
234
syslog('error', "DlistUtils.unsubscribeFromList called with '%s', but it can't be found in the SQL database", key)
236
self.store.find(Alias,Alias.subscriber_id == subscriber_id).remove()
239
def subscribeToList(self, key):
240
"""Add a member to the subscriber database, or change the record from deleted if it was already present."""
242
command = "count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8'))).count()\n"
244
syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
245
# First see if member is subscribed with deleted field = false
246
count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8"))).count()
249
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8')).set(deleted = False)"
251
syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
252
self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = False)
254
# format is text-only (1) by default
255
command = "subscriber = self.store.add(Subscriber())\nsubscriber.mailman_key = unicode(key,'utf-8')\nsubscriber.preference = 1\nsubscriber.deleted = False\nsubscriber.format = 1\nsubscriber.suppress = 0"
257
syslog('info', 'DlistUtils(subscribeToList)Executing query:\n%s', command)
258
self.mailman_key = unicode(key,"utf-8")
266
def mergeSubscribers(self, oldaddr, newaddr):
267
# use the original subscriber id as the ID going forward
269
syslog('info', 'Executing commands of mergeSubscribers:')
270
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8"))
271
new_id = [(subscriber.subscriber_id) for subscriber in result]
274
syslog('info', 'the value of new_id: %s', new_id)
275
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8"))
276
obsolete_id = [(subscriber.subscriber_id) for subscriber in result]
277
obsolete_id = obsolete_id[0]
279
syslog('info', 'the value of obsolete_id: %s', obsolete_id)
281
self.store.find(Message,Message.sender_id == obsolete_id).set(sender_id = new_id)
282
self.store.find(Override,Override.subscriber_id == obsolete_id).set(subscriber_id = new_id)
283
self.store.find(Alias,Alias.subscriber_id == obsolete_id).set(subscriber_id = new_id)
284
self.store.find(Subscriber,Subscriber.subscriber_id == obsolete_id).remove()
62
__storm_table__ = "subscriber"
63
subscriber_id = Int(primary = True,default = AutoReload)
64
mailman_key = Unicode()
65
preference = Int(default = 1)
66
format = Int(default = 3)
67
deleted = Bool(default = False)
68
suppress = Int(default = 0)
70
def __init__(self,mlist):
72
self.database = getConn(mlist)
75
def getSubscriber_id_raw(self, addr):
79
command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),'utf-8'))\na = [(subscriber.subscriber_id) for subscriber in result]\n"
81
syslog('info', "DlistUtils:(getSubscriber_id_raw)executing query:\n%s", command)
82
#storm recognizes unicode only
83
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),"utf-8"))
84
a = [(subscriber.subscriber_id) for subscriber in result]
86
syslog('info', 'value of a is: %s\n', a)
88
#The ResultSet "a" obtained in above storm command and later on in similar ones is always a list
94
def getSubscriber_id_raw_or_die(self, addr):
95
result = self.getSubscriber_id_raw(addr)
97
#syslog('error', 'getSubscriber_id_raw_or_die /msg nickserv register <your-password> <your-email>unable to find address /%s/ for mailing list /%s/', addr, self.mlist.internal_name())
98
raise ErrorsDlist.InternalError
102
def getSubscriber_id(self, msg, msgdata, safe=0, loose=0):
103
"""Returns the subscriber_id of the sender of a message and sets the 'subscriber_id' field in the msg object."""
104
fromAddr = email.Utils.parseaddr(msg['From'])[1]
106
subscriber_id = msgdata['subscriber_id']
109
subscriber_id = self.getSubscriber_id_raw(fromAddr)
110
if subscriber_id == None:
111
alias = Alias(self.mlist)
112
bestAddr = alias.canonicalize_sender(msg.get_senders())
113
subscriber_id = self.getSubscriber_id_raw(bestAddr)
114
if subscriber_id == None:
116
#syslog('info', "DlistUtils.getSubscriber_id: subscriber_id is None and safe is %d", safe)
118
# This could happen if a non-member is given permission to post
122
#syslog('info', "Raising ErrorsDlist.NotMemberError")
123
raise ErrorsDlist.NotMemberError("Your request could not be processed because %s is not subscribed to %s. Perhaps you are subscribed with a different email address, which forwards to %s. If so, please log into %s and add this as an 'other incoming email address'." % (fromAddr, self.mlist.internal_name(), fromAddr, self.mlist.GetScriptURL('options', 1)))
125
syslog('info', 'subscriber_id = %d\n', subscriber_id)
126
msgdata['subscriber_id'] = subscriber_id
130
def get_format(self, subscriber_id):
131
command = "return int(self.store.get(Subscriber,subscriber_id).format)\n"
133
syslog('info', 'DlistUtils(get_format):Executing query:\n%s', command)
134
return int(self.store.get(Subscriber,subscriber_id).format)
137
def setDisable(self, member, flag):
138
"""Disable/enable delivery based on mm_cfg.DisableDelivery"""
139
command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
141
syslog('info', 'DlistUtils(setDisable):Executing query:\n%s\n Member whoes suppress value is to be found \n %s', command,member)
142
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))
143
oldval = [(subscriber.suppress) for subscriber in result]
146
syslog('info','oldval is an empty list.\nThis can happen either because of\n 1)Permission issues (Do a: bin/check_perms)\n 2)Inconsistency between database and pickle files (A user is in the database but not in pickle files or vice versa,Do a bin/find_problems.py)')
149
syslog('info','the value of oldval is %s:',oldval)
152
newval = oldval | 1 # Disable delivery
154
newval = oldval & ~1 # Enable delivery
156
syslog('info','the value of newval is %s:',newval)
157
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"
159
syslog('info', 'DlistUtils(setDisable):Executing query:\n%s', command)
160
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(suppress = newval)
163
def setDigest(self, member, flag):
164
"""Disable/enable delivery based on user digest status"""
166
command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
168
syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
169
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))
170
oldval = [(subscriber.suppress) for subscriber in result]
173
syslog('info','oldval is an empty list.\nThis can happen either because of\n 1)permission issues (Do a: bin/check_perms)\n 2)Inconsistency between database and pickle files (A user is in the database but not in pickle files or vice versa,Do a bin/find_problems.py)')
176
syslog('info','value of oldval %s:',oldval)
179
newval = oldval | 2 # Suppress delivery (in favor of digests)
181
newval = oldval & ~2 # Enable delivery (instead of digests)
183
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"
185
syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
186
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(suppress = newval)
189
def changePreference(self, member, preference):
190
"""Change a user's default preference for new threads."""
191
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(preference = preference)\n"
193
syslog('info', 'DlistUtils(changePreference):Executing query:\n%s', command)
194
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(preference = preference)
197
def changeFormat(self, member, format):
198
"""Change a user's preferred delivery format (plain text and/or html)"""
199
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(format = format)\n"
201
syslog('info', 'DlistUtils(changeFormat):Executing query:\n%s', command)
202
self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(format = format)
204
### We have to watch out for a tricky case (that actually happened):
205
### User foo@bar.com changes her address to something already in the
206
### subscriber database (possibly deleted).
208
def changeAddress(self, oldaddr, newaddr):
209
"""Change email address in SQL database"""
211
#syslog('info', "Changing email address on %s from '%s' to '%s'", self.mlist.internal_name(), oldaddr, newaddr)
212
## Check if newaddr is in sql database
213
num_matches = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).count()
216
#syslog('error', 'Multiple users with same key (%s): %s', self.mlist.internal_name(), newaddr)
219
self.mergeSubscribers(oldaddr, newaddr)
221
command = "num_matches = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,'utf-8')).count()\nself.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,'utf-8')).set(mailman_key = unicode(newaddr,'utf-8'))\nself.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,'utf-8')).set(deleted = False)\n"
223
syslog('info', 'DlistUtils(changeAddress):Executing query:\n%s', command)
224
self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8")).set(mailman_key = unicode(newaddr,"utf-8"))
226
self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).set(deleted = False)
229
def unsubscribeFromList(self, key):
230
"""Indicate that a user has unsubscribed by setting the deleted flag in the subscriber table."""
232
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8')).set(deleted = True)\nself.store.find(Alias,Alias.subscriber_id == subscriber_id).remove()\n"
234
syslog('info', 'DlisUtils(unsubscribeFromList):Executing query:\n%s', command)
235
self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = True)
236
# get the subscriber id from the mailman_key & delete all aliases
237
subscriber_id = self.getSubscriber_id_raw(key)
239
if subscriber_id == None:
240
syslog('error', "DlistUtils.unsubscribeFromList called with '%s', but it can't be found in the SQL database", key)
242
self.store.find(Alias,Alias.subscriber_id == subscriber_id).remove()
245
def subscribeToList(self, key):
246
"""Add a member to the subscriber database, or change the record from deleted if it was already present."""
248
command = "count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8'))).count()\n"
250
syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
251
# First see if member is subscribed with deleted field = false
252
count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8"))).count()
255
command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8')).set(deleted = False)"
257
syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
258
self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = False)
260
# format is text-only (1) by default
261
command = "subscriber = self.store.add(Subscriber())\nsubscriber.mailman_key = unicode(key,'utf-8')\nsubscriber.preference = 1\nsubscriber.deleted = False\nsubscriber.format = 1\nsubscriber.suppress = 0"
263
syslog('info', 'DlistUtils(subscribeToList)Executing query:\n%s', command)
264
self.mailman_key = unicode(key,"utf-8")
272
def mergeSubscribers(self, oldaddr, newaddr):
273
# use the original subscriber id as the ID going forward
275
syslog('info', 'Executing commands of mergeSubscribers:')
276
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8"))
277
new_id = [(subscriber.subscriber_id) for subscriber in result]
280
syslog('info', 'the value of new_id: %s', new_id)
281
result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8"))
282
obsolete_id = [(subscriber.subscriber_id) for subscriber in result]
283
obsolete_id = obsolete_id[0]
285
syslog('info', 'the value of obsolete_id: %s', obsolete_id)
287
self.store.find(Message,Message.sender_id == obsolete_id).set(sender_id = new_id)
288
self.store.find(Override,Override.subscriber_id == obsolete_id).set(subscriber_id = new_id)
289
self.store.find(Alias,Alias.subscriber_id == obsolete_id).set(subscriber_id = new_id)
290
self.store.find(Subscriber,Subscriber.subscriber_id == obsolete_id).remove()
286
292
class Message(object):
287
__storm_table__ = "message"
288
message_id = Int(primary = True,default = AutoReload)
290
subscriber = Reference(sender_id,Subscriber.subscriber_id)
294
def __init__(self,mlist):
296
self.database = getConn(mlist)
299
def createMessage(self, msg, msgdata):
300
"""Create a new message (in the database), returning its id."""
301
subscriber = Subscriber(self.mlist)
302
senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
303
# extract subject and escape quotes
304
subject = msg['Subject'].encode().replace("'", "''")
307
threadID = msgdata['thread_id']
311
command = "message = self.store.add(Message())\nmessage.sender_id = senderID\nmessage.thread_id = threadID\nmessage.subject = unicode(subject,'utf-8')\nmessageID = self.store.find(Message).max(Message.message_id)\n"
313
syslog('info','DlistUtils:(createMessage)executing query:\n%s',command)
314
#message_id has autoreload set,its value will be serially updated in database
315
self.sender_id = senderID
316
self.thread_id = threadID
317
self.subject = unicode(subject,"utf-8")
319
messageID = self.store.find(Message).max(Message.message_id)
321
syslog('info','Result of query(messageID) is: %s\n',messageID)
293
__storm_table__ = "message"
294
message_id = Int(primary = True,default = AutoReload)
296
subscriber = Reference(sender_id,Subscriber.subscriber_id)
300
def __init__(self,mlist):
302
self.database = getConn(mlist)
305
def createMessage(self, msg, msgdata):
306
"""Create a new message (in the database), returning its id."""
307
subscriber = Subscriber(self.mlist)
308
senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
309
# extract subject and escape quotes
310
subject = msg['Subject'].encode().replace("'", "''")
312
threadID = msgdata['thread_id']
315
command = "message = self.store.add(Message())\nmessage.sender_id = senderID\nmessage.thread_id = threadID\nmessage.subject = unicode(subject,'utf-8')\nmessageID = self.store.find(Message).max(Message.message_id)\n"
317
syslog('info','DlistUtils:(createMessage)executing query:\n%s',command)
318
#message_id has autoreload set,its value will be serially updated in database
319
self.sender_id = senderID
320
self.thread_id = threadID
321
self.subject = unicode(subject,"utf-8")
323
messageID = self.store.find(Message).max(Message.message_id)
325
syslog('info','Result of query(messageID) is: %s\n',messageID)
325
328
class Thread(object):
326
__storm_table__ = "thread"
327
thread_id = Int(primary = True,default = AutoReload)
328
thread_name = Unicode()
329
base_message_id = Int()
330
message = Reference(base_message_id,Message.message_id)
331
status = Int(default = 0)
334
def __init__(self,mlist):
336
self.database = getConn(mlist)
339
def createThread(self, msg, msgdata, threadBase):
340
"""Create a new thread, returning its unique id, name."""
341
message = Message(self.mlist)
342
subscriber = Subscriber(self.mlist)
343
msgdata['message_id'] = message.createMessage(msg, msgdata)
344
senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
346
command = "thread = self.store.add(Thread())\nthread.base_message_id = msgdata['message_id']\nthreadID = self.store.find(Thread).max(Thread.thread_id)\nself.store.find(Message,Message.message_id == msgdata['message_id']).set(thread_id = threadID)\n"
348
syslog('info','DlistUtils:(createThread)executing query:\n%s',command)
349
self.base_message_id = msgdata['message_id']
351
threadID = self.store.find(Thread).max(Thread.thread_id)
352
self.store.find(Message,Message.message_id == msgdata['message_id']).set(thread_id = threadID)
354
## Choose a unique name for the thread
355
# Try to get the name from the to-line, e.g., listname+new+name@hostname
357
threadBase = self.alphanumericOnly(threadBase).lower()
358
if threadBase in lousyThreadNames or not len(threadBase):
361
# If a name wasn't explicitly specified, try to get one from the subject
363
# No thread name was specified -- try to get from subject line.
364
# If none in subject line, the threadID will be returned
365
threadBase = self.subjectToName(msg['Subject'].encode(), threadID)
367
# Make sure the thread can fit in the field (char(16))
368
threadBase = threadBase[:13]
369
command = "num = self.store.find(Thread,Thread.thread_name == unicode(threadBase,'utf-8')).count()\nself.store.find(Thread,Thread.thread_id == threadID).set(thread_name = threadName)\n"
371
syslog('info','DlistUtils:(createThread)executing query:\n%s',command)
372
# If the threadBase is not unique, make threadBase unique by appending a number
373
num = self.store.find(Thread,Thread.thread_name == unicode(threadBase,"utf-8")).count()
376
threadName = unicode(threadBase,"utf-8")
378
threadName = unicode(threadBase + str(num+1),"utf-8" )
380
self.store.find(Thread,Thread.thread_id == threadID).set(thread_name = threadName)
382
return (threadID, threadName.encode("utf-8"))
384
def email_recepients(self, msg, msgdata, lists, pref):
386
"""Finding the email addresses of matching subscribers from lists,and using the list info to email everyone"""
387
returnList = GetEmailAddress(self.mlist, lists)
388
msgdata['recips'] = returnList
389
self.setFooterText(msg, msgdata, pref)
390
dq = get_switchboard(mm_cfg.DLISTQUEUE_DIR)
391
dq.enqueue(msg, msgdata, listname=self.mlist.internal_name())
394
def newThread(self, msg, msgdata, threadBase=None):
395
"""Starts a new thread, including creating and enqueueing the initial messages"""
396
id, name = self.createThread(msg, msgdata, threadBase)
397
msgdata['thread_id'] = id
398
msgdata['thread_name'] = name
400
# Delete any other 'To' headings
402
msg['To'] = '%s+%s@%s' % (self.mlist.internal_name(),
404
self.mlist.host_name)
406
# different emails for different prefs, so we need to queue separately
408
#For condition where preference = True
410
command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"
412
syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = true\n', command)
414
#For condition where preference = False
416
command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"
418
syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = false\n', command)
420
#Execute a SELECT statement, to find the list of matching subscribers.
421
result_new_sql = self.store.find(Subscriber,And(Subscriber.preference == pref,Subscriber.deleted == False,Subscriber.suppress == 0))
422
lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]
424
syslog('info', 'value of lists: %s\n', lists)
425
self.email_recepients(msg, msgdata, lists, pref)
427
# Make original message go to nobody (but be archived)
428
msgdata['recips'] = []
431
def continueThread(self, msg, msgdata, threadReference):
432
"""Continue an existing thread, no return value."""
433
subscriber = Subscriber(self.mlist)
434
message = Message(self.mlist)
435
senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
436
msgdata['message_id'] = message.createMessage(msg, msgdata)
440
# email selected people
441
#Execute a SELECT statement, to find the list of matching subscribers.
442
command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_continue_sql]\n"
444
syslog('info', 'DlistUtils:(continueThread) executing query:\n%s', command)
446
result_pref_one = self.store.find((Subscriber,Override),And(Override.subscriber_id == Subscriber.subscriber_id,Override.thread_id == msgdata['thread_id'],Override.preference == 0))
447
lists_pref_one = [(override.subscriber_id) for (subscriber,override) in result_pref_one]
449
result_pref_zero = self.store.find((Subscriber,Override),And(Override.subscriber_id == Subscriber.subscriber_id,Override.thread_id == msgdata['thread_id'],Override.preference == 1))
450
lists_pref_zero = [(override.subscriber_id) for (subscriber,override) in result_pref_zero]
452
result_continue_sql = self.store.find(Subscriber,And(Subscriber.deleted == False,Subscriber.suppress == 0,Or(And(Subscriber.preference == 1,lists_pref_one == []),And(Subscriber.preference == 0,lists_pref_zero != []))))
453
lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_continue_sql]
455
self.email_recepients(msg, msgdata, lists, pref)
457
# Make original message go to nobody (but be archived)
458
msgdata['recips'] = []
461
def threadIDandName(self, threadReference):
462
"""Given thread_id or thread_name, determine the other, returning (thread_id, thread_name)"""
464
thread_id = int(thread_reference)
466
command = "result = self.store.find(Thread,Thread.thread_id == thread_id)\nthread_name = [(thread.thread_name) for thread in result]\nthread_name = thread_name.encode('utf-8')\n"
468
syslog('info', 'DlistUtils(threadIDandName)Executing query:\n%s', command)
469
result = self.store.find(Thread,Thread.thread_id == thread_id)
470
thread_name = [(thread.thread_name) for thread in result]
471
thread_name = thread_name.encode('utf-8')
473
raise NonexistentThreadRequest("Your message could not be sent because you specified a nonexistent conversation (%d). Perhaps you meant to start a new conversation, which you can do by addressing your message to %s+new@%s" % (thread_id, self.mlist.real_name, self.mlist.host_name))
475
thread_name = threadReference.lower()
477
syslog('info', 'thread_name = %s\n', thread_name)
478
command = "result = self.store.find(Thread,Thread.thread_name == unicode(thread_name,'utf-8'))\nthread_id = [(thread.thread_id) for thread in result]\n"
480
syslog('info', 'DlistUtils(threadIDandName)Executing query:\n%s', command)
481
result = self.store.find(Thread,Thread.thread_name == unicode(thread_name,'utf-8'))
482
thread_id = [(thread.thread_id) for thread in result]
483
thread_id = thread_id[0]#to convert a list to int
485
syslog('info', 'thread_id: %s\n', thread_id)
487
if thread_id == None:
488
raise NonexistentThreadRequest("Your message could not be sent because you addressed it to a nonexistent conversation (%s). Perhaps you meant to start a new conversation named %s, which you can do by addressing your message to %s+new+%s@%s" % (thread_name, thread_name, self.mlist.real_name, thread_name, self.mlist.host_name))
490
return (thread_id, thread_name)
492
def getThreadAddress(msgdata):
493
"""The list address for any given thread."""
494
return "%s+%s@%s" % (self.mlist.internal_name(), msgdata['thread_name'], self.mlist.host_name)
496
def subscribeToThread(self, msg, msgdata):
497
"""Subscribe the sender of a message to a given thread."""
498
override = Override(self.mlist)
499
override.override(msg, msgdata, 1)
501
def unsubscribeFromThread(self, msg, msgdata):
502
"""Unubscribe the sender of a message from a given thread."""
503
override = Override(self.mlist)
504
override.override(msg, msgdata, 0)
506
def alphanumericOnly(self,s):
507
"""Filter any non-letter characters from a string"""
508
result = [letter for letter in s if letter in string.ascii_letters or letter in string.digits]
509
return string.join(result, '')
511
def subjectToName(self,subject, threadID):
512
"""Return a lower-case name for a new thread based on the subject, if present, or on the threadID"""
518
subjectWords = [self.alphanumericOnly(w) for w in subject.split()]
520
# Choose the longest word of 4 or more characters
523
for word in subjectWords and word not in lousyThreadNames:#TODO and word not in....was not there before
524
if len(word) > maxLength:
526
maxLength = len(word)
528
result = maxWord.lower()
531
# Choose the first word that's not a stop word or lousy thread name
532
stopWords = ["a", "an", "the", "of", "re", "you", "i", "no", "not", "do", "for"]
533
for word in subjectWords:
534
if word not in stopWords and word not in lousyThreadNames:
535
result = word.lower()
539
# If no other candidate, just return the first subject word
540
result = subjectWords[0].lower() + str(threadID)#TODO str(threadID) was not before
544
def setFooterText(self, msg, msgdata, preference):
545
msgdata['dlists_preference'] = preference
546
thread_name = msgdata['thread_name']
547
thread_id = msgdata['thread_id']
548
override = Override(self.mlist)
549
web_addr = override.overrideURL(self.mlist.internal_name(), self.mlist.host_name, thread_id, not preference)
551
subscribe_string = "unsubscribe"
554
subscribe_string = "subscribe"
556
email_addr = '%s+%s+%s@%s' % (self.mlist.internal_name(), thread_name, subscribe_string, self.mlist.host_name)
558
#syslog('info', "msg['Subject'] = /%s/", msg['Subject'])
559
subject = urllib.quote(msg['Subject'].encode())
560
post_addr = "%s+%s@%s" % (self.mlist.internal_name(), thread_name, self.mlist.host_name)
561
post_addr_with_subject = "%s?Subject=%s" % (post_addr, subject)
563
#syslog('info', 'post_addr_with_subject = %s', post_addr)
565
# Used in Handlers/ToDigest.py
566
msgdata['contribute'] = "To contribute to this conversation, send " \
567
"your message to <%s+%s@%s>\n" % \
568
(self.mlist.internal_name(), msgdata['thread_name'], self.mlist.host_name)
571
msgdata['contribute-html'] = 'To contribute to this conversation, send your message to <a href="mailto:%s">%s</a>\n' % (post_addr, post_addr)
572
msgdata['footer-text'] = '\n\nTo %s %s this conversation, send email to <%s> or visit <%s>\nTo contribute to this conversation, use your mailer\'s reply-all or reply-group command or send your message to %s\nTo start a new conversation, send email to <%s+new@%s>\nTo unsubscribe entirely from %s, send email to <%s-request@%s> with subject unsubscribe.' % (subscribe_string, preposition, email_addr, web_addr, post_addr, self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.internal_name(), self.mlist.host_name)
574
#syslog('info', 'footer-text = /%s/', msgdata['footer-text'])
575
msgdata['footer-html'] = '<br>To %s %s this conversation, send email to <a href="mailto:%s">%s</a> or visit <a href="%s">%s</a>.<br>To contribute to this conversation, use your mailer\'s reply-all or reply-group command or send your message to <a href="mailto:%s?subject=%s">%s</a>.<br>To start a new conversation, send email to <a href="mailto:%s+new@%s">%s+new@%s</a><br>To unsubscribe entirely from %s, send mail to <a href="mailto:%s-request@%s?subject=unsubscribe">%s-request@%s</a> with subject unsubscribe.' % (subscribe_string, preposition, email_addr, email_addr, web_addr, web_addr, post_addr, subject, post_addr, self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.host_name)
329
__storm_table__ = "thread"
330
thread_id = Int(primary = True,default = AutoReload)
331
thread_name = Unicode()
332
base_message_id = Int()
333
message = Reference(base_message_id,Message.message_id)
334
status = Int(default = 0)
337
def __init__(self,mlist):
339
self.database = getConn(mlist)
342
def createThread(self, msg, msgdata, threadBase):
343
"""Create a new thread, returning its unique id, name."""
344
message = Message(self.mlist)
345
subscriber = Subscriber(self.mlist)
346
msgdata['message_id'] = message.createMessage(msg, msgdata)
347
senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
348
command = "thread = self.store.add(Thread())\nthread.base_message_id = msgdata['message_id']\nthreadID = self.store.find(Thread).max(Thread.thread_id)\nself.store.find(Message,Message.message_id == msgdata['message_id']).set(thread_id = threadID)\n"
350
syslog('info','DlistUtils:(createThread)executing query:\n%s',command)
351
self.base_message_id = msgdata['message_id']
353
threadID = self.store.find(Thread).max(Thread.thread_id)
354
self.store.find(Message,Message.message_id == msgdata['message_id']).set(thread_id = threadID)
356
## Choose a unique name for the thread
357
# Try to get the name from the to-line, e.g., listname+new+name@hostname
359
threadBase = self.alphanumericOnly(threadBase).lower()
360
if threadBase in lousyThreadNames or not len(threadBase):
363
# If a name wasn't explicitly specified, try to get one from the subject
365
# No thread name was specified -- try to get from subject line.
366
# If none in subject line, the threadID will be returned
367
threadBase = self.subjectToName(msg['Subject'].encode(), threadID)
369
# Make sure the thread can fit in the field (char(16))
370
threadBase = threadBase[:13]
371
command = "num = self.store.find(Thread,Thread.thread_name == unicode(threadBase,'utf-8')).count()\nself.store.find(Thread,Thread.thread_id == threadID).set(thread_name = threadName)\n"
373
syslog('info','DlistUtils:(createThread)executing query:\n%s',command)
374
# If the threadBase is not unique, make threadBase unique by appending a number
375
num = self.store.find(Thread,Thread.thread_name == unicode(threadBase,"utf-8")).count()
378
threadName = unicode(threadBase,"utf-8")
380
threadName = unicode(threadBase + str(num+1),"utf-8" )
382
self.store.find(Thread,Thread.thread_id == threadID).set(thread_name = threadName)
384
return (threadID, threadName.encode("utf-8"))
386
def email_recepients(self, msg, msgdata, lists, pref):
387
"""Finding the email addresses of matching subscribers from lists,and using the list info to email everyone"""
388
returnList = GetEmailAddress(self.mlist, lists)
389
msgdata['recips'] = returnList
390
self.setFooterText(msg, msgdata, pref)
391
dq = get_switchboard(mm_cfg.DLISTQUEUE_DIR)
392
dq.enqueue(msg, msgdata, listname=self.mlist.internal_name())
395
def newThread(self, msg, msgdata, threadBase=None):
396
"""Starts a new thread, including creating and enqueueing the initial messages"""
397
id, name = self.createThread(msg, msgdata, threadBase)
398
msgdata['thread_id'] = id
399
msgdata['thread_name'] = name
401
# Delete any other 'To' headings
403
msg['To'] = '%s+%s@%s' % (self.mlist.internal_name(),
405
self.mlist.host_name)
407
# different emails for different prefs, so we need to queue separately
409
#For condition where preference = True
411
command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"
413
syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = true\n', command)
415
#For condition where preference = False
417
command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"
419
syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = false\n', command)
421
#Execute a SELECT statement, to find the list of matching subscribers.
422
result_new_sql = self.store.find(Subscriber,And(Subscriber.preference == pref,Subscriber.deleted == False,Subscriber.suppress == 0))
423
lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]
425
syslog('info', 'value of lists: %s\n', lists)
426
self.email_recepients(msg, msgdata, lists, pref)
428
# Make original message go to nobody (but be archived)
429
msgdata['recips'] = []
432
def continueThread(self, msg, msgdata, threadReference):
433
"""Continue an existing thread, no return value."""
434
subscriber = Subscriber(self.mlist)
435
message = Message(self.mlist)
436
senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
437
msgdata['message_id'] = message.createMessage(msg, msgdata)
441
# email selected people
442
#Execute a SELECT statement, to find the list of matching subscribers.
443
command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_continue_sql]\n"
445
syslog('info', 'DlistUtils:(continueThread) executing query:\n%s', command)
447
result_pref_one = self.store.find((Subscriber,Override),And(Override.subscriber_id == Subscriber.subscriber_id,Override.thread_id == msgdata['thread_id'],Override.preference == 0))
448
lists_pref_one = [(override.subscriber_id) for (subscriber,override) in result_pref_one]
450
result_pref_zero = self.store.find((Subscriber,Override),And(Override.subscriber_id == Subscriber.subscriber_id,Override.thread_id == msgdata['thread_id'],Override.preference == 1))
451
lists_pref_zero = [(override.subscriber_id) for (subscriber,override) in result_pref_zero]
453
result_continue_sql = self.store.find(Subscriber,And(Subscriber.deleted == False,Subscriber.suppress == 0,Or(And(Subscriber.preference == 1,lists_pref_one == []),And(Subscriber.preference == 0,lists_pref_zero != []))))
454
lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_continue_sql]
456
self.email_recepients(msg, msgdata, lists, pref)
458
# Make original message go to nobody (but be archived)
459
msgdata['recips'] = []
462
def threadIDandName(self, threadReference):
463
"""Given thread_id or thread_name, determine the other, returning (thread_id, thread_name)"""
465
thread_id = int(thread_reference)
467
command = "result = self.store.find(Thread,Thread.thread_id == thread_id)\nthread_name = [(thread.thread_name) for thread in result]\nthread_name = thread_name.encode('utf-8')\n"
469
syslog('info', 'DlistUtils(threadIDandName)Executing query:\n%s', command)
470
result = self.store.find(Thread,Thread.thread_id == thread_id)
471
thread_name = [(thread.thread_name) for thread in result]
472
thread_name = thread_name.encode('utf-8')
474
raise NonexistentThreadRequest("Your message could not be sent because you specified a nonexistent conversation (%d). Perhaps you meant to start a new conversation, which you can do by addressing your message to %s+new@%s" % (thread_id, self.mlist.real_name, self.mlist.host_name))
476
thread_name = threadReference.lower()
478
syslog('info', 'thread_name = %s\n', thread_name)
479
command = "result = self.store.find(Thread,Thread.thread_name == unicode(thread_name,'utf-8'))\nthread_id = [(thread.thread_id) for thread in result]\n"
481
syslog('info', 'DlistUtils(threadIDandName)Executing query:\n%s', command)
482
result = self.store.find(Thread,Thread.thread_name == unicode(thread_name,'utf-8'))
483
thread_id = [(thread.thread_id) for thread in result]
484
thread_id = thread_id[0]#to convert a list to int
486
syslog('info', 'thread_id: %s\n', thread_id)
488
if thread_id == None:
489
raise NonexistentThreadRequest("Your message could not be sent because you addressed it to a nonexistent conversation (%s). Perhaps you meant to start a new conversation named %s, which you can do by addressing your message to %s+new+%s@%s" % (thread_name, thread_name, self.mlist.real_name, thread_name, self.mlist.host_name))
491
return (thread_id, thread_name)
493
def getThreadAddress(msgdata):
494
"""The list address for any given thread."""
495
return "%s+%s@%s" % (self.mlist.internal_name(), msgdata['thread_name'], self.mlist.host_name)
497
def subscribeToThread(self, msg, msgdata):
498
"""Subscribe the sender of a message to a given thread."""
499
override = Override(self.mlist)
500
override.override(msg, msgdata, 1)
502
def unsubscribeFromThread(self, msg, msgdata):
503
"""Unubscribe the sender of a message from a given thread."""
504
override = Override(self.mlist)
505
override.override(msg, msgdata, 0)
507
def alphanumericOnly(self,s):
508
"""Filter any non-letter characters from a string"""
509
result = [letter for letter in s if letter in string.ascii_letters or letter in string.digits]
510
return string.join(result, '')
512
def subjectToName(self,subject, threadID):
513
"""Return a lower-case name for a new thread based on the subject, if present, or on the threadID"""
519
subjectWords = [self.alphanumericOnly(w) for w in subject.split()]
521
# Choose the longest word of 4 or more characters
524
for word in subjectWords:
525
if len(word) > maxLength and word not in lousyThreadNames:
527
maxLength = len(word)
529
result = maxWord.lower()
532
# Choose the first word that's not a stop word or lousy thread name
533
stopWords = ["a", "an", "the", "of", "re", "you", "i", "no", "not", "do", "for"]
534
for word in subjectWords:
535
if word not in stopWords and word not in lousyThreadNames:
536
result = word.lower()
540
# If no other candidate, just return the first subject word
541
result = subjectWords[0].lower() + str(threadID)
545
def setFooterText(self, msg, msgdata, preference):
546
msgdata['dlists_preference'] = preference
547
thread_name = msgdata['thread_name']
548
thread_id = msgdata['thread_id']
549
override = Override(self.mlist)
550
web_addr = override.overrideURL(self.mlist.internal_name(), self.mlist.host_name, thread_id, not preference)
552
subscribe_string = "unsubscribe"
555
subscribe_string = "subscribe"
557
email_addr = '%s+%s+%s@%s' % (self.mlist.internal_name(), thread_name, subscribe_string, self.mlist.host_name)
559
#syslog('info', "msg['Subject'] = /%s/", msg['Subject'])
560
subject = urllib.quote(msg['Subject'].encode())
561
post_addr = "%s+%s@%s" % (self.mlist.internal_name(), thread_name, self.mlist.host_name)
562
post_addr_with_subject = "%s?Subject=%s" % (post_addr, subject)
564
#syslog('info', 'post_addr_with_subject = %s', post_addr)
566
# Used in Handlers/ToDigest.py
567
msgdata['contribute'] = "To contribute to this conversation, send " \
568
"your message to <%s+%s@%s>\n" % \
569
(self.mlist.internal_name(), msgdata['thread_name'], self.mlist.host_name)
572
msgdata['contribute-html'] = 'To contribute to this conversation, send your message to <a href="mailto:%s">%s</a>\n' % (post_addr, post_addr)
573
msgdata['footer-text'] = '\n\nTo %s %s this conversation, send email to <%s> or visit <%s>\nTo contribute to this conversation, use your mailer\'s reply-all or reply-group command or send your message to %s\nTo start a new conversation, send email to <%s+new@%s>\nTo unsubscribe entirely from %s, send email to <%s-request@%s> with subject unsubscribe.' % (subscribe_string, preposition, email_addr, web_addr, post_addr, self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.internal_name(), self.mlist.host_name)
575
#syslog('info', 'footer-text = /%s/', msgdata['footer-text'])
576
msgdata['footer-html'] = '<br>To %s %s this conversation, send email to <a href="mailto:%s">%s</a> or visit <a href="%s">%s</a>.<br>To contribute to this conversation, use your mailer\'s reply-all or reply-group command or send your message to <a href="mailto:%s?subject=%s">%s</a>.<br>To start a new conversation, send email to <a href="mailto:%s+new@%s">%s+new@%s</a><br>To unsubscribe entirely from %s, send mail to <a href="mailto:%s-request@%s?subject=unsubscribe">%s-request@%s</a> with subject unsubscribe.' % (subscribe_string, preposition, email_addr, email_addr, web_addr, web_addr, post_addr, subject, post_addr, self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.internal_name(), self.mlist.host_name, self.mlist.internal_name(), self.mlist.host_name)
577
578
class Alias(object):
578
__storm_table__ = "alias"
579
pseudonym = Unicode(primary = True)
580
subscriber_id = Int()
581
subscriber = Reference(subscriber_id,Subscriber.subscriber_id)
583
def __init__(self,mlist):
585
self.database = getConn(mlist)
588
def canonicalize_sender(self, aliases):
589
"""Execute a SELECT statement, returning the email addresses of matching subscribers."""
591
command = "result = self.store.find((Subscriber,Alias),Alias.pseudonym = alias,Subscriber.subscriber_id = Alias.subscriber_id)\nlists = [(subscriber.mailman_key) for subscriber,alias in result]\n"
593
syslog('info', 'DlistUtils(canonicalize_sender)Executing query:\n%s', command)
595
for alias in aliases:
597
#syslog('info', 'Checking if <%s> is an alias', alias)
598
result = self.store.find((Subscriber,Alias),And(Alias.pseudonym == unicode(alias,'utf-8'),Subscriber.subscriber_id == Alias.subscriber_id))
599
lists = [(subscriber.mailman_key.encode('utf-8')) for (subscriber,alias) in result]
601
returnList = GetEmailAddress(self.mlist, lists)
605
# I should really confirm that there is only one match
607
#syslog('info', 'Match: %s', match)
609
syslog('info', 'Match found: <%s>', match[0])
612
raise DatabaseIntegrityError
615
def get_aliases(self, subscriber_id):
616
"""Execute a SELECT statement, returning the results as a list."""
617
command = "result = self.store.find(Alias,Alias.subscriber_id == subscriber_id)\nlists = [(alias.pseudonym.encode('utf-8')) for alias in result]\n"
619
syslog('info', 'DlistUtils(get_aliases):Executing query:\n%s', command)
621
syslog('info', 'The value of subscriber_id is: %s\n', subscriber_id)
622
result = self.store.find(Alias,Alias.subscriber_id == subscriber_id)
623
lists = [(alias.pseudonym.encode('utf-8')) for alias in result]
628
def change_aliases(self, subscriber_id, oldAliasList, newAliasList):
630
syslog('info', 'executing change_aliases\n')
632
for a in oldAliasList:
633
if a not in newAliasList:
634
self.store.find(Alias,Alias.pseudonym == unicode(a)).remove()
635
for a in newAliasList:
636
if a not in oldAliasList:
637
self.pseudonym = unicode(a,"utf-8")
638
self.subscriber_id = subscriber_id
579
__storm_table__ = "alias"
580
pseudonym = Unicode(primary = True)
581
subscriber_id = Int()
582
subscriber = Reference(subscriber_id,Subscriber.subscriber_id)
584
def __init__(self,mlist):
586
self.database = getConn(mlist)
589
def canonicalize_sender(self, aliases):
590
"""Execute a SELECT statement, returning the email addresses of matching subscribers."""
592
command = "result = self.store.find((Subscriber,Alias),Alias.pseudonym = alias,Subscriber.subscriber_id = Alias.subscriber_id)\nlists = [(subscriber.mailman_key) for subscriber,alias in result]\n"
594
syslog('info', 'DlistUtils(canonicalize_sender)Executing query:\n%s', command)
596
for alias in aliases:
598
#syslog('info', 'Checking if <%s> is an alias', alias)
599
result = self.store.find((Subscriber,Alias),And(Alias.pseudonym == unicode(alias,'utf-8'),Subscriber.subscriber_id == Alias.subscriber_id))
600
lists = [(subscriber.mailman_key.encode('utf-8')) for (subscriber,alias) in result]
602
returnList = GetEmailAddress(self.mlist, lists)
606
# I should really confirm that there is only one match
608
#syslog('info', 'Match: %s', match)
610
syslog('info', 'Match found: <%s>', match[0])
613
raise DatabaseIntegrityError
616
def get_aliases(self, subscriber_id):
617
"""Execute a SELECT statement, returning the results as a list."""
618
command = "result = self.store.find(Alias,Alias.subscriber_id == subscriber_id)\nlists = [(alias.pseudonym.encode('utf-8')) for alias in result]\n"
620
syslog('info', 'DlistUtils(get_aliases):Executing query:\n%s', command)
622
syslog('info', 'The value of subscriber_id is: %s\n', subscriber_id)
623
result = self.store.find(Alias,Alias.subscriber_id == subscriber_id)
624
lists = [(alias.pseudonym.encode('utf-8')) for alias in result]
629
def change_aliases(self, subscriber_id, oldAliasList, newAliasList):
631
syslog('info', 'executing change_aliases\n')
632
for a in oldAliasList:
633
if a not in newAliasList:
634
self.store.find(Alias,Alias.pseudonym == unicode(a,"utf-8")).remove()
635
for a in newAliasList:
636
if a not in oldAliasList:
637
syslog('info', 'going to add alias in database\n')
638
self.pseudonym = unicode(a,"utf-8")
639
self.subscriber_id = subscriber_id
641
syslog('info', 'alias added\n')
641
643
class Override(object):
642
__storm_table__ = "override"
643
__storm_primary__ = "subscriber_id", "thread_id"
644
subscriber_id = Int()
645
subscriber = Reference(subscriber_id,Subscriber.subscriber_id)
647
thread = Reference(thread_id,Thread.thread_id)
650
def __init__(self,mlist):
652
self.database = getConn(mlist)
654
def override(self, msg, msgdata, preference):
655
"""Subscribe or unsubscribe a user from a given thread."""
657
syslog('info', 'DlistUtils: in override')
658
subscriber = Subscriber(self.mlist)
659
subscriberID = subscriber.getSubscriber_id(msg, msgdata, loose=1)
660
threadID = msgdata['thread_id']
661
self._override_inner(subscriberID, threadID, preference)
663
def override_from_web(self, subscriberID, thread_reference, preference):
664
"""Add an override entry to the database, returning false if unable to fulfil request"""
666
threadNum = int(thread_reference)
667
preference = int(preference)#TODO this line was not here before
669
thread = Thread(self.mlist)
670
threadNum, temp =thread.threadIDandName(thread_reference)
671
return self._override_inner(subscriberID, threadNum, preference)
673
# This is called both by override and override_from_web, above
675
def _override_inner(self, subscriberID, threadID, preference):
676
"""Add an override entry to the database, returning false if unable to fulfil request"""
679
#syslog('info', 'DlistUtils: in override_inner')
680
#First check if thread exists
681
command = "exists = self.store.find(Thread,Thread.thread_id == threadID).count()\nself.store.find(Override,And(Override.subscriber_id == subscriberID,Override.thread_id == threadID)).remove()\noverride = self.store.add(Override())\noverride.subscriber_id = subscriberID\noverride.thread_id = threadID\noverride.preference = preference\n"
683
syslog('info', 'DlistUtils(override_from_web):Executing query:\n%s', command)
684
exists = self.store.find(Thread,Thread.thread_id == threadID).count()
689
# Remove any prior override by this user
690
self.store.find(Override,And(Override.subscriber_id == subscriberID,Override.thread_id == threadID)).remove()
692
# Now, insert the change
693
self.subscriber_id = subscriberID
694
self.thread_id = threadID
695
self.preference = preference
700
def overrideURL(self,list_name, host_name, thread_reference, preference):
701
return 'http://%s/mailman/options/%s?override=%s&preference=%d' % (host_name, list_name, thread_reference, preference)
644
__storm_table__ = "override"
645
__storm_primary__ = "subscriber_id", "thread_id"
646
subscriber_id = Int()
647
subscriber = Reference(subscriber_id,Subscriber.subscriber_id)
649
thread = Reference(thread_id,Thread.thread_id)
652
def __init__(self,mlist):
654
self.database = getConn(mlist)
656
def override(self, msg, msgdata, preference):
657
"""Subscribe or unsubscribe a user from a given thread."""
659
syslog('info', 'DlistUtils: in override')
660
subscriber = Subscriber(self.mlist)
661
subscriberID = subscriber.getSubscriber_id(msg, msgdata, loose=1)
662
threadID = msgdata['thread_id']
663
self._override_inner(subscriberID, threadID, preference)
665
def override_from_web(self, subscriberID, thread_reference, preference):
666
"""Add an override entry to the database, returning false if unable to fulfil request"""
668
threadNum = int(thread_reference)
669
preference = int(preference)
671
thread = Thread(self.mlist)
672
threadNum, temp =thread.threadIDandName(thread_reference)
673
return self._override_inner(subscriberID, threadNum, preference)
675
# This is called both by override and override_from_web, above
677
def _override_inner(self, subscriberID, threadID, preference):
678
"""Add an override entry to the database, returning false if unable to fulfil request"""
681
#syslog('info', 'DlistUtils: in override_inner')
682
#First check if thread exists
683
command = "exists = self.store.find(Thread,Thread.thread_id == threadID).count()\nself.store.find(Override,And(Override.subscriber_id == subscriberID,Override.thread_id == threadID)).remove()\noverride = self.store.add(Override())\noverride.subscriber_id = subscriberID\noverride.thread_id = threadID\noverride.preference = preference\n"
685
syslog('info', 'DlistUtils(override_from_web):Executing query:\n%s', command)
686
exists = self.store.find(Thread,Thread.thread_id == threadID).count()
691
# Remove any prior override by this user
692
self.store.find(Override,And(Override.subscriber_id == subscriberID,Override.thread_id == threadID)).remove()
694
# Now, insert the change
695
self.subscriber_id = subscriberID
696
self.thread_id = threadID
697
self.preference = preference
702
def overrideURL(self,list_name, host_name, thread_reference, preference):
703
return 'http://%s/mailman/options/%s?override=%s&preference=%d' % (host_name, list_name, thread_reference, preference)
704
706
#Functions Independent of the database tables in DlistUtils
706
708
def GetEmailAddress(mlist, lists):
707
"""Extract email address from list of matching subscribers"""
712
newMember = mlist.getMemberCPAddress(e)
713
returnList.append(newMember)
715
#syslog('error', 'Unable to find user ' + e + ' in internal database for %s', mlist.internal_name());
716
syslog('error', 'User name was found through this query: ' + command);
718
syslog('info', 'Result is: %s', returnList)
709
"""Extract email address from list of matching subscribers"""
714
newMember = mlist.getMemberCPAddress(e)
715
returnList.append(newMember)
717
#syslog('error', 'Unable to find user ' + e + ' in internal database for %s', mlist.internal_name());
718
syslog('error', 'User name was found through this query: ' + command);
720
syslog('info', 'Result is: %s', returnList)
721
723
def enabled(mlist):
722
"""Check whether a given mailing list has dynamic sublists enabled"""
724
return mlist.dlists_enabled
726
return True #TODO was 0 before
724
"""Check whether a given mailing list has dynamic sublists enabled"""
726
return mlist.dlists_enabled
728
730
def getConn(mlist):
730
"""Return a connection to the database for a given mailing list.The argument may be either the mlist object or a string."""
732
uri = mm_cfg.STORM_DB + '://' + mm_cfg.STORM_DB_USER + ':' + mm_cfg.STORM_DB_PASS + '@' + mm_cfg.STORM_DB_HOST + '/' + mm_cfg.STORM_DB
733
return create_database(uri)
736
name = mlist.internal_name()
739
uri = mm_cfg.STORM_DB + '://' + mm_cfg.STORM_DB_USER + ':' + mm_cfg.STORM_DB_PASS + '@' + mm_cfg.STORM_DB_HOST + '/' + name
740
return create_database(uri)
731
"""Return a connection to the database for a given mailing list.The argument may be either the mlist object or a string."""
733
uri = mm_cfg.STORM_DB + '://' + mm_cfg.STORM_DB_USER + ':' + mm_cfg.STORM_DB_PASS + '@' + mm_cfg.STORM_DB_HOST + '/' + mm_cfg.STORM_DB
734
return create_database(uri)
737
name = mlist.internal_name()
740
uri = mm_cfg.STORM_DB + '://' + mm_cfg.STORM_DB_USER + ':' + mm_cfg.STORM_DB_PASS + '@' + mm_cfg.STORM_DB_HOST + '/' + name
741
return create_database(uri)
742
743
def _create_database(name):
743
"""Create a database. This requires us to not be in a transaction."""
744
conn = pgdb.connect(host='localhost', database='postgres',user='mailman', password='mailman')
745
old_iso_lvl = conn.isolation_level
746
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
747
cursor = conn.cursor()
748
cursor.execute('create database "%s"' % name)
749
conn.set_isolation_level(old_iso_lvl)
744
"""Create a database. This requires us to not be in a transaction."""
745
conn = pgdb.connect(host='localhost', database='postgres',user='mailman', password='mailman')
746
old_iso_lvl = conn.isolation_level
747
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
748
cursor = conn.cursor()
749
cursor.execute('create database "%s"' % name)
750
conn.set_isolation_level(old_iso_lvl)
751
752
def executeSQL(mlist, commands):
752
"""Execute a sequence of SQL commands that create tables in the database."""
753
database = getConn(mlist)
754
store = Store(database)
756
# In case it's called with a string
757
if (type(commands) == types.StringType):
758
commands = [commands]
759
for command in commands:
761
syslog('info', "DlistUtils:(executeSQL)executing query:%s\n", command)
763
store.execute(command)
753
"""Execute a sequence of SQL commands that create tables in the database."""
754
database = getConn(mlist)
755
store = Store(database)
756
# In case it's called with a string
757
if(type(commands) == types.StringType):
758
commands = [commands]
759
for command in commands:
761
syslog('info', "DlistUtils:(executeSQL)executing query:%s\n", command)
763
store.execute(command)
770
770
def create_dlist(mlist):
771
""" Set the dynamic sublist options for a mailing list and create the corresponding postgres database and tables."""
773
_create_database(mlist.internal_name())
775
syslog('info', "Database created %s:\n", mlist.internal_name())
778
["CREATE TABLE subscriber (subscriber_id SERIAL PRIMARY KEY,\
779
mailman_key VARCHAR(255) UNIQUE,\
780
preference INT2 DEFAULT 1,\
781
format INT2 DEFAULT 3, \
782
deleted BOOLEAN DEFAULT FALSE, \
783
suppress INT2 DEFAULT 0)",
784
"CREATE UNIQUE INDEX subscriber_mailman_key ON subscriber(mailman_key)",
785
"CREATE TABLE message (message_id SERIAL PRIMARY KEY,\
786
sender_id INTEGER REFERENCES subscriber,\
787
subject VARCHAR(255),\
788
thread_id INTEGER DEFAULT NULL)",
789
"CREATE TABLE thread (thread_id SERIAL PRIMARY KEY,\
790
thread_name CHAR(16),\
791
base_message_id INTEGER REFERENCES message,\
792
status INT2 DEFAULT 0,\
793
parent INTEGER DEFAULT NULL)",
794
"CREATE TABLE alias (pseudonym VARCHAR(255) PRIMARY KEY, \
795
subscriber_id INTEGER REFERENCES subscriber)",
796
"CREATE TABLE override (subscriber_id INTEGER REFERENCES subscriber,\
797
thread_id INTEGER NOT NULL REFERENCES thread,\
798
preference INT2 NOT NULL)"])
801
database = getConn(mlist)
802
store = Store(database)
804
# Needed in case non-subscribers post
805
subscriber = Subscriber(mlist)
806
subscriber.subscriber_id = 0
807
subscriber.suppress = 1
808
#"INSERT INTO subscriber (subscriber_id, suppress) VALUES (0,1)")
809
store.add(subscriber)
814
alreadyLocked = mlist.Locked()
815
if alreadyLocked == 0:
816
mlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT)
817
mlist.dlists_enabled = True #TODO was 1 before
818
mlist.require_explicit_destination = 0
819
mlist.include_list_post_header = 0
820
mlist.include_rfc2369_headers = 0
821
mlist.loose_email_matching = 1
822
mlist.obscure_addresses = 0
825
if alreadyLocked == 0:
771
""" Set the dynamic sublist options for a mailing list and create the corresponding postgres database and tables."""
773
_create_database(mlist.internal_name())
775
syslog('info', "Database created: %s\n", mlist.internal_name())
778
["CREATE TABLE subscriber (subscriber_id SERIAL PRIMARY KEY,\
779
mailman_key VARCHAR(255) UNIQUE,\
780
preference INT2 DEFAULT 1,\
781
format INT2 DEFAULT 3, \
782
deleted BOOLEAN DEFAULT FALSE, \
783
suppress INT2 DEFAULT 0)",
784
"CREATE UNIQUE INDEX subscriber_mailman_key ON subscriber(mailman_key)",
785
"CREATE TABLE message (message_id SERIAL PRIMARY KEY,\
786
sender_id INTEGER REFERENCES subscriber,\
787
subject VARCHAR(255),\
788
thread_id INTEGER DEFAULT NULL)",
789
"CREATE TABLE thread (thread_id SERIAL PRIMARY KEY,\
790
thread_name CHAR(16),\
791
base_message_id INTEGER REFERENCES message,\
792
status INT2 DEFAULT 0,\
793
parent INTEGER DEFAULT NULL)",
794
"CREATE TABLE alias (pseudonym VARCHAR(255) PRIMARY KEY, \
795
subscriber_id INTEGER REFERENCES subscriber)",
796
"CREATE TABLE override (subscriber_id INTEGER REFERENCES subscriber,\
797
thread_id INTEGER NOT NULL REFERENCES thread,\
798
preference INT2 NOT NULL)"])
801
database = getConn(mlist)
802
store = Store(database)
804
# Needed in case non-subscribers post
805
subscriber = Subscriber(mlist)
806
subscriber.subscriber_id = 0
807
subscriber.suppress = 1
808
#"INSERT INTO subscriber (subscriber_id, suppress) VALUES (0,1)")
809
store.add(subscriber)
814
alreadyLocked = mlist.Locked()
815
if alreadyLocked == 0:
816
mlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT)
817
mlist.dlists_enabled = True
818
mlist.require_explicit_destination = 0
819
mlist.include_list_post_header = 0
820
mlist.include_rfc2369_headers = 0
821
mlist.loose_email_matching = 1
822
mlist.obscure_addresses = 0
825
if alreadyLocked == 0: