1# Copyright (C) 2002-2018 by the Free Software Foundation, Inc. 2# 3# This program is free software; you can redistribute it and/or 4# modify it under the terms of the GNU General Public License 5# as published by the Free Software Foundation; either version 2 6# of the License, or (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 17from email.Utils import parseaddr, formatdate 18 19from Mailman import mm_cfg 20from Mailman import Errors 21from Mailman import MemberAdaptor 22from Mailman import i18n 23 24def _(s): return s 25 26OVERVIEW = _(""" 27 set ... 28 Set or view your membership options. 29 30 Use `set help' (without the quotes) to get a more detailed list of the 31 options you can change. 32 33 Use `set show' (without the quotes) to view your current option 34 settings. 35""") 36 37DETAILS = _(""" 38 set help 39 Show this detailed help. 40 41 set show [address=<address>] 42 View your current option settings. If you're posting from an address 43 other than your membership address, specify your membership address 44 with `address=<address>' (no brackets around the email address, and no 45 quotes!). 46 47 set authenticate <password> [address=<address>] 48 To set any of your options, you must include this command first, along 49 with your membership password. If you're posting from an address 50 other than your membership address, specify your membership address 51 with `address=<address>' (no brackets around the email address, and no 52 quotes!). 53 54 set ack on 55 set ack off 56 When the `ack' option is turned on, you will receive an 57 acknowledgement message whenever you post a message to the list. 58 59 set digest plain 60 set digest mime 61 set digest off 62 When the `digest' option is turned off, you will receive postings 63 immediately when they are posted. Use `set digest plain' if instead 64 you want to receive postings bundled into a plain text digest 65 (i.e. RFC 1153 digest). Use `set digest mime' if instead you want to 66 receive postings bundled together into a MIME digest. 67 68 set delivery on 69 set delivery off 70 Turn delivery on or off. This does not unsubscribe you, but instead 71 tells Mailman not to deliver messages to you for now. This is useful 72 if you're going on vacation. Be sure to use `set delivery on' when 73 you return from vacation! 74 75 set myposts on 76 set myposts off 77 Use `set myposts off' to not receive copies of messages you post to 78 the list. This has no effect if you're receiving digests. 79 80 set hide on 81 set hide off 82 Use `set hide on' to conceal your email address when people request 83 the membership list. 84 85 set duplicates on 86 set duplicates off 87 Use `set duplicates off' if you want Mailman to not send you messages 88 if your address is explicitly mentioned in the To: or Cc: fields of 89 the message. This can reduce the number of duplicate postings you 90 will receive. 91 92 set reminders on 93 set reminders off 94 Use `set reminders off' if you want to disable the monthly password 95 reminder for this mailing list. 96""") 97 98_ = i18n._ 99 100STOP = 1 101 102 103 104def gethelp(mlist): 105 return _(OVERVIEW) 106 107 108 109class SetCommands: 110 def __init__(self): 111 self.__address = None 112 self.__authok = 0 113 114 def process(self, res, args): 115 if not args: 116 res.results.append(_(DETAILS)) 117 return STOP 118 subcmd = args.pop(0) 119 methname = 'set_' + subcmd 120 method = getattr(self, methname, None) 121 if method is None: 122 res.results.append(_('Bad set command: %(subcmd)s')) 123 res.results.append(_(DETAILS)) 124 return STOP 125 return method(res, args) 126 127 def set_help(self, res, args=1): 128 res.results.append(_(DETAILS)) 129 if args: 130 return STOP 131 132 def _usage(self, res): 133 res.results.append(_('Usage:')) 134 return self.set_help(res) 135 136 def set_show(self, res, args): 137 mlist = res.mlist 138 if not args: 139 realname, address = parseaddr(res.msg['from']) 140 elif len(args) == 1 and args[0].startswith('address='): 141 # Send the results to the address, not the From: dude 142 address = args[0][8:] 143 res.returnaddr = address 144 else: 145 return self._usage(res) 146 if not mlist.isMember(address): 147 listname = mlist.real_name 148 res.results.append( 149 _('You are not a member of the %(listname)s mailing list')) 150 return STOP 151 res.results.append(_('Your current option settings:')) 152 opt = mlist.getMemberOption(address, mm_cfg.AcknowledgePosts) 153 onoff = opt and _('on') or _('off') 154 res.results.append(_(' ack %(onoff)s')) 155 # Digests are a special ternary value 156 digestsp = mlist.getMemberOption(address, mm_cfg.Digests) 157 if digestsp: 158 plainp = mlist.getMemberOption(address, mm_cfg.DisableMime) 159 if plainp: 160 res.results.append(_(' digest plain')) 161 else: 162 res.results.append(_(' digest mime')) 163 else: 164 res.results.append(_(' digest off')) 165 # If their membership is disabled, let them know why 166 status = mlist.getDeliveryStatus(address) 167 how = None 168 if status == MemberAdaptor.ENABLED: 169 status = _('delivery on') 170 elif status == MemberAdaptor.BYUSER: 171 status = _('delivery off') 172 how = _('by you') 173 elif status == MemberAdaptor.BYADMIN: 174 status = _('delivery off') 175 how = _('by the admin') 176 elif status == MemberAdaptor.BYBOUNCE: 177 status = _('delivery off') 178 how = _('due to bounces') 179 else: 180 assert status == MemberAdaptor.UNKNOWN 181 status = _('delivery off') 182 how = _('for unknown reasons') 183 changetime = mlist.getDeliveryStatusChangeTime(address) 184 if how and changetime > 0: 185 date = formatdate(changetime) 186 res.results.append(_(' %(status)s (%(how)s on %(date)s)')) 187 else: 188 res.results.append(' ' + status) 189 opt = mlist.getMemberOption(address, mm_cfg.DontReceiveOwnPosts) 190 # sense is reversed 191 onoff = (not opt) and _('on') or _('off') 192 res.results.append(_(' myposts %(onoff)s')) 193 opt = mlist.getMemberOption(address, mm_cfg.ConcealSubscription) 194 onoff = opt and _('on') or _('off') 195 res.results.append(_(' hide %(onoff)s')) 196 opt = mlist.getMemberOption(address, mm_cfg.DontReceiveDuplicates) 197 # sense is reversed 198 onoff = (not opt) and _('on') or _('off') 199 res.results.append(_(' duplicates %(onoff)s')) 200 opt = mlist.getMemberOption(address, mm_cfg.SuppressPasswordReminder) 201 # sense is reversed 202 onoff = (not opt) and _('on') or _('off') 203 res.results.append(_(' reminders %(onoff)s')) 204 205 def set_authenticate(self, res, args): 206 mlist = res.mlist 207 if len(args) == 1: 208 realname, address = parseaddr(res.msg['from']) 209 password = args[0] 210 elif len(args) == 2 and args[1].startswith('address='): 211 password = args[0] 212 address = args[1][8:] 213 else: 214 return self._usage(res) 215 # See if the password matches 216 if not mlist.isMember(address): 217 listname = mlist.real_name 218 res.results.append( 219 _('You are not a member of the %(listname)s mailing list')) 220 return STOP 221 if not mlist.Authenticate((mm_cfg.AuthUser, 222 mm_cfg.AuthListAdmin), 223 password, address): 224 res.results.append(_('You did not give the correct password')) 225 return STOP 226 self.__authok = 1 227 self.__address = address 228 229 def _status(self, res, arg): 230 status = arg.lower() 231 if status == 'on': 232 flag = 1 233 elif status == 'off': 234 flag = 0 235 else: 236 res.results.append(_('Bad argument: %(arg)s')) 237 self._usage(res) 238 return -1 239 # See if we're authenticated 240 if not self.__authok: 241 res.results.append(_('Not authenticated')) 242 self._usage(res) 243 return -1 244 return flag 245 246 def set_ack(self, res, args): 247 mlist = res.mlist 248 if len(args) <> 1: 249 return self._usage(res) 250 status = self._status(res, args[0]) 251 if status < 0: 252 return STOP 253 mlist.setMemberOption(self.__address, mm_cfg.AcknowledgePosts, status) 254 res.results.append(_('ack option set')) 255 256 def set_digest(self, res, args): 257 mlist = res.mlist 258 if len(args) <> 1: 259 return self._usage(res) 260 if not self.__authok: 261 res.results.append(_('Not authenticated')) 262 self._usage(res) 263 return STOP 264 arg = args[0].lower() 265 if arg == 'off': 266 try: 267 mlist.setMemberOption(self.__address, mm_cfg.Digests, 0) 268 except Errors.AlreadyReceivingRegularDeliveries: 269 pass 270 elif arg == 'plain': 271 try: 272 mlist.setMemberOption(self.__address, mm_cfg.Digests, 1) 273 except Errors.AlreadyReceivingDigests: 274 pass 275 mlist.setMemberOption(self.__address, mm_cfg.DisableMime, 1) 276 elif arg == 'mime': 277 try: 278 mlist.setMemberOption(self.__address, mm_cfg.Digests, 1) 279 except Errors.AlreadyReceivingDigests: 280 pass 281 mlist.setMemberOption(self.__address, mm_cfg.DisableMime, 0) 282 else: 283 res.results.append(_('Bad argument: %(arg)s')) 284 self._usage(res) 285 return STOP 286 res.results.append(_('digest option set')) 287 288 def set_delivery(self, res, args): 289 mlist = res.mlist 290 if len(args) <> 1: 291 return self._usage(res) 292 status = self._status(res, args[0]) 293 if status < 0: 294 return STOP 295 # Delivery status is handled differently than other options. If 296 # status is true (set delivery on), then we enable delivery. 297 # Otherwise, we have to use the setDeliveryStatus() interface to 298 # specify that delivery was disabled by the user. 299 if status: 300 mlist.setDeliveryStatus(self.__address, MemberAdaptor.ENABLED) 301 res.results.append(_('delivery enabled')) 302 else: 303 mlist.setDeliveryStatus(self.__address, MemberAdaptor.BYUSER) 304 res.results.append(_('delivery disabled by user')) 305 306 def set_myposts(self, res, args): 307 mlist = res.mlist 308 if len(args) <> 1: 309 return self._usage(res) 310 status = self._status(res, args[0]) 311 if status < 0: 312 return STOP 313 # sense is reversed 314 mlist.setMemberOption(self.__address, mm_cfg.DontReceiveOwnPosts, 315 not status) 316 res.results.append(_('myposts option set')) 317 318 def set_hide(self, res, args): 319 mlist = res.mlist 320 if len(args) <> 1: 321 return self._usage(res) 322 status = self._status(res, args[0]) 323 if status < 0: 324 return STOP 325 mlist.setMemberOption(self.__address, mm_cfg.ConcealSubscription, 326 status) 327 res.results.append(_('hide option set')) 328 329 def set_duplicates(self, res, args): 330 mlist = res.mlist 331 if len(args) <> 1: 332 return self._usage(res) 333 status = self._status(res, args[0]) 334 if status < 0: 335 return STOP 336 # sense is reversed 337 mlist.setMemberOption(self.__address, mm_cfg.DontReceiveDuplicates, 338 not status) 339 res.results.append(_('duplicates option set')) 340 341 def set_reminders(self, res, args): 342 mlist = res.mlist 343 if len(args) <> 1: 344 return self._usage(res) 345 status = self._status(res, args[0]) 346 if status < 0: 347 return STOP 348 # sense is reversed 349 mlist.setMemberOption(self.__address, mm_cfg.SuppressPasswordReminder, 350 not status) 351 res.results.append(_('reminder option set')) 352 353 354 355def process(res, args): 356 # We need to keep some state between set commands 357 if not getattr(res, 'setstate', None): 358 res.setstate = SetCommands() 359 res.setstate.process(res, args) 360