+CORNERCHARS_DOUBLE = ['╚', '╝', '╗', '╔']
+CORNERCHARS_SINGLE = ['└', '┘', '┐', '┌']
+LINECHARS_DOUBLE = ['║', '═']
+LINECHARS_SINGLE = ['│', '─']
+
+class Output:
+
+ def __init__(self):
+ self.content = ""
+ self.destination = ""
+
+ def append(self, content):
+ self.content += content
+
+ def write(self, destination=""):
+ if destination == "":
+ destination = self.destination
+ if destination == "":
+ logger.warning("No destination path provided")
+ return 1
+ with open(destination, 'w') as f:
+ f.write(self.content)
+ logger.info("Written output to {}".format(destination))
+
+
+class PlaintextOutput(Output):
+
+ def __init__(self, linewidth=80):
+ self.content = ""
+ self.destination = ""
+ self.linewidth = linewidth;
+
+ def append_header(self, template=''):
+ init_varfilter()
+ box = PlaintextBox(content=Template("$title $version on $hostname\n\n$time $date").safe_substitute(varsubst), vpadding=2, hpadding="\t\t", linewidth=config.prefs['linewidth'])
+ line = PlaintextLine(self.linewidth)
+ self.append(box.draw() + line.draw())
+
+ def append_footer(self):
+ init_varfilter()
+ self.append(PlaintextLine(self.linewidth, vpadding=1).draw())
+ self.append(Template("$hostname $time $date").safe_substitute(varsubst))
+
+ def append_section(self, section):
+ self.append(PlaintextBox(content=section.title, double=False, fullwidth=False, vpadding=0, hpadding=" ").draw())
+ self.append('\n'*2)
+ for data in section.data:
+ self.append(self._fmt_data(data.subtitle, data.items))
+ self.append('\n')
+
+ def _fmt_data(self, subtitle, data = None): # write title and data
+ if (subtitle == ""):
+ logger.warning("No subtitle provided.. skipping section")
+ return
+
+ if (data == None or len(data) == 0):
+ logger.debug("No data provided.. just printing subtitle")
+ return subtitle + '\n'
+ else:
+ logger.debug("Received data " + str(data))
+ subtitle += ':'
+ if (len(data) == 1):
+ return subtitle + ' ' + data[0] + '\n'
+ else:
+ itemoutput = subtitle + '\n'
+ for datum in data:
+ datum = '• ' + datum
+ if len(datum) > config.prefs['linewidth']:
+ words = datum.split()
+ if max(map(len, words)) > config.prefs['linewidth']:
+ raise ValueError("Content width is too small")
+ res, part, others = [], words[0], words[1:]
+ for word in others:
+ if len(' ') + len(word) > config.prefs['linewidth'] - len(part):
+ res.append(part)
+ part = word
+ else:
+ part += ' ' + word
+ if part:
+ res.append(part)
+ datum = '\n'.join(res)
+ itemoutput += datum + '\n'
+ return itemoutput
+
+
+class HtmlOutput(Output):
+
+ def __init__(self):
+ self.content = ""
+ self.destination = ""
+ self.css = ""
+
+ def embed_css(self, css):
+ self.content = mail.mailprep(self.content, css)
+
+ def append_header(self, template): # insert variables into header template file
+ init_varfilter()
+ headercontent = Template(open(template, 'r').read())
+ self.append(headercontent.safe_substitute(varsubst))
+ self.append(opentag('div', id='main'))
+
+ def append_footer(self):
+ self.append(closetag('div') + closetag('body') + closetag('html'))
+
+ def append_section(self, section):
+ self.append(opentag('div', 1, section.title, 'section'))
+ self.append(self._gen_title(section.title))
+ for data in section.data:
+ self.append(self._fmt_data(data.subtitle, data.items))
+ self.append(closetag('div', 1))
+
+ def _gen_title(self, title): # write title for a section
+ if (title == '' or '\n' in title):
+ logger.error("Invalid title")
+ raise ValueError
+ logger.debug("Writing title for " + title)
+ return tag('h2', 0, title)
+
+ def _fmt_data(self, subtitle, data = None): # write title and data
+ if (subtitle == ""):
+ logger.warning("No subtitle provided.. skipping section")
+ return
+
+ if (data == None or len(data) == 0):
+ logger.debug("No data provided.. just printing subtitle")
+ return tag('p', 0, subtitle)
+ else:
+ logger.debug("Received data " + str(data))
+ subtitle += ':'
+ if (len(data) == 1):
+ return tag('p', 0, subtitle + ' ' + data[0])
+ else:
+ output = ""
+ output += tag('p', 0, subtitle)
+ output += opentag('ul', 1)
+ coderegex = re.compile('`(.*)`')
+ for datum in data:
+ if datum == "" or datum == None:
+ continue
+ datum = coderegex.sub(r'<code>{\1}</code>', str(datum))
+ output += tag('li', 0, datum)
+ output += closetag('ul', 1)
+ return output
+
+
+class Section:
+
+ def __init__(self, title):
+ self.title = title
+ self.data = []
+
+ def add_data(self, data):
+ self.data.append(data)
+
+class Data:
+
+ def __init__(self, subtitle, items=None):
+ self.subtitle = subtitle
+ self.items = items
+
+
+class PlaintextLine:
+
+ def __init__(self, linewidth=80, double=True, vpadding=1, hpadding=""):
+ self.linewidth = linewidth
+ self.double = False
+ self.vpadding = vpadding
+ self.hpadding = hpadding
+
+ def draw(self):
+ line = (LINECHARS_DOUBLE[1] if self.double else LINECHARS_SINGLE[1])
+ return "\n" * self.vpadding + self.hpadding + line * (self.linewidth - 2 * len(self.hpadding)) + self.hpadding + "\n" * self.vpadding
+
+class PlaintextBox:
+
+ def __init__(self, content="", double=True, fullwidth=True, linewidth=80, hpadding="\t", vpadding=1):
+ self.content = content
+ self.fullwidth = fullwidth
+ self.linewidth = linewidth
+ self.hpadding = hpadding
+ self.vpadding = vpadding
+ self.double = double
+
+ def draw(self):
+
+ if self.double == True:
+ cornerchars = CORNERCHARS_DOUBLE
+ linechars = LINECHARS_DOUBLE
+ else:
+ cornerchars = CORNERCHARS_SINGLE
+ linechars = LINECHARS_SINGLE
+
+ # Check hpadding has a definite width
+ self.hpadding = self.hpadding.replace("\t", " "*4)
+
+ # Calculate number of characters per line
+ contentlines = self.content.splitlines()
+ contentwidth = int((self.linewidth if self.linewidth > 0 else 80) if self.content.splitlines() else len(max(contentlines, key=len)))
+ logger.debug("Contentwidth is {0}".format(str(contentwidth)))
+ logger.debug("Longest line is {0}".format(len(max(contentlines, key=len))))
+ contentwidth += -2*(len(self.hpadding)+1)
+ if not self.fullwidth:
+ longestline = len(max(contentlines, key=len))
+ if longestline <= self.linewidth - 2*(len(self.hpadding)+1):
+ contentwidth = longestline
+
+ # Split lines that are too long
+ for i, line in enumerate(contentlines):
+ if len(line) > contentwidth:
+ words = line.split()
+ if max(map(len, words)) > contentwidth:
+ raise ValueError("Content width is too small")
+ res, part, others = [], words[0], words[1:]
+ for word in others:
+ if len(' ') + len(word) > contentwidth - len(part):
+ res.append(part)
+ part = word
+ else:
+ part += ' ' + word
+ if part:
+ res.append(part)
+ contentlines[i] = res
+
+ # Flatten list
+ # Note list comprehension doesn't work here, so we must iterate through each item
+ newlines = []
+ for line in contentlines:
+ if isinstance(line, list):
+ for subline in line:
+ newlines.append(subline)
+ else:
+ newlines.append(line)
+ contentlines = newlines
+
+ # Add vertical padding
+ for _ in range(self.vpadding):
+ contentlines.insert(0, ' '*contentwidth)
+ contentlines.append(' '*contentwidth)
+
+ # Insert horizontal padding on lines that are too short
+ contentlines = [linechars[0] + self.hpadding + x + ' '*(self.linewidth-(len(x)+2*len(self.hpadding)+2) if len(x) < contentwidth else 0) + self.hpadding + linechars[0] for x in contentlines]
+ contentlines.insert(0, cornerchars[3] + linechars[1] * (contentwidth + len(self.hpadding)*2) + cornerchars[2])
+ contentlines.append(cornerchars[0] + linechars[1] * (contentwidth + len(self.hpadding)*2) + cornerchars[1])
+ return ('\n').join(contentlines)