Implement a maximum message length; will be used for emails (SMTP,
Pipe outputs).

* tools/hook-scripts/mailer/mailer.py:
  (Writer.__init__): accept and save a MAXBYTES value.
  (Writer.write): introduce a new write_limited() local function to
    check if writing S will surpass the defined maximum length. Raise
    MessageTooLarge, when that happens. Return this new local function
    as a wrapper on the conditional _write() function.
  (OutputBase.send): pass a MAXBYTES value to the Writer() creation.
    ### TBD to fetch this from the configuration.
    Bugfix: must seek to the start of the buffer, after truncating it.
    Capture MessageTooLarge on the "short" write, and put a message
    into the buffer that the (email) was truncated.
  (generate_urls): add a placeholder function to construct emails
    using URLs to (ViewVC) web pages showing each diff.


git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1917168 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tools/hook-scripts/mailer/mailer.py b/tools/hook-scripts/mailer/mailer.py
index 15adb7a..54c1823 100755
--- a/tools/hook-scripts/mailer/mailer.py
+++ b/tools/hook-scripts/mailer/mailer.py
@@ -167,7 +167,8 @@
 class Writer:
   "Simple class for writing strings/binary, with optional encoding."
 
-  def __init__(self, encoding):
+  def __init__(self, maxbytes, encoding):
+    self.maxbytes = maxbytes
     self.buffer = BytesIO()
 
     # Attach a couple functions to SELF, rather than methods.
@@ -181,7 +182,15 @@
       def _write(s):
         "Write text string S using the *default* encoding (utf-8)."
         return self.buffer.write(to_bytes(s))
-    self.write = _write
+
+    def write_limited(s):
+        # If it looks like this write() will surpass the maximum length,
+        # then bail out.
+        if len(self.buffer.getbuffer()) + len(s) > self.maxbytes:
+            raise MessageTooLarge
+        return _write(s)
+
+    self.write = write_limited
 
 
 class OutputBase:
@@ -193,14 +202,22 @@
     self._CHUNKSIZE = 128 * 1024
 
   def send(self, subject_line, group, params, long_func, short_func):
-      writer = Writer(self.get_encoding())
+      ### get the MAXBYTEs from the configuration
+      writer = Writer(90000, self.get_encoding())
 
       try:
           try:
               long_func(writer)
           except MessageTooLarge:
               writer.buffer.truncate(0)
-              short_func(writer)
+              writer.buffer.seek(0)
+              try:
+                  short_func(writer)
+              except MessageTooLarge:
+                  # NOTE: don't use the Writer() API, or it will check the
+                  # length again. Reach inside.
+                  writer.buffer.write(b'\n\n\n... message too long. Truncated.\n')
+                  # FALLTHRU
       except MessageSendFailure:
         return True  # failed
 
@@ -819,6 +836,11 @@
   render_commit(w, wb, data)
 
 
+def generate_urls(writer, cfg, repos, changelist, group, params, paths,
+                  pool):
+    writer.write('COMMIT MESSAGE USING URLS\n')
+
+
 def generate_summary(changelist, paths, in_paths):
   def gather_info(action):
     return _gather_paths(action, changelist, paths, in_paths)