Package svnmailer :: Package notifier :: Module _multimail

[hide private]

# Source Code for Module svnmailer.notifier._multimail

      1  # -*- coding: utf-8 -*-
      2  # pylint: disable-msg = W0201, W0233, W0613
      3  #
      4  # Copyright 2004-2006 André Malo or his licensors, as applicable
      5  #
      6  # Licensed under the Apache License, Version 2.0 (the "License");
      7  # you may not use this file except in compliance with the License.
      8  # You may obtain a copy of the License at
      9  #
     10  #     http://www.apache.org/licenses/LICENSE-2.0
     11  #
     12  # Unless required by applicable law or agreed to in writing, software
     13  # distributed under the License is distributed on an "AS IS" BASIS,
     14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  # See the License for the specific language governing permissions and
     16  # limitations under the License.
     17  """
     18  email notifier
     19  """
     20  __author__    = "André Malo"
     21  __docformat__ = "epytext en"
     22  __all__       = ['Error', 'InvalidMailOption', 'getNotifier']
     23
     24  # global imports
     25  from svnmailer.notifier import _mail
     26
     27  # exceptions
     28 -class Error(Exception):
     29      """ Base exception for this module """
     30      pass
     31
     32 -class InvalidMailOption(Error):
     33      """ Invalid Multipart mail option """
     34      pass
     35
     36
     37 -def getNotifier(cls, config, groupset):
     38      """ Returns an initialized notifier or nothing
     39
     40          @param cls: The notifier base class to use
     41          @type cls: C{class}
     42
     43          @param config: The svnmailer config
     44          @type config: C{svnmailer.settings.Settings}
     45
     46          @param groupset: The groupset to process
     47          @type groupset: C{list}
     48
     49          @return: The list of notifiers (containing 0 or 1 member)
     50          @rtype: C{list}
     51      """
     52      from svnmailer import util
     53
     54      return [decorateNotifier(
     55          util.inherit(cls, MultiMailNotifier),
     56          groupset.groups[0].long_mail_action, config, groupset
     57      )]
     58
     59
     60 -def decorateNotifier(cls, action, config, groupset):
     61      """ Decorates the notifier class (or not)
     62
     63          @param cls: The notifier class
     64          @type cls: C{class}
     65
     66          @param action: The configured action
     67          @type action: C{unicode}
     68
     69          @param config: The svnmailer config
     70          @type config: C{svnmailer.settings.Settings}
     71
     72          @param groupset: The groupset to process
     73          @type groupset: C{list}
     74
     75          @return: The decorated class or C{None}
     76          @rtype: C{class}
     77      """
     78      if action and action.maxbytes:
     79          from svnmailer import util
     80
     81          decorator = None
     82          generator = cls
     83
     84          if action.mode == action.TRUNCATE:
     85              decorator = util.inherit(TruncatingDecorator, generator)
     86
     87          elif action.mode == action.URLS:
     88              decorator = util.inherit(URLDecorator, generator)
     89              if action.truncate:
     90                  decorator = util.inherit(URLTruncatingDecorator, decorator)
     91
     92          elif action.mode == action.SPLIT:
     93              decorator = util.inherit(SplittingDecorator, generator)
     94              if action.truncate:
     95                  decorator = util.inherit(
     96                      SplittingTruncatingDecorator, decorator
     97                  )
     98
     99          if decorator:
    100              return decorator(config, groupset, action.maxbytes, action.drop)
    101
    102      return cls(config, groupset)
    103
    104
    105 -class MultiMailNotifier(_mail.MailNotifier):
    106      """ Bases class for mail notifiers using attachments for the diffs """
    107      __implements__ = [_mail.MailNotifier]
    108
    109      # need this (variable args) for deco classes
    110 -    def __init__(self, config, groupset, *args, **kwargs):
    111          """ Initialization """
    112          _mail.MailNotifier.__init__(self, config, groupset)
    113          self.mctype, self.mdispo = self._parseMailType()
    114
    115
    116 -    def _parseMailType(self):
    117          """ Returns the multimail options
    118
    119              @return: The diff content type and disposition
    120              @rtype: C{tuple}
    121          """
    122          from svnmailer import util
    123
    124          ctype = 'text/plain'
    125          dispo = 'inline'
    126          for option in (self.config.mail_type or '').split()[1:]:
    127              try:
    128                  name, value = [val.lower() for val in option.split('=')]
    129                  if name == u'type':
    130                      ctype = util.parseContentType(value)
    131                      if not ctype:
    132                          raise ValueError(
    133                              "invalid multimail type specification %r" % value
    134                          )
    135                      ctype = ctype[0]
    136                  elif name == u'disposition':
    137                      if value not in (u'inline', 'attachment'):
    138                          raise ValueError(
    139                              "invalid disposition specification %r" % value
    140                          )
    141                      dispo = value
    142                  else:
    143                      raise ValueError("unknown multimail option %r" % option)
    144              except ValueError, exc:
    145                  raise InvalidMailOption(str(exc))
    146
    147          return (ctype, dispo)
    148
    149
    150 -    def composeMail(self):
    151          """ Composes the mail
    152
    153              @return: The senders, the receivers, the mail(s)
    154              @rtype: C{tuple}
    155          """
    156          import cStringIO
    157
    158          groups = self._groupset.groups
    159          sender, to_addrs, headers = self.composeHeaders(groups)
    160
    161          self.diff_file_list = []
    162          self.fp = self._getMailWriter(cStringIO.StringIO())
    163          self.writeNotification()
    164
    165          # create the mails
    166          mails = self._getMultiMails()
    167
    168          for mail in mails:
    169              mail.update(headers)
    170              yield (
    171                  sender.encode('utf-8'),
    172                  [addr.encode('utf-8') for addr in to_addrs],
    173                  mail
    174              )
    175
    176          self.diff_file_list = []
    177          self.fp.close()
    178
    179
    180 -    def sendMail(self, sender, to_addr, mail):
    181          """ Sends the mail (abstract method) """
    182          raise NotImplementedError()
    183
    184
    185 -    def _getMultiMails(self):
    186          """ Returns the multipart mail(s)
    187
    188              @return: The mail(s)
    189              @rtype: C{list} of C{_MultiMail}
    190          """
    191          parts = [_SinglePart(self.fp.getvalue(), encoding = 'utf-8')]
    192          diff = None # avoid UnboundLocalError if the loop is not run
    193          for diff in self.diff_file_list:
    194              parts.append(diff.toMIMEPart())
    195          del diff # cleanup
    196
    197          return [_MultiMail(self.getMailSubject(), parts)]
    198
    199
    200 -    def _getMailWriter(self, fp):
    201          """ Returns a mail writer
    202
    203              @param fp: The stream to wrap
    204              @type fp: file like object
    205
    206              @return: The file object
    207              @rtype: file like object
    208          """
    209          from svnmailer import stream
    210
    211          return stream.UnicodeStream(fp)
    212
    213
    214 -    def writeDiffList(self):
    215          """ Writes the commit diffs into temp files """
    216          from svnmailer import stream
    217
    218          # drop all stuff between diffs
    219          old_fp, self.fp = (self.fp, stream.DevNullStream())
    220
    221          super(MultiMailNotifier, self).writeDiffList()
    222
    223          # restore the descriptor
    224          self.fp.close()
    225          self.fp = old_fp
    226
    227
    228 -    def writeContentDiff(self, change, raw = False):
    229          """ Dump the diff into a separate file """
    230          if change.isDirectory():
    231              return
    232
    233          tmpfile = self.getTempFile()
    234          old_fp, self.fp = (self.fp, self._getDiffStream(tmpfile.fp))
    235
    236          raw = True
    237          super(MultiMailNotifier, self).writeContentDiff(change, raw)
    238
    239          self.fp.close()
    240          self.diff_file_list.append(DiffDescriptor(self, tmpfile, change))
    241
    242          self.fp = old_fp
    243
    244
    245 -    def writePropertyDiffs(self, diff_tokens, change, raw = False):
    246          """ Dump the property diff into a separate file """
    247          tmpfile = self.getTempFile()
    248          old_fp, self.fp = (self.fp, self._getDiffStream(tmpfile.fp))
    249
    250          raw = True
    251          super(MultiMailNotifier, self).writePropertyDiffs(
    252              diff_tokens, change, raw
    253          )
    254
    255          self.fp.close()
    256          self.diff_file_list.append(DiffDescriptor(self, tmpfile, change, True))
    257
    258          self.fp = old_fp
    259
    260
    261 -    def _getDiffStream(self, fp):
    262          """ Returns the (possibly decorated) diff stream """
    263          from svnmailer import stream
    264
    265          return stream.BinaryOrUnicodeStream(fp)
    266
    267
    268 -class SplittingDecorator(object):
    269      """ Splits the content between diffs if it gets too long
    270
    271          @ivar max_notification_size: Maximum size of one mail content
    272          @type max_notification_size: C{int}
    273
    274          @ivar drop: maximum number of mails
    275          @type drop: C{int}
    276
    277          @ivar drop_fp: The alternate summary stream
    278          @type drop_fp: file like object
    279      """
    280
    281 -    def __init__(self, settings, groupset, maxsize, drop):
    282          """ Initialization
    283
    284              @param maxsize: The maximum number of bytes that should be written
    285                  into one mail
    286              @type maxsize: C{int}
    287
    288              @param drop: maximum number of mails
    289              @type drop: C{int}
    290          """
    291          self.__super = super(self.__decorator_class, self)
    292          self.__super.__init__(settings, groupset, maxsize, drop)
    293          self.max_notification_size = maxsize
    294          self.drop = drop
    295
    296
    297 -    def _getMailWriter(self, fp):
    298          """ inits the drop_fp """
    299          import cStringIO
    300
    301          self.drop_fp = self.__super._getMailWriter(cStringIO.StringIO())
    302
    303          return self.__super._getMailWriter(fp)
    304
    305
    306 -    def writeMetaData(self):
    307          """ write meta data to drop_fp as well """
    308          old_fp, self.fp = (self.fp, self.drop_fp)
    309          self.__super.writeMetaData()
    310          self.fp = old_fp
    311
    312          self.__super.writeMetaData()
    313
    314
    315 -    def _getMultiMails(self):
    316          """ Returns the multipart mail(s)
    317
    318              @return: The mail(s)
    319              @rtype: C{list} of C{_MultiMail}
    320          """
    321          parts = [_SinglePart(self.fp.getvalue(), encoding = 'utf-8')]
    322
    323          diff = None
    324          asize = parts[0].getSize()
    325          diffs = [(diff, diff.toMIMEPart().getSize())
    326              for diff in self.diff_file_list
    327          ]
    328          diff_sizes = [diff[1] for diff in diffs]
    329          maxsize = sum(diff_sizes, asize)
    330
    331          if maxsize <= self.max_notification_size or not diff_sizes:
    332              parts.extend([diff.toMIMEPart() for diff in self.diff_file_list])
    333              yield _MultiMail(self.getMailSubject(), parts)
    334          else:
    335              nummails = 1
    336              tsize = asize
    337              for size in diff_sizes:
    338                  tsize += size
    339                  if tsize > self.max_notification_size:
    340                      nummails += 1
    341                      tsize = size
    342
    343              if self.drop and nummails > self.drop:
    344                  self.drop_fp.write((
    345                      u"\n[This commit notification would consist of %d parts, "
    346                      u"\nwhich exceeds the limit of %d ones, so it was "
    347                      u"shortened to the summary.]\n" % (nummails, self.drop)
    348                  ).encode("utf-8"))
    349
    350                  yield _MultiMail(self.getMailSubject(), [_SinglePart(
    351                      self.drop_fp.getvalue(), encoding = 'utf-8'
    352                  )])
    353              else:
    354                  mcount = 1
    355                  for diff in diffs:
    356                      asize += diff[1]
    357                      if asize > self.max_notification_size:
    358                          yield _MultiMail(
    359                              self.getMailSubject(u"[%d/%d]" % (mcount, nummails)
    360                          ), parts)
    361                          mcount += 1
    362                          asize = diff[1]
    363                          parts = [_SinglePart((
    364                              u"[Part %d/%d]\n" % (mcount, nummails)
    365                          ).encode('utf-8'), encoding = 'utf-8')]
    366                      parts.append(diff[0].toMIMEPart())
    367
    368                  if parts:
    369                      yield _MultiMail(
    370                          self.getMailSubject(u"[%d/%d]" % (mcount, nummails)
    371                      ), parts)
    372
    373          del diff # cleanup
    374
    375
    376 -class SplittingTruncatingDecorator(object):
    377      """ split/truncate decorator """
    378
    379 -    def __init__(self, settings, groupset, maxsize, drop):
    380          """ Initialization
    381
    382              @param maxsize: The maximum number of bytes that should be written
    383                  into one mail
    384              @type maxsize: C{int}
    385
    386              @param drop: maximum number of mails
    387              @type drop: C{int}
    388          """
    389          self.__super = super(self.__decorator_class, self)
    390          self.__super.__init__(settings, groupset, maxsize, drop)
    391
    392
    393 -    def _getMailWriter(self, fp):
    394          """ Returns a truncating mail writer """
    395          from svnmailer import stream
    396
    397          fp = stream.TruncatingStream(fp, self.max_notification_size, True)
    398          return self.__super._getMailWriter(fp)
    399
    400
    401 -    def _getDiffStream(self, fp):
    402          """ Returns the truncating diff stream """
    403          from svnmailer import stream
    404
    405          fp = stream.TruncatingFileStream(fp, self.max_notification_size, True)
    406          return self.__super._getDiffStream(fp)
    407
    408
    409 -class TruncatingDecorator(object):
    410      """ Truncates the mail body after n bytes
    411
    412          @ivar max_notification_size: Maximum size of one mail content
    413          @type max_notification_size: C{int}
    414      """
    415
    416 -    def __init__(self, settings, groupset, maxsize, drop):
    417          """ Initialization
    418
    419              @param maxsize: The maximum number of bytes that should be written
    420                  into one mail
    421              @type maxsize: C{int}
    422
    423              @param drop: maximum number of mails
    424              @type drop: C{int}
    425          """
    426          self.__super = super(self.__decorator_class, self)
    427          self.__super.__init__(settings, groupset, maxsize, drop)
    428          self.max_notification_size = maxsize
    429
    430
    431 -    def _getMailWriter(self, fp):
    432          """ Returns a truncating mail writer """
    433          from svnmailer import stream
    434
    435          fp = stream.TruncatingStream(fp, self.max_notification_size, True)
    436          return self.__super._getMailWriter(fp)
    437
    438
    439 -    def _getMultiMails(self):
    440          """ Returns the multipart mail(s)
    441
    442              @return: The mail(s)
    443              @rtype: C{list} of C{_MultiMail}
    444          """
    445          parts = [_SinglePart(self.fp.getvalue(), encoding = 'utf-8')]
    446
    447          if self.fp.getTruncatedLineCount():
    448              dlen = len(self.diff_file_list)
    449              if dlen:
    450                  parts.append(_SinglePart((
    451                      u"[... %d diffs stripped ...]\n" % dlen
    452                  ).encode('utf-8'), encoding = 'utf-8'))
    453          else:
    454              diff = None # avoid UnboundLocalError if the loop is not run
    455              asize = parts[0].getSize()
    456              mcount = len(self.diff_file_list)
    457              for diff in self.diff_file_list:
    458                  thispart = diff.toMIMEPart()
    459                  asize += thispart.getSize()
    460                  if asize > self.max_notification_size:
    461                      break
    462                  parts.append(thispart)
    463                  mcount -= 1
    464              del diff # cleanup
    465              if mcount:
    466                  parts.append(_SinglePart((
    467                      u"[... %d diffs stripped ...]\n" % mcount
    468                  ).encode('utf-8'), encoding = 'utf-8'))
    469
    470          return [_MultiMail(self.getMailSubject(), parts)]
    471
    472
    473 -class URLDecorator(object):
    474      """ Shows only the urls, if the mail gets too long
    475
    476          @ivar url_fp: The alternative stream
    477          @type url_fp: file like object
    478
    479          @ivar do_truncate: truncating mode?
    480          @type do_truncate: C{bool}
    481
    482          @ivar max_notification_size: Maximum size of one mail content
    483          @type max_notification_size: C{int}
    484      """
    485
    486 -    def __init__(self, settings, groupset, maxsize, drop):
    487          """ Initialization
    488
    489              @param maxsize: The maximum number of bytes that should be written
    490                  into one mail
    491              @type maxsize: C{int}
    492
    493              @param drop: maximum number of mails
    494              @type drop: C{int}
    495          """
    496          self.__super = super(self.__decorator_class, self)
    497          self.__super.__init__(settings, groupset, maxsize, drop)
    498          self.max_notification_size = maxsize
    499          self.do_truncate = False
    500
    501
    502 -    def _getMailWriter(self, fp):
    503          """ Returns a "urling" mail writer """
    504          import cStringIO
    505          from svnmailer import stream
    506
    507          if self.do_truncate:
    508              fp = stream.TruncatingStream(
    509                  self.__super._getMailWriter(fp),
    510                  self.max_notification_size,
    511                  True
    512              )
    513
    514          self.url_fp = self.__super._getMailWriter(cStringIO.StringIO())
    515          return fp
    516
    517
    518 -    def writeContentDiff(self, change):
    519          """ Writes the content diff for a particular change """
    520          self.__super.writeContentDiff(change)
    521
    522          url = self.getContentDiffUrl(self.config, change)
    523          if url is not None:
    524              old_fp, self.fp = (self.fp, self.url_fp)
    525              self.__super.writeContentDiffAction(change)
    526              self.fp = old_fp
    527              self.url_fp.write("URL: %s\n" % url)
    528              self.url_fp.write("\n")
    529
    530
    531 -    def _getMultiMails(self):
    532          """ Returns the multipart mail(s)
    533
    534              @return: The mail(s)
    535              @rtype: C{list} of C{_MultiMail}
    536          """
    537          part0 = _SinglePart(self.fp.getvalue(), encoding = 'utf-8')
    538          if self.do_truncate and self.fp.getTruncatedLineCount():
    539              return [_MultiMail(self.getMailSubject(), [part0])]
    540
    541          diff = None
    542          asize = part0.getSize()
    543          diff_sizes = [diff.toMIMEPart().getSize()
    544              for diff in self.diff_file_list
    545          ]
    546          maxsize = sum(diff_sizes, asize)
    547          del diff
    548
    549          if maxsize <= self.max_notification_size or not diff_sizes:
    550              # fine.
    551              self.url_fp.close()
    552              return self.__super._getMultiMails()
    553
    554          if not self.getUrl(self.config):
    555              self.fp.write(
    556                  u"\n[This mail would be too long, it should contain the "
    557                  u"URLs only, but no browser base url was configured...]\n"
    558              )
    559              parts = [_SinglePart(self.fp.getvalue(), encoding = 'utf-8')]
    560          else:
    561              self.fp.write(
    562                  u"\n[This mail would be too long, it was shortened to "
    563                  u"contain the URLs only.]\n"
    564              )
    565              parts = [_SinglePart(self.fp.getvalue(), encoding = 'utf-8')]
    566
    567              if self.do_truncate:
    568                  import cStringIO
    569                  from svnmailer import stream
    570                  tfp = stream.TruncatingStream(
    571                      cStringIO.StringIO(),
    572                      self.max_notification_size - parts[0].getSize(),
    573                      True
    574                  )
    575                  tfp.write(self.url_fp.getvalue())
    576                  self.url_fp.close()
    577                  self.url_fp = tfp
    578
    579              parts.append(
    580                  _SinglePart(self.url_fp.getvalue(), encoding = 'utf-8')
    581              )
    582
    583          self.url_fp.close()
    584          return [_MultiMail(self.getMailSubject(), parts)]
    585
    586
    587 -class URLTruncatingDecorator(object):
    588      """ Truncates url-only mails """
    589
    590 -    def __init__(self, settings, groupset, maxsize, drop):
    591          """ Initialization
    592
    593              @param maxsize: The maximum number of bytes that should be written
    594                  into one mail
    595              @type maxsize: C{int}
    596
    597              @param drop: maximum number of mails
    598              @type drop: C{int}
    599          """
    600          super(self.__decorator_class, self).__init__(
    601              settings, groupset, maxsize, drop
    602          )
    603          self.do_truncate = True
    604
    605
    606 -class DiffDescriptor(object):
    607      """ Container class to describe a dumped diff """
    608
    609 -    def __init__(self, notifier, tmpfile, change, propdiff = False):
    610          """ Initialization
    611
    612              @param tmpfile: The tempfile the diff was dumped to
    613              @type tmpfile: C{svnmailer.util.TempFile}
    614
    615              @param change: The change in question
    616              @type change: C{svnmailer.subversion.VersionedPathDescriptor}
    617
    618              @param propdiff: is a property diff?
    619              @type propdiff: C{bool}
    620          """
    621          self.tmpfile  = tmpfile
    622          self.change   = change
    623          self.propdiff = propdiff
    624          self.encoding = None
    625          self.ctype    = notifier.mctype
    626          self.dispo    = notifier.mdispo
    627
    628          if not self.propdiff:
    629              enc1, enc2 = notifier.getContentEncodings(change, None)
    630              if enc1 and enc1 == enc2:
    631                  self.encoding = enc1
    632
    633
    634 -    def getValue(self):
    635          """ Returns the diff value
    636
    637              @return: The value
    638              @rtype: C{str}
    639          """
    640          return file(self.tmpfile.name, 'rb').read()
    641
    642
    643 -    def getSize(self):
    644          """ Returns the size of the diff part
    645
    646              @return: The size
    647              @rtype: C{int}
    648          """
    649          import os
    650          return os.stat(self.tmpfile.name).st_size
    651
    652
    653 -    def toMIMEPart(self):
    654          """ Returns the diff as MIME part """
    655          import posixpath
    656          ext = self.propdiff and 'propchange' or 'diff'
    657          name = "%s.%s" % (posixpath.basename(self.change.path), ext)
    658          enc = (self.propdiff and [None] or [self.encoding])[0]
    659
    660          part = _SinglePart(self.getValue(),
    661              name = name, encoding = enc, binary = True, ctype = self.ctype,
    662              dispo = self.dispo
    663          )
    664
    665          return part
    666
    667
    668  from email import MIMEMultipart, MIMENonMultipart
    669 -class _MultiMail(MIMEMultipart.MIMEMultipart):
    670      """ A multimail class """
    671
    672 -    def __init__(self, subject, parts):
    673          """ Initialization
    674
    675              @param subject: The subject to use
    676              @type subject: C{str}
    677
    678              @param parts: The body parts
    679              @type parts: C{list}
    680          """
    681          from email import Header
    682
    683          MIMEMultipart.MIMEMultipart.__init__(self)
    684          self['Subject'] = Header.Header(subject, 'iso-8859-1')
    685          for part in parts:
    686              self.attach(part)
    687
    688
    689 -    def dump(self, fp):
    690          """ Serializes the mail into a descriptor
    691
    692              @param fp: The file object
    693              @type fp: file like object
    694          """
    695          from email import Generator
    696
    697          generator = Generator.Generator(fp, mangle_from_ = False)
    698          generator.flatten(self, unixfrom = False)
    699
    700
    701 -    def update(self, headers):
    702          """ Update the header set of the mail
    703
    704              @param headers: The new headers
    705              @type headers: C{dict}
    706          """
    707          for name, value in headers.items():
    708              self[name] = value
    709
    710
    711 -class _SinglePart(MIMENonMultipart.MIMENonMultipart):
    712      """ A single part of a multipart mail """
    713
    714 -    def __init__(self, body, name = None, encoding = None, binary = False,
    715          ctype = 'text/plain', dispo = 'inline'):
    716          """ Initialization
    717
    718              @param body: The body
    719              @type body: C{str}
    720          """
    721          tparam = {}
    722          dparam = {}
    723          if name is not None:
    724              # Deal with RFC 2184 encoding
    725              name = name.decode('utf-8')
    726              names = self._encodeRfc2184(name, dosplit = False)
    727
    728              # safe name
    729              if names[0] == name:
    730                  tparam['name'] = dparam['filename'] = name.encode('utf-8')
    731
    732              # encoded simple
    733              elif len(names) == 1:
    734                  tparam['name*'] = dparam['filename*'] = names[0]
    735
    736              # encoded and splitted (turned off above [dosplit = False])
    737              else:
    738                  for idx, name in enumerate(names):
    739                      tparam['name*%d*' % idx] = name
    740                      dparam['filename*%d*' % idx] = name
    741
    742          if encoding is not None:
    743              tparam['charset'] = encoding
    744
    745          maintype, subtype = ctype.encode('us-ascii').split('/')
    746          MIMENonMultipart.MIMENonMultipart.__init__(
    747              self, maintype, subtype, **tparam
    748          )
    749          self.set_payload(body)
    750          self.add_header('Content-Disposition',
    751              dispo.encode('us-ascii'), **dparam)
    752          if binary:
    753              cte = 'binary'
    754          else:
    755              cte = '7bit'
    756              try:
    757                  body.encode('us-ascii')
    758              except UnicodeError:
    759                  cte = '8bit'
    760          self['Content-Transfer-Encoding'] = cte
    761          del self['MIME-Version']
    762
    763
    764 -    def _encodeRfc2184(self, value, dosplit = False):
    765          """ Encode a string (parameter value) according to RFC 2184
    766
    767              @param value: The value to encode
    768              @type value: C{unicode}
    769
    770              @param dosplit: Allow long parameter splitting? (Note that is not
    771                  widely supported...)
    772              @type dosplit: C{bool}
    773
    774              @return: The list of encoded values
    775              @rtype: C{list}
    776          """
    777          encode = False
    778          try:
    779              value.encode('us-ascii')
    780          except UnicodeError:
    781              encode = True
    782          else:
    783              if u'"' in value or (len(value) > 78 and dosplit):
    784                  encode = True
    785
    786          values = []
    787          if encode:
    788              import urllib
    789              value = "utf-8''%s" % urllib.quote(value.encode('utf-8'))
    790              if dosplit: # doesn't seem to be supported that much ...
    791                  while len(value) > 78:
    792                      slen = value[77] == '%' and 80 or (
    793                          value[76] == '%' and 79 or 78
    794                      )
    795                      values.append(value[:slen])
    796                      value = value[slen + 1:]
    797          values.append(value)
    798
    799          return values
    800
    801
    802 -    def getSize(self):
    803          """ Serializes the mail into a descriptor
    804
    805              @return: The size of the serialized object
    806              @rtype: C{int}
    807          """
    808          from email import Generator
    809          from svnmailer import stream
    810
    811          fp = stream.CountStream()
    812          generator = Generator.Generator(fp, mangle_from_ = False)
    813          generator.flatten(self, unixfrom = False)
    814
    815          return fp.size
    816