def getLabel(self, optionWidth, valueWidth, summaryWidth):
        """
    Provides display string of the configuration entry with the given
    constraints on the width of the contents.
    
    Arguments:
      optionWidth  - width of the option column
      valueWidth   - width of the value column
      summaryWidth - width of the summary column
    """

        # Fetching the display entries is very common so this caches the values.
        # Doing this substantially drops cpu usage when scrolling (by around 40%).

        argSet = (optionWidth, valueWidth, summaryWidth)
        if not self.labelCache or self.labelCacheArgs != argSet:
            optionLabel = uiTools.cropStr(self.get(Field.OPTION), optionWidth)
            valueLabel = uiTools.cropStr(self.get(Field.VALUE), valueWidth)
            summaryLabel = uiTools.cropStr(self.get(Field.SUMMARY),
                                           summaryWidth, None)
            lineTextLayout = "%%-%is %%-%is %%-%is" % (optionWidth, valueWidth,
                                                       summaryWidth)
            self.labelCache = lineTextLayout % (optionLabel, valueLabel,
                                                summaryLabel)
            self.labelCacheArgs = argSet

        return self.labelCache
示例#2
0
    def _drawSelectionPanel(self, selection, width, detailPanelHeight, isScrollbarVisible):
        """
    Renders a panel for the selected configuration option.
    """

        # This is a solid border unless the scrollbar is visible, in which case a
        # 'T' pipe connects the border to the bar.
        uiTools.drawBox(self, 0, 0, width, detailPanelHeight + 1)
        if isScrollbarVisible:
            self.addch(detailPanelHeight, 1, curses.ACS_TTEE)

        selectionFormat = curses.A_BOLD | uiTools.getColor(CATEGORY_COLOR[selection.get(Field.CATEGORY)])

        # first entry:
        # <option> (<category> Option)
        optionLabel = " (%s Option)" % selection.get(Field.CATEGORY)
        self.addstr(1, 2, selection.get(Field.OPTION) + optionLabel, selectionFormat)

        # second entry:
        # Value: <value> ([default|custom], <type>, usage: <argument usage>)
        if detailPanelHeight >= 3:
            valueAttr = []
            valueAttr.append("default" if selection.get(Field.IS_DEFAULT) else "custom")
            valueAttr.append(selection.get(Field.TYPE))
            valueAttr.append("usage: %s" % (selection.get(Field.ARG_USAGE)))
            valueAttrLabel = ", ".join(valueAttr)

            valueLabelWidth = width - 12 - len(valueAttrLabel)
            valueLabel = uiTools.cropStr(selection.get(Field.VALUE), valueLabelWidth)

            self.addstr(2, 2, "Value: %s (%s)" % (valueLabel, valueAttrLabel), selectionFormat)

        # remainder is filled with the man page description
        descriptionHeight = max(0, detailPanelHeight - 3)
        descriptionContent = "Description: " + selection.get(Field.DESCRIPTION)

        for i in range(descriptionHeight):
            # checks if we're done writing the description
            if not descriptionContent:
                break

            # there's a leading indent after the first line
            if i > 0:
                descriptionContent = "  " + descriptionContent

            # we only want to work with content up until the next newline
            if "\n" in descriptionContent:
                lineContent, descriptionContent = descriptionContent.split("\n", 1)
            else:
                lineContent, descriptionContent = descriptionContent, ""

            if i != descriptionHeight - 1:
                # there's more lines to display
                msg, remainder = uiTools.cropStr(lineContent, width - 3, 4, 4, uiTools.Ending.HYPHEN, True)
                descriptionContent = remainder.strip() + descriptionContent
            else:
                # this is the last line, end it with an ellipse
                msg = uiTools.cropStr(lineContent, width - 3, 4, 4)

            self.addstr(3 + i, 2, msg, selectionFormat)
示例#3
0
 def _drawSelectionPanel(self, selection, width, detailPanelHeight, isScrollbarVisible):
   """
   Renders a panel for the selected configuration option.
   """
   
   # This is a solid border unless the scrollbar is visible, in which case a
   # 'T' pipe connects the border to the bar.
   uiTools.drawBox(self, 0, 0, width, detailPanelHeight + 1)
   if isScrollbarVisible: self.addch(detailPanelHeight, 1, curses.ACS_TTEE)
   
   selectionFormat = curses.A_BOLD | uiTools.getColor(CATEGORY_COLOR[selection.get(Field.CATEGORY)])
   
   # first entry:
   # <option> (<category> Option)
   optionLabel =" (%s Option)" % selection.get(Field.CATEGORY)
   self.addstr(1, 2, selection.get(Field.OPTION) + optionLabel, selectionFormat)
   
   # second entry:
   # Value: <value> ([default|custom], <type>, usage: <argument usage>)
   if detailPanelHeight >= 3:
     valueAttr = []
     valueAttr.append("default" if selection.get(Field.IS_DEFAULT) else "custom")
     valueAttr.append(selection.get(Field.TYPE))
     valueAttr.append("usage: %s" % (selection.get(Field.ARG_USAGE)))
     valueAttrLabel = ", ".join(valueAttr)
     
     valueLabelWidth = width - 12 - len(valueAttrLabel)
     valueLabel = uiTools.cropStr(selection.get(Field.VALUE), valueLabelWidth)
     
     self.addstr(2, 2, "Value: %s (%s)" % (valueLabel, valueAttrLabel), selectionFormat)
   
   # remainder is filled with the man page description
   descriptionHeight = max(0, detailPanelHeight - 3)
   descriptionContent = "Description: " + selection.get(Field.DESCRIPTION)
   
   for i in range(descriptionHeight):
     # checks if we're done writing the description
     if not descriptionContent: break
     
     # there's a leading indent after the first line
     if i > 0: descriptionContent = "  " + descriptionContent
     
     # we only want to work with content up until the next newline
     if "\n" in descriptionContent:
       lineContent, descriptionContent = descriptionContent.split("\n", 1)
     else: lineContent, descriptionContent = descriptionContent, ""
     
     if i != descriptionHeight - 1:
       # there's more lines to display
       msg, remainder = uiTools.cropStr(lineContent, width - 3, 4, 4, uiTools.Ending.HYPHEN, True)
       descriptionContent = remainder.strip() + descriptionContent
     else:
       # this is the last line, end it with an ellipse
       msg = uiTools.cropStr(lineContent, width - 3, 4, 4)
     
     self.addstr(3 + i, 2, msg, selectionFormat)
示例#4
0
 def draw(self, subwindow, width, height):
   self.valsLock.acquire()
   
   # draws the top label
   titleLabel = "%s Configuration:" % ("Tor" if self.configType == TOR_STATE else "Arm")
   self.addstr(0, 0, titleLabel, curses.A_STANDOUT)
   
   # panel with details for the current selection
   detailPanelHeight = self._config["features.config.selectionDetails.height"]
   if detailPanelHeight == 0 or detailPanelHeight + 2 >= height:
     # no detail panel
     detailPanelHeight = 0
     scrollLoc = self.scroller.getScrollLoc(self.confContents, height - 1)
     cursorSelection = self.getSelection()
   else:
     # Shrink detail panel if there isn't sufficient room for the whole
     # thing. The extra line is for the bottom border.
     detailPanelHeight = min(height - 1, detailPanelHeight + 1)
     scrollLoc = self.scroller.getScrollLoc(self.confContents, height - 1 - detailPanelHeight)
     cursorSelection = self.getSelection()
     
     self._drawSelectionPanel(cursorSelection, width, detailPanelHeight, titleLabel)
   
   # draws left-hand scroll bar if content's longer than the height
   scrollOffset = 0
   if len(self.confContents) > height - detailPanelHeight - 1:
     scrollOffset = 3
     self.addScrollBar(scrollLoc, scrollLoc + height - detailPanelHeight - 1, len(self.confContents), 1 + detailPanelHeight)
   
   optionWidth = self._config["features.config.state.colWidth.option"]
   valueWidth = self._config["features.config.state.colWidth.value"]
   descriptionWidth = max(0, width - scrollOffset - optionWidth - valueWidth - 2)
   
   for lineNum in range(scrollLoc, len(self.confContents)):
     entry = self.confContents[lineNum]
     drawLine = lineNum + detailPanelHeight + 1 - scrollLoc
     
     optionLabel = uiTools.cropStr(entry.get(FIELD_OPTION), optionWidth)
     valueLabel = uiTools.cropStr(entry.get(FIELD_VALUE), valueWidth)
     
     # ends description at the first newline
     descriptionLabel = uiTools.cropStr(entry.get(FIELD_DESCRIPTION).split("\n")[0], descriptionWidth, None)
     
     lineFormat = curses.A_NORMAL if entry.get(FIELD_IS_DEFAULT) else curses.A_BOLD
     if entry.get(FIELD_CATEGORY): lineFormat |= uiTools.getColor(CATEGORY_COLOR[entry.get(FIELD_CATEGORY)])
     if entry == cursorSelection: lineFormat |= curses.A_STANDOUT
     
     lineTextLayout = "%%-%is %%-%is %%-%is" % (optionWidth, valueWidth, descriptionWidth)
     lineText = lineTextLayout % (optionLabel, valueLabel, descriptionLabel)
     self.addstr(drawLine, scrollOffset, lineText, lineFormat)
     
     if drawLine >= height: break
   
   self.valsLock.release()
示例#5
0
 def getLabel(self, optionWidth, valueWidth, summaryWidth):
   """
   Provides display string of the configuration entry with the given
   constraints on the width of the contents.
   
   Arguments:
     optionWidth  - width of the option column
     valueWidth   - width of the value column
     summaryWidth - width of the summary column
   """
   
   # Fetching the display entries is very common so this caches the values.
   # Doing this substantially drops cpu usage when scrolling (by around 40%).
   
   argSet = (optionWidth, valueWidth, summaryWidth)
   if not self.labelCache or self.labelCacheArgs != argSet:
     optionLabel = uiTools.cropStr(self.get(Field.OPTION), optionWidth)
     valueLabel = uiTools.cropStr(self.get(Field.VALUE), valueWidth)
     summaryLabel = uiTools.cropStr(self.get(Field.SUMMARY), summaryWidth, None)
     lineTextLayout = "%%-%is %%-%is %%-%is" % (optionWidth, valueWidth, summaryWidth)
     self.labelCache = lineTextLayout % (optionLabel, valueLabel, summaryLabel)
     self.labelCacheArgs = argSet
   
   return self.labelCache
示例#6
0
文件: wizard.py 项目: JustMe23/arm
def _splitStr(msg, width):
  """
  Splits a string into substrings of a given length.
  
  Arguments:
    msg   - string to be broken up
    width - max length of any returned substring
  """
  
  results = []
  while msg:
    msgSegment, msg = uiTools.cropStr(msg, width, None, endType = None, getRemainder = True)
    if not msgSegment: break # happens if the width is less than the first word
    results.append(msgSegment.strip())
  
  return results
示例#7
0
文件: circEntry.py 项目: lnrsoft/arm
    def _getListingEntry(self, width, currentTime, listingType):
        lineFormat = uiTools.getColor(connEntry.CATEGORY_COLOR[self.getType()])

        # The required widths are the sum of the following:
        # initial space (1 character)
        # bracketing (3 characters)
        # placementLabel (14 characters)
        # gap between etc and placement label (5 characters)

        baselineSpace = 14 + 5

        dst, etc = "", ""
        if listingType == entries.ListingType.IP_ADDRESS:
            # TODO: include hostname when that's available
            # dst width is derived as:
            # src (21) + dst (26) + divider (7) + right gap (2) - bracket (3) = 53 char
            dst = "%-53s" % self.getDestinationLabel(53, includeLocale=True)

            # fills the nickname into the empty space here
            dst = "%s%-25s   " % (
                dst[:25], uiTools.cropStr(self.foreign.getNickname(), 25, 0))

            etc = self.getEtcContent(width - baselineSpace - len(dst),
                                     listingType)
        elif listingType == entries.ListingType.HOSTNAME:
            # min space for the hostname is 40 characters
            etc = self.getEtcContent(width - baselineSpace - 40, listingType)
            dstLayout = "%%-%is" % (width - baselineSpace - len(etc))
            dst = dstLayout % self.foreign.getHostname(
                self.foreign.getIpAddr())
        elif listingType == entries.ListingType.FINGERPRINT:
            # dst width is derived as:
            # src (9) + dst (40) + divider (7) + right gap (2) - bracket (3) = 55 char
            dst = "%-55s" % self.foreign.getFingerprint()
            etc = self.getEtcContent(width - baselineSpace - len(dst),
                                     listingType)
        else:
            # min space for the nickname is 56 characters
            etc = self.getEtcContent(width - baselineSpace - 56, listingType)
            dstLayout = "%%-%is" % (width - baselineSpace - len(etc))
            dst = dstLayout % self.foreign.getNickname()

        return ((dst + etc, lineFormat),
                (" " * (width - baselineSpace - len(dst) - len(etc) + 5),
                 lineFormat), ("%-14s" % self.placementLabel, lineFormat))
示例#8
0
文件: circEntry.py 项目: JustMe23/arm
 def _getListingEntry(self, width, currentTime, listingType):
   lineFormat = uiTools.getColor(connEntry.CATEGORY_COLOR[self.getType()])
   
   # The required widths are the sum of the following:
   # initial space (1 character)
   # bracketing (3 characters)
   # placementLabel (14 characters)
   # gap between etc and placement label (5 characters)
   
   baselineSpace = 14 + 5
   
   dst, etc = "", ""
   if listingType == entries.ListingType.IP_ADDRESS:
     # TODO: include hostname when that's available
     # dst width is derived as:
     # src (21) + dst (26) + divider (7) + right gap (2) - bracket (3) = 53 char
     dst = "%-53s" % self.getDestinationLabel(53, includeLocale = True)
     
     # fills the nickname into the empty space here
     dst = "%s%-25s   " % (dst[:25], uiTools.cropStr(self.foreign.getNickname(), 25, 0))
     
     etc = self.getEtcContent(width - baselineSpace - len(dst), listingType)
   elif listingType == entries.ListingType.HOSTNAME:
     # min space for the hostname is 40 characters
     etc = self.getEtcContent(width - baselineSpace - 40, listingType)
     dstLayout = "%%-%is" % (width - baselineSpace - len(etc))
     dst = dstLayout % self.foreign.getHostname(self.foreign.getIpAddr())
   elif listingType == entries.ListingType.FINGERPRINT:
     # dst width is derived as:
     # src (9) + dst (40) + divider (7) + right gap (2) - bracket (3) = 55 char
     dst = "%-55s" % self.foreign.getFingerprint()
     etc = self.getEtcContent(width - baselineSpace - len(dst), listingType)
   else:
     # min space for the nickname is 56 characters
     etc = self.getEtcContent(width - baselineSpace - 56, listingType)
     dstLayout = "%%-%is" % (width - baselineSpace - len(etc))
     dst = dstLayout % self.foreign.getNickname()
   
   return ((dst + etc, lineFormat),
           (" " * (width - baselineSpace - len(dst) - len(etc) + 5), lineFormat),
           ("%-14s" % self.placementLabel, lineFormat))
示例#9
0
def _splitStr(msg, width):
    """
  Splits a string into substrings of a given length.
  
  Arguments:
    msg   - string to be broken up
    width - max length of any returned substring
  """

    results = []
    while msg:
        msgSegment, msg = uiTools.cropStr(msg,
                                          width,
                                          None,
                                          endType=None,
                                          getRemainder=True)
        if not msgSegment:
            break  # happens if the width is less than the first word
        results.append(msgSegment.strip())

    return results
示例#10
0
    def doHelp(self, arg, outputEntry):
        """
    Performs the '/help' operation, giving usage information for the given
    argument or a general summary if there wasn't one.
    """

        arg = arg.upper()

        # If there's multiple arguments then just take the first. This is
        # particularly likely if they're trying to query a full command (for
        # instance "/help GETINFO version")

        arg = arg.split(" ")[0]

        # strip slash if someone enters an interpretor command (ex. "/help /help")
        if arg.startswith("/"): arg = arg[1:]

        if arg:
            if arg in HELP_OPTIONS:
                # Provides information for the tor or interpretor argument. This bolds
                # the usage information and indents the description after it.
                usage, description = HELP_OPTIONS[arg]

                outputEntry.append(
                    (usage + "\n", OUTPUT_FORMAT + (Attr.BOLD, )))

                for line in description.split("\n"):
                    outputEntry.append(("  " + line + "\n", OUTPUT_FORMAT))

                if arg == "GETINFO":
                    # if this is the GETINFO option then also list the valid options
                    infoOptions = torTools.getConn().getInfo("info/names")

                    if infoOptions:
                        for line in infoOptions.split("\n"):
                            if line.startswith("config/*") or line.startswith(
                                    "dir-usage"):
                                continue

                            lineMatch = re.match("^(.+) -- (.+)$", line)

                            if lineMatch:
                                opt, description = lineMatch.groups()

                                outputEntry.append(
                                    ("%-33s" % opt,
                                     OUTPUT_FORMAT + (Attr.BOLD, )))
                                outputEntry.append(
                                    (" - %s\n" % description, OUTPUT_FORMAT))
                elif arg == "GETCONF":
                    # lists all of the configuration options

                    confOptions = torTools.getConn().getInfo("config/names")
                    if confOptions:
                        confEntries = [
                            opt.split(" ", 1)[0]
                            for opt in confOptions.split("\n")
                        ]

                        # displays two columns of 42 characters
                        for i in range(0, len(confEntries), 2):
                            lineEntries = confEntries[i:i + 2]

                            lineContent = ""
                            for entry in lineEntries:
                                lineContent += "%-42s" % entry

                            outputEntry.append(
                                (lineContent + "\n", OUTPUT_FORMAT))

                        outputEntry.append((
                            "For more information use '/help [CONFIG OPTION]'.",
                            OUTPUT_FORMAT + (Attr.BOLD, )))
                elif arg == "SIGNAL":
                    # lists descriptions for all of the signals
                    for signal, description in SIGNAL_DESCRIPTIONS:
                        outputEntry.append(
                            ("%-15s" % signal, OUTPUT_FORMAT + (Attr.BOLD, )))
                        outputEntry.append(
                            (" - %s\n" % description, OUTPUT_FORMAT))
                elif arg == "SETEVENTS":
                    # lists all of the event types
                    eventOptions = torTools.getConn().getInfo("events/names")
                    if eventOptions:
                        eventEntries = eventOptions.split()

                        # displays four columns of 20 characters
                        for i in range(0, len(eventEntries), 4):
                            lineEntries = eventEntries[i:i + 4]

                            lineContent = ""
                            for entry in lineEntries:
                                lineContent += "%-20s" % entry

                            outputEntry.append(
                                (lineContent + "\n", OUTPUT_FORMAT))
                elif arg == "USEFEATURE":
                    # lists the feature options
                    featureOptions = torTools.getConn().getInfo(
                        "features/names")
                    if featureOptions:
                        outputEntry.append(
                            (featureOptions + "\n", OUTPUT_FORMAT))
                elif arg in ("LOADCONF", "POSTDESCRIPTOR"):
                    # gives a warning that this option isn't yet implemented
                    outputEntry.append(
                        ("\n" + MULTILINE_UNIMPLEMENTED_NOTICE + "\n",
                         ERROR_FORMAT))
            else:
                # check if this is a configuration option
                manEntry = torConfig.getConfigDescription(arg)

                if manEntry:
                    # provides basic usage information in bold, followed an indented
                    # copy of the man page description (wrapped to eighty characters)

                    helpTitle = "%s %s (category: %s)\n" % (
                        manEntry.option, manEntry.argUsage, manEntry.category)
                    outputEntry.append(
                        (helpTitle, OUTPUT_FORMAT + (Attr.BOLD, )))

                    descLines = manEntry.description.split("\n")

                    for line in descLines:
                        if not line:
                            outputEntry.append(("\n", OUTPUT_FORMAT))
                        else:
                            while line:
                                drawPortion, line = uiTools.cropStr(
                                    line, 88, 4, 4, uiTools.Ending.HYPHEN,
                                    True)
                                outputEntry.append(
                                    ("  %s\n" % drawPortion.strip(),
                                     OUTPUT_FORMAT))
                else:
                    outputEntry.append(
                        ("No help information available for '%s'..." % arg,
                         ERROR_FORMAT))
        else:
            # provides the GENERAL_HELP with everything bolded except descriptions
            for line in GENERAL_HELP.split("\n"):
                cmdStart = line.find(" - ")

                if cmdStart != -1:
                    outputEntry.append(
                        (line[:cmdStart], OUTPUT_FORMAT + (Attr.BOLD, )))
                    outputEntry.append((line[cmdStart:] + "\n", OUTPUT_FORMAT))
                else:
                    outputEntry.append(
                        (line + "\n", OUTPUT_FORMAT + (Attr.BOLD, )))
示例#11
0
文件: logPanel.py 项目: zhou-kz/arm
    def draw(self, subwindow, width, height):
        """
    Redraws message log. Entries stretch to use available space and may
    contain up to two lines. Starts with newest entries.
    """

        self.valsLock.acquire()
        self._lastLoggedEvents, self._lastUpdate = list(
            self.msgLog), time.time()

        # draws the top label
        self.addstr(0, 0, self._getTitle(width), curses.A_STANDOUT)

        # restricts scroll location to valid bounds
        self.scroll = max(
            0, min(self.scroll, self.lastContentHeight - height + 1))

        # draws left-hand scroll bar if content's longer than the height
        msgIndent, dividerIndent = 0, 0  # offsets for scroll bar
        isScrollBarVisible = self.lastContentHeight > height - 1
        if isScrollBarVisible:
            msgIndent, dividerIndent = 3, 2
            self.addScrollBar(self.scroll, self.scroll + height - 1,
                              self.lastContentHeight, 1)

        # draws log entries
        lineCount = 1 - self.scroll
        seenFirstDateDivider = False
        dividerAttr, duplicateAttr = curses.A_BOLD | uiTools.getColor(
            "yellow"), curses.A_BOLD | uiTools.getColor("green")

        isDatesShown = self.regexFilter == None and self._config[
            "features.log.showDateDividers"]
        eventLog = getDaybreaks(
            self.msgLog, self._isPaused) if isDatesShown else list(self.msgLog)
        if not self.showDuplicates:
            deduplicatedLog = getDuplicates(eventLog)

            if deduplicatedLog == None:
                msg = "Deduplication took too long. Its current implementation has difficulty handling large logs so disabling it to keep the interface responsive."
                log.log(log.WARN, msg)
                self.showDuplicates = True
                deduplicatedLog = [(entry, 0) for entry in eventLog]
        else:
            deduplicatedLog = [(entry, 0) for entry in eventLog]

        # determines if we have the minimum width to show date dividers
        showDaybreaks = width - dividerIndent >= 3

        while deduplicatedLog:
            entry, duplicateCount = deduplicatedLog.pop(0)

            if self.regexFilter and not self.regexFilter.search(
                    entry.getDisplayMessage()):
                continue  # filter doesn't match log message - skip

            # checks if we should be showing a divider with the date
            if entry.type == DAYBREAK_EVENT:
                # bottom of the divider
                if seenFirstDateDivider:
                    if lineCount >= 1 and lineCount < height and showDaybreaks:
                        self.win.vline(lineCount, dividerIndent,
                                       curses.ACS_LLCORNER | dividerAttr, 1)
                        self.win.hline(lineCount, dividerIndent + 1,
                                       curses.ACS_HLINE | dividerAttr,
                                       width - dividerIndent - 1)
                        self.win.vline(lineCount, width,
                                       curses.ACS_LRCORNER | dividerAttr, 1)

                    lineCount += 1

                # top of the divider
                if lineCount >= 1 and lineCount < height and showDaybreaks:
                    timeLabel = time.strftime(" %B %d, %Y ",
                                              time.localtime(entry.timestamp))
                    self.win.vline(lineCount, dividerIndent,
                                   curses.ACS_ULCORNER | dividerAttr, 1)
                    self.win.hline(lineCount, dividerIndent + 1,
                                   curses.ACS_HLINE | dividerAttr, 1)
                    self.addstr(lineCount, dividerIndent + 2, timeLabel,
                                curses.A_BOLD | dividerAttr)

                    if dividerIndent + len(timeLabel) + 2 <= width:
                        lineLength = width - dividerIndent - len(timeLabel) - 2
                        self.win.hline(lineCount,
                                       dividerIndent + len(timeLabel) + 2,
                                       curses.ACS_HLINE | dividerAttr,
                                       lineLength)
                        self.win.vline(
                            lineCount,
                            dividerIndent + len(timeLabel) + 2 + lineLength,
                            curses.ACS_URCORNER | dividerAttr, 1)

                seenFirstDateDivider = True
                lineCount += 1
            else:
                # entry contents to be displayed, tuples of the form:
                # (msg, formatting, includeLinebreak)
                displayQueue = []

                msgComp = entry.getDisplayMessage().split("\n")
                for i in range(len(msgComp)):
                    font = curses.A_BOLD if "ERR" in entry.type else curses.A_NORMAL  # emphasizes ERR messages
                    displayQueue.append((msgComp[i].strip(),
                                         font | uiTools.getColor(entry.color),
                                         i != len(msgComp) - 1))

                if duplicateCount:
                    pluralLabel = "s" if duplicateCount > 1 else ""
                    duplicateMsg = DUPLICATE_MSG % (duplicateCount,
                                                    pluralLabel)
                    displayQueue.append((duplicateMsg, duplicateAttr, False))

                cursorLoc, lineOffset = msgIndent, 0
                maxEntriesPerLine = self._config[
                    "features.log.maxLinesPerEntry"]
                while displayQueue:
                    msg, format, includeBreak = displayQueue.pop(0)
                    drawLine = lineCount + lineOffset
                    if lineOffset == maxEntriesPerLine: break

                    maxMsgSize = width - cursorLoc
                    if len(msg) > maxMsgSize:
                        # message is too long - break it up
                        if lineOffset == maxEntriesPerLine - 1:
                            msg = uiTools.cropStr(msg, maxMsgSize)
                        else:
                            msg, remainder = uiTools.cropStr(
                                msg, maxMsgSize, 4, 4, uiTools.END_WITH_HYPHEN,
                                True)
                            displayQueue.insert(
                                0, (remainder.strip(), format, includeBreak))

                        includeBreak = True

                    if drawLine < height and drawLine >= 1:
                        if seenFirstDateDivider and width - dividerIndent >= 3 and showDaybreaks:
                            self.win.vline(drawLine, dividerIndent,
                                           curses.ACS_VLINE | dividerAttr, 1)
                            self.win.vline(drawLine, width,
                                           curses.ACS_VLINE | dividerAttr, 1)

                        self.addstr(drawLine, cursorLoc, msg, format)

                    cursorLoc += len(msg)

                    if includeBreak or not displayQueue:
                        lineOffset += 1
                        cursorLoc = msgIndent + ENTRY_INDENT

                lineCount += lineOffset

            # if this is the last line and there's room, then draw the bottom of the divider
            if not deduplicatedLog and seenFirstDateDivider:
                if lineCount < height and showDaybreaks:
                    # when resizing with a small width the following entries can be
                    # problematc (though I'm not sure why)
                    try:
                        self.win.vline(lineCount, dividerIndent,
                                       curses.ACS_LLCORNER | dividerAttr, 1)
                        self.win.hline(lineCount, dividerIndent + 1,
                                       curses.ACS_HLINE | dividerAttr,
                                       width - dividerIndent - 1)
                        self.win.vline(lineCount, width,
                                       curses.ACS_LRCORNER | dividerAttr, 1)
                    except:
                        pass

                lineCount += 1

        # redraw the display if...
        # - lastContentHeight was off by too much
        # - we're off the bottom of the page
        newContentHeight = lineCount + self.scroll - 1
        contentHeightDelta = abs(self.lastContentHeight - newContentHeight)
        forceRedraw, forceRedrawReason = True, ""

        if contentHeightDelta >= CONTENT_HEIGHT_REDRAW_THRESHOLD:
            forceRedrawReason = "estimate was off by %i" % contentHeightDelta
        elif newContentHeight > height and self.scroll + height - 1 > newContentHeight:
            forceRedrawReason = "scrolled off the bottom of the page"
        elif not isScrollBarVisible and newContentHeight > height - 1:
            forceRedrawReason = "scroll bar wasn't previously visible"
        elif isScrollBarVisible and newContentHeight <= height - 1:
            forceRedrawReason = "scroll bar shouldn't be visible"
        else:
            forceRedraw = False

        self.lastContentHeight = newContentHeight
        if forceRedraw:
            forceRedrawReason = "redrawing the log panel with the corrected content height (%s)" % forceRedrawReason
            log.log(self._config["log.logPanel.forceDoubleRedraw"],
                    forceRedrawReason)
            self.redraw(True)

        self.valsLock.release()
示例#12
0
def draw(popup, fingerprint, displayText, displayColor, scroll, showLineNumber):
  popup.win.erase()
  popup.win.box()
  xOffset = 2
  
  if fingerprint: title = "Consensus Descriptor (%s):" % fingerprint
  else: title = "Consensus Descriptor:"
  popup.addstr(0, 0, title, curses.A_STANDOUT)
  
  lineNumWidth = int(math.log10(len(displayText))) + 1
  isEncryptionBlock = False   # flag indicating if we're currently displaying a key
  
  # checks if first line is in an encryption block
  for i in range(0, scroll):
    lineText = displayText[i].strip()
    if lineText in SIG_START_KEYS: isEncryptionBlock = True
    elif lineText in SIG_END_KEYS: isEncryptionBlock = False
  
  drawLine, pageHeight = 1, popup.maxY - 2
  for i in range(scroll, scroll + pageHeight):
    lineText = displayText[i].strip()
    xOffset = 2
    
    if showLineNumber:
      lineNumLabel = ("%%%ii" % lineNumWidth) % (i + 1)
      lineNumFormat = curses.A_BOLD | uiTools.getColor(LINE_NUM_COLOR)
      
      popup.addstr(drawLine, xOffset, lineNumLabel, lineNumFormat)
      xOffset += lineNumWidth + 1
    
    # Most consensus and descriptor lines are keyword/value pairs. Both are
    # shown with the same color, but the keyword is bolded.
    
    keyword, value = lineText, ""
    drawFormat = uiTools.getColor(displayColor)
    
    if lineText.startswith(HEADER_PREFIX[0]) or lineText.startswith(HEADER_PREFIX[1]):
      keyword, value = lineText, ""
      drawFormat = uiTools.getColor(HEADER_COLOR)
    elif lineText == UNRESOLVED_MSG or lineText == ERROR_MSG:
      keyword, value = lineText, ""
    elif lineText in SIG_START_KEYS:
      keyword, value = lineText, ""
      isEncryptionBlock = True
      drawFormat = uiTools.getColor(SIG_COLOR)
    elif lineText in SIG_END_KEYS:
      keyword, value = lineText, ""
      isEncryptionBlock = False
      drawFormat = uiTools.getColor(SIG_COLOR)
    elif isEncryptionBlock:
      keyword, value = "", lineText
      drawFormat = uiTools.getColor(SIG_COLOR)
    elif " " in lineText:
      divIndex = lineText.find(" ")
      keyword, value = lineText[:divIndex], lineText[divIndex:]
    
    displayQueue = [(keyword, drawFormat | curses.A_BOLD), (value, drawFormat)]
    cursorLoc = xOffset
    
    while displayQueue:
      msg, format = displayQueue.pop(0)
      if not msg: continue
      
      maxMsgSize = popup.maxX - 1 - cursorLoc
      if len(msg) >= maxMsgSize:
        # needs to split up the line
        msg, remainder = uiTools.cropStr(msg, maxMsgSize, None, endType = None, getRemainder = True)
        
        if xOffset == cursorLoc and msg == "":
          # first word is longer than the line
          msg = uiTools.cropStr(remainder, maxMsgSize)
          
          if " " in remainder:
            remainder = remainder.split(" ", 1)[1]
          else: remainder = ""
        
        popup.addstr(drawLine, cursorLoc, msg, format)
        cursorLoc = xOffset
        
        if remainder:
          displayQueue.insert(0, (remainder.strip(), format))
          drawLine += 1
      else:
        popup.addstr(drawLine, cursorLoc, msg, format)
        cursorLoc += len(msg)
      
      if drawLine > pageHeight: break
    
    drawLine += 1
    if drawLine > pageHeight: break
  
  popup.win.refresh()
示例#13
0
    def draw(self, subwindow, width, height):
        self.valsLock.acquire()
        isWide = width + 1 >= MIN_DUAL_COL_WIDTH

        # space available for content
        if isWide:
            leftWidth = max(width / 2, 77)
            rightWidth = width - leftWidth
        else:
            leftWidth = rightWidth = width

        # Line 1 / Line 1 Left (system and tor version information)
        sysNameLabel = "arm - %s" % self.vals["sys/hostname"]
        contentSpace = min(leftWidth, 40)

        if len(sysNameLabel) + 10 <= contentSpace:
            sysTypeLabel = "%s %s" % (self.vals["sys/os"],
                                      self.vals["sys/version"])
            sysTypeLabel = uiTools.cropStr(
                sysTypeLabel, contentSpace - len(sysNameLabel) - 3, 4)
            self.addstr(0, 0, "%s (%s)" % (sysNameLabel, sysTypeLabel))
        else:
            self.addstr(0, 0, uiTools.cropStr(sysNameLabel, contentSpace))

        contentSpace = leftWidth - 43
        if 7 + len(self.vals["tor/version"]) + len(
                self.vals["tor/versionStatus"]) <= contentSpace:
            versionColor = VERSION_STATUS_COLORS[self.vals["tor/versionStatus"]] if \
                self.vals["tor/versionStatus"] in VERSION_STATUS_COLORS else "white"
            versionStatusMsg = "<%s>%s</%s>" % (
                versionColor, self.vals["tor/versionStatus"], versionColor)
            self.addfstr(
                0, 43,
                "Tor %s (%s)" % (self.vals["tor/version"], versionStatusMsg))
        elif 11 <= contentSpace:
            self.addstr(
                0, 43,
                uiTools.cropStr("Tor %s" % self.vals["tor/version"],
                                contentSpace, 4))

        # Line 2 / Line 2 Left (tor ip/port information)
        if self.vals["tor/orPort"]:
            # acting as a relay (we can assume certain parameters are set
            entry = ""
            dirPortLabel = ", Dir Port: %s" % self.vals[
                "tor/dirPort"] if self.vals["tor/dirPort"] != "0" else ""
            for label in (self.vals["tor/nickname"],
                          " - " + self.vals["tor/address"],
                          ":" + self.vals["tor/orPort"], dirPortLabel):
                if len(entry) + len(label) <= leftWidth: entry += label
                else: break
        else:
            # non-relay (client only)
            # TODO: not sure what sort of stats to provide...
            entry = "<red><b>Relaying Disabled</b></red>"

        if self.vals["tor/isAuthPassword"]: authType = "password"
        elif self.vals["tor/isAuthCookie"]: authType = "cookie"
        else: authType = "open"

        if len(entry) + 19 + len(
                self.vals["tor/controlPort"]) + len(authType) <= leftWidth:
            authColor = "red" if authType == "open" else "green"
            authLabel = "<%s>%s</%s>" % (authColor, authType, authColor)
            self.addfstr(
                1, 0, "%s, Control Port (%s): %s" %
                (entry, authLabel, self.vals["tor/controlPort"]))
        elif len(entry) + 16 + len(self.vals["tor/controlPort"]) <= leftWidth:
            self.addstr(
                1, 0,
                "%s, Control Port: %s" % (entry, self.vals["tor/controlPort"]))
        else:
            self.addstr(1, 0, entry)

        # Line 3 / Line 1 Right (system usage info)
        y, x = (0, leftWidth) if isWide else (2, 0)
        if self.vals["stat/rss"] != "0":
            memoryLabel = uiTools.getSizeLabel(
                int(self.vals["stat/rss"]) * 1024)
        else:
            memoryLabel = "0"

        sysFields = ((0, "cpu: %s%% tor, %s%% arm" %
                      (self.vals["stat/%torCpu"], self.vals["stat/%armCpu"])),
                     (27, "mem: %s (%s%%)" %
                      (memoryLabel, self.vals["stat/%mem"])),
                     (47, "pid: %s" %
                      (self.vals["stat/pid"] if self._isTorConnected else "")),
                     (59, "uptime: %s" % self.vals["stat/etime"]))

        for (start, label) in sysFields:
            if start + len(label) <= rightWidth:
                self.addstr(y, x + start, label)
            else:
                break

        if self.vals["tor/orPort"]:
            # Line 4 / Line 2 Right (fingerprint)
            y, x = (1, leftWidth) if isWide else (3, 0)
            fingerprintLabel = uiTools.cropStr(
                "fingerprint: %s" % self.vals["tor/fingerprint"], width)
            self.addstr(y, x, fingerprintLabel)

            # Line 5 / Line 3 Left (flags)
            if self._isTorConnected:
                flagLine = "flags: "
                for flag in self.vals["tor/flags"]:
                    flagColor = FLAG_COLORS[flag] if flag in FLAG_COLORS.keys(
                    ) else "white"
                    flagLine += "<b><%s>%s</%s></b>, " % (flagColor, flag,
                                                          flagColor)

                if len(self.vals["tor/flags"]) > 0: flagLine = flagLine[:-2]
                else: flagLine += "<b><cyan>none</cyan></b>"

                self.addfstr(2 if isWide else 4, 0, flagLine)
            else:
                statusTime = torTools.getConn().getStatus()[1]
                statusTimeLabel = time.strftime("%H:%M %m/%d/%Y",
                                                time.localtime(statusTime))
                self.addfstr(
                    2 if isWide else 4, 0,
                    "<b><red>Tor Disconnected</red></b> (%s)" %
                    statusTimeLabel)

            # Undisplayed / Line 3 Right (exit policy)
            if isWide:
                exitPolicy = self.vals["tor/exitPolicy"]

                # adds note when default exit policy is appended
                if exitPolicy == "": exitPolicy = "<default>"
                elif not exitPolicy.endswith((" *:*", " *")):
                    exitPolicy += ", <default>"

                # color codes accepts to be green, rejects to be red, and default marker to be cyan
                isSimple = len(exitPolicy) > rightWidth - 13
                policies = exitPolicy.split(", ")
                for i in range(len(policies)):
                    policy = policies[i].strip()
                    displayedPolicy = policy.replace("accept", "").replace(
                        "reject", "").strip() if isSimple else policy
                    if policy.startswith("accept"):
                        policy = "<green><b>%s</b></green>" % displayedPolicy
                    elif policy.startswith("reject"):
                        policy = "<red><b>%s</b></red>" % displayedPolicy
                    elif policy.startswith("<default>"):
                        policy = "<cyan><b>%s</b></cyan>" % displayedPolicy
                    policies[i] = policy

                self.addfstr(2, leftWidth,
                             "exit policy: %s" % ", ".join(policies))
        else:
            # Client only
            # TODO: not sure what information to provide here...
            pass

        self._isLastDrawWide = isWide
        self._isChanged = False
        self.valsLock.release()
示例#14
0
文件: connEntry.py 项目: refnode/arm
 def _getListingContent(self, width, listingType):
   """
   Provides the source, destination, and extra info for our listing.
   
   Arguments:
     width       - maximum length of the line
     listingType - primary attribute we're listing connections by
   """
   
   conn = torTools.getConn()
   myType = self.getType()
   dstAddress = self.getDestinationLabel(26, includeLocale = True)
   
   # The required widths are the sum of the following:
   # - room for LABEL_FORMAT and LABEL_MIN_PADDING (11 characters)
   # - base data for the listing
   # - that extra field plus any previous
   
   usedSpace = len(LABEL_FORMAT % tuple([""] * 4)) + LABEL_MIN_PADDING
   localPort = ":%s" % self.local.getPort() if self.includePort else ""
   
   src, dst, etc = "", "", ""
   if listingType == entries.ListingType.IP_ADDRESS:
     myExternalIpAddr = conn.getInfo("address", self.local.getIpAddr())
     addrDiffer = myExternalIpAddr != self.local.getIpAddr()
     
     # Expanding doesn't make sense, if the connection isn't actually
     # going through Tor's external IP address. As there isn't a known
     # method for checking if it is, we're checking the type instead.
     #
     # This isn't entirely correct. It might be a better idea to check if
     # the source and destination addresses are both private, but that might
     # not be perfectly reliable either.
     
     isExpansionType = not myType in (Category.SOCKS, Category.HIDDEN, Category.CONTROL)
     
     if isExpansionType: srcAddress = myExternalIpAddr + localPort
     else: srcAddress = self.local.getIpAddr() + localPort
     
     if myType in (Category.SOCKS, Category.CONTROL):
       # Like inbound connections these need their source and destination to
       # be swapped. However, this only applies when listing by IP or hostname
       # (their fingerprint and nickname are both for us). Reversing the
       # fields here to keep the same column alignments.
       
       src = "%-21s" % dstAddress
       dst = "%-26s" % srcAddress
     else:
       src = "%-21s" % srcAddress # ip:port = max of 21 characters
       dst = "%-26s" % dstAddress # ip:port (xx) = max of 26 characters
     
     usedSpace += len(src) + len(dst) # base data requires 47 characters
     
     # Showing the fingerprint (which has the width of 42) has priority over
     # an expanded address field. Hence check if we either have space for
     # both or wouldn't be showing the fingerprint regardless.
     
     isExpandedAddrVisible = width > usedSpace + 28
     if isExpandedAddrVisible and CONFIG["features.connection.showColumn.fingerprint"]:
       isExpandedAddrVisible = width < usedSpace + 42 or width > usedSpace + 70
     
     if addrDiffer and isExpansionType and isExpandedAddrVisible and self.includeExpandedIpAddr and CONFIG["features.connection.showColumn.expandedIp"]:
       # include the internal address in the src (extra 28 characters)
       internalAddress = self.local.getIpAddr() + localPort
       
       # If this is an inbound connection then reverse ordering so it's:
       # <foreign> --> <external> --> <internal>
       # when the src and dst are swapped later
       
       if myType == Category.INBOUND: src = "%-21s  -->  %s" % (src, internalAddress)
       else: src = "%-21s  -->  %s" % (internalAddress, src)
       
       usedSpace += 28
     
     etc = self.getEtcContent(width - usedSpace, listingType)
     usedSpace += len(etc)
   elif listingType == entries.ListingType.HOSTNAME:
     # 15 characters for source, and a min of 40 reserved for the destination
     # TODO: when actually functional the src and dst need to be swapped for
     # SOCKS and CONTROL connections
     src = "localhost%-6s" % localPort
     usedSpace += len(src)
     minHostnameSpace = 40
     
     etc = self.getEtcContent(width - usedSpace - minHostnameSpace, listingType)
     usedSpace += len(etc)
     
     hostnameSpace = width - usedSpace
     usedSpace = width # prevents padding at the end
     if self.isPrivate():
       dst = ("%%-%is" % hostnameSpace) % "<scrubbed>"
     else:
       hostname = self.foreign.getHostname(self.foreign.getIpAddr())
       portLabel = ":%-5s" % self.foreign.getPort() if self.includePort else ""
       
       # truncates long hostnames and sets dst to <hostname>:<port>
       hostname = uiTools.cropStr(hostname, hostnameSpace, 0)
       dst = ("%%-%is" % hostnameSpace) % (hostname + portLabel)
   elif listingType == entries.ListingType.FINGERPRINT:
     src = "localhost"
     if myType == Category.CONTROL: dst = "localhost"
     else: dst = self.foreign.getFingerprint()
     dst = "%-40s" % dst
     
     usedSpace += len(src) + len(dst) # base data requires 49 characters
     
     etc = self.getEtcContent(width - usedSpace, listingType)
     usedSpace += len(etc)
   else:
     # base data requires 50 min characters
     src = self.local.getNickname()
     if myType == Category.CONTROL: dst = self.local.getNickname()
     else: dst = self.foreign.getNickname()
     minBaseSpace = 50
     
     etc = self.getEtcContent(width - usedSpace - minBaseSpace, listingType)
     usedSpace += len(etc)
     
     baseSpace = width - usedSpace
     usedSpace = width # prevents padding at the end
     
     if len(src) + len(dst) > baseSpace:
       src = uiTools.cropStr(src, baseSpace / 3)
       dst = uiTools.cropStr(dst, baseSpace - len(src))
     
     # pads dst entry to its max space
     dst = ("%%-%is" % (baseSpace - len(src))) % dst
   
   if myType == Category.INBOUND: src, dst = dst, src
   padding = " " * (width - usedSpace + LABEL_MIN_PADDING)
   return LABEL_FORMAT % (src, dst, etc, padding)
示例#15
0
文件: connEntry.py 项目: refnode/arm
 def getDestinationLabel(self, maxLength, includeLocale=False, includeHostname=False):
   """
   Provides a short description of the destination. This is made up of two
   components, the base <ip addr>:<port> and an extra piece of information in
   parentheses. The IP address is scrubbed from private connections.
   
   Extra information is...
   - the port's purpose for exit connections
   - the locale and/or hostname if set to do so, the address isn't private,
     and isn't on the local network
   - nothing otherwise
   
   Arguments:
     maxLength       - maximum length of the string returned
     includeLocale   - possibly includes the locale
     includeHostname - possibly includes the hostname
   """
   
   # the port and port derived data can be hidden by config or without includePort
   includePort = self.includePort and (CONFIG["features.connection.showExitPort"] or self.getType() != Category.EXIT)
   
   # destination of the connection
   ipLabel = "<scrubbed>" if self.isPrivate() else self.foreign.getIpAddr()
   portLabel = ":%s" % self.foreign.getPort() if includePort else ""
   dstAddress = ipLabel + portLabel
   
   # Only append the extra info if there's at least a couple characters of
   # space (this is what's needed for the country codes).
   if len(dstAddress) + 5 <= maxLength:
     spaceAvailable = maxLength - len(dstAddress) - 3
     
     if self.getType() == Category.EXIT and includePort:
       purpose = connections.getPortUsage(self.foreign.getPort())
       
       if purpose:
         # BitTorrent is a common protocol to truncate, so just use "Torrent"
         # if there's not enough room.
         if len(purpose) > spaceAvailable and purpose == "BitTorrent":
           purpose = "Torrent"
         
         # crops with a hyphen if too long
         purpose = uiTools.cropStr(purpose, spaceAvailable, endType = uiTools.Ending.HYPHEN)
         
         dstAddress += " (%s)" % purpose
     elif not connections.isIpAddressPrivate(self.foreign.getIpAddr()):
       extraInfo = []
       conn = torTools.getConn()
       
       if includeLocale and not conn.isGeoipUnavailable():
         foreignLocale = self.foreign.getLocale("??")
         extraInfo.append(foreignLocale)
         spaceAvailable -= len(foreignLocale) + 2
       
       if includeHostname:
         dstHostname = self.foreign.getHostname()
         
         if dstHostname:
           # determines the full space available, taking into account the ", "
           # dividers if there's multiple pieces of extra data
           
           maxHostnameSpace = spaceAvailable - 2 * len(extraInfo)
           dstHostname = uiTools.cropStr(dstHostname, maxHostnameSpace)
           extraInfo.append(dstHostname)
           spaceAvailable -= len(dstHostname)
       
       if extraInfo:
         dstAddress += " (%s)" % ", ".join(extraInfo)
   
   return dstAddress[:maxLength]
示例#16
0
    def getDestinationLabel(self,
                            maxLength,
                            includeLocale=False,
                            includeHostname=False):
        """
    Provides a short description of the destination. This is made up of two
    components, the base <ip addr>:<port> and an extra piece of information in
    parentheses. The IP address is scrubbed from private connections.
    
    Extra information is...
    - the port's purpose for exit connections
    - the locale and/or hostname if set to do so, the address isn't private,
      and isn't on the local network
    - nothing otherwise
    
    Arguments:
      maxLength       - maximum length of the string returned
      includeLocale   - possibly includes the locale
      includeHostname - possibly includes the hostname
    """

        # the port and port derived data can be hidden by config or without includePort
        includePort = self.includePort and (
            CONFIG["features.connection.showExitPort"]
            or self.getType() != Category.EXIT)

        # destination of the connection
        ipLabel = "<scrubbed>" if self.isPrivate() else self.foreign.getIpAddr(
        )
        portLabel = ":%s" % self.foreign.getPort() if includePort else ""
        dstAddress = ipLabel + portLabel

        # Only append the extra info if there's at least a couple characters of
        # space (this is what's needed for the country codes).
        if len(dstAddress) + 5 <= maxLength:
            spaceAvailable = maxLength - len(dstAddress) - 3

            if self.getType() == Category.EXIT and includePort:
                purpose = connections.getPortUsage(self.foreign.getPort())

                if purpose:
                    # BitTorrent is a common protocol to truncate, so just use "Torrent"
                    # if there's not enough room.
                    if len(purpose
                           ) > spaceAvailable and purpose == "BitTorrent":
                        purpose = "Torrent"

                    # crops with a hyphen if too long
                    purpose = uiTools.cropStr(purpose,
                                              spaceAvailable,
                                              endType=uiTools.Ending.HYPHEN)

                    dstAddress += " (%s)" % purpose
            elif not connections.isIpAddressPrivate(self.foreign.getIpAddr()):
                extraInfo = []
                conn = torTools.getConn()

                if includeLocale and not conn.isGeoipUnavailable():
                    foreignLocale = self.foreign.getLocale("??")
                    extraInfo.append(foreignLocale)
                    spaceAvailable -= len(foreignLocale) + 2

                if includeHostname:
                    dstHostname = self.foreign.getHostname()

                    if dstHostname:
                        # determines the full space available, taking into account the ", "
                        # dividers if there's multiple pieces of extra data

                        maxHostnameSpace = spaceAvailable - 2 * len(extraInfo)
                        dstHostname = uiTools.cropStr(dstHostname,
                                                      maxHostnameSpace)
                        extraInfo.append(dstHostname)
                        spaceAvailable -= len(dstHostname)

                if extraInfo:
                    dstAddress += " (%s)" % ", ".join(extraInfo)

        return dstAddress[:maxLength]
示例#17
0
    def _drawSelectionPanel(self, cursorSelection, width, detailPanelHeight,
                            titleLabel):
        """
    Renders a panel for the selected configuration option.
    """

        # border (top)
        if width >= len(titleLabel):
            self.win.hline(0, len(titleLabel), curses.ACS_HLINE,
                           width - len(titleLabel))
            self.win.addch(0, width, curses.ACS_URCORNER)

        # border (sides)
        self.win.vline(1, 0, curses.ACS_VLINE, detailPanelHeight - 1)
        self.win.vline(1, width, curses.ACS_VLINE, detailPanelHeight - 1)

        # border (bottom)
        self.win.addch(detailPanelHeight, 0, curses.ACS_LLCORNER)
        if width >= 2: self.win.addch(detailPanelHeight, 1, curses.ACS_TTEE)
        if width >= 3:
            self.win.hline(detailPanelHeight, 2, curses.ACS_HLINE, width - 2)
        self.win.addch(detailPanelHeight, width, curses.ACS_LRCORNER)

        selectionFormat = curses.A_BOLD | uiTools.getColor(
            CATEGORY_COLOR[cursorSelection.get(FIELD_CATEGORY)])

        # first entry:
        # <option> (<category> Option)
        optionLabel = " (%s Option)" % torConfig.OPTION_CATEGORY_STR[
            cursorSelection.get(FIELD_CATEGORY)]
        self.addstr(1, 2,
                    cursorSelection.get(FIELD_OPTION) + optionLabel,
                    selectionFormat)

        # second entry:
        # Value: <value> ([default|custom], <type>, usage: <argument usage>)
        if detailPanelHeight >= 3:
            valueAttr = []
            valueAttr.append("default" if cursorSelection.get(FIELD_IS_DEFAULT
                                                              ) else "custom")
            valueAttr.append(cursorSelection.get(FIELD_TYPE))
            valueAttr.append("usage: %s" %
                             (cursorSelection.get(FIELD_ARG_USAGE)))
            valueAttrLabel = ", ".join(valueAttr)

            valueLabelWidth = width - 12 - len(valueAttrLabel)
            valueLabel = uiTools.cropStr(cursorSelection.get(FIELD_VALUE),
                                         valueLabelWidth)

            self.addstr(2, 2, "Value: %s (%s)" % (valueLabel, valueAttrLabel),
                        selectionFormat)

        # remainder is filled with the man page description
        descriptionHeight = max(0, detailPanelHeight - 3)
        descriptionContent = "Description: " + cursorSelection.get(
            FIELD_DESCRIPTION)

        for i in range(descriptionHeight):
            # checks if we're done writing the description
            if not descriptionContent: break

            # there's a leading indent after the first line
            if i > 0: descriptionContent = "  " + descriptionContent

            # we only want to work with content up until the next newline
            if "\n" in descriptionContent:
                lineContent, descriptionContent = descriptionContent.split(
                    "\n", 1)
            else:
                lineContent, descriptionContent = descriptionContent, ""

            if i != descriptionHeight - 1:
                # there's more lines to display
                msg, remainder = uiTools.cropStr(lineContent, width - 2, 4, 4,
                                                 uiTools.END_WITH_HYPHEN, True)
                descriptionContent = remainder.strip() + descriptionContent
            else:
                # this is the last line, end it with an ellipse
                msg = uiTools.cropStr(lineContent, width - 2, 4, 4)

            self.addstr(3 + i, 2, msg, selectionFormat)
示例#18
0
    def _getListingContent(self, width, listingType):
        """
    Provides the source, destination, and extra info for our listing.
    
    Arguments:
      width       - maximum length of the line
      listingType - primary attribute we're listing connections by
    """

        conn = torTools.getConn()
        myType = self.getType()
        dstAddress = self.getDestinationLabel(26, includeLocale=True)

        # The required widths are the sum of the following:
        # - room for LABEL_FORMAT and LABEL_MIN_PADDING (11 characters)
        # - base data for the listing
        # - that extra field plus any previous

        usedSpace = len(LABEL_FORMAT % tuple([""] * 4)) + LABEL_MIN_PADDING
        localPort = ":%s" % self.local.getPort() if self.includePort else ""

        src, dst, etc = "", "", ""
        if listingType == entries.ListingType.IP_ADDRESS:
            myExternalIpAddr = conn.getInfo("address", self.local.getIpAddr())
            addrDiffer = myExternalIpAddr != self.local.getIpAddr()

            # Expanding doesn't make sense, if the connection isn't actually
            # going through Tor's external IP address. As there isn't a known
            # method for checking if it is, we're checking the type instead.
            #
            # This isn't entirely correct. It might be a better idea to check if
            # the source and destination addresses are both private, but that might
            # not be perfectly reliable either.

            isExpansionType = not myType in (Category.SOCKS, Category.HIDDEN,
                                             Category.CONTROL)

            if isExpansionType: srcAddress = myExternalIpAddr + localPort
            else: srcAddress = self.local.getIpAddr() + localPort

            if myType in (Category.SOCKS, Category.CONTROL):
                # Like inbound connections these need their source and destination to
                # be swapped. However, this only applies when listing by IP or hostname
                # (their fingerprint and nickname are both for us). Reversing the
                # fields here to keep the same column alignments.

                src = "%-21s" % dstAddress
                dst = "%-26s" % srcAddress
            else:
                src = "%-21s" % srcAddress  # ip:port = max of 21 characters
                dst = "%-26s" % dstAddress  # ip:port (xx) = max of 26 characters

            usedSpace += len(src) + len(
                dst)  # base data requires 47 characters

            # Showing the fingerprint (which has the width of 42) has priority over
            # an expanded address field. Hence check if we either have space for
            # both or wouldn't be showing the fingerprint regardless.

            isExpandedAddrVisible = width > usedSpace + 28
            if isExpandedAddrVisible and CONFIG[
                    "features.connection.showColumn.fingerprint"]:
                isExpandedAddrVisible = width < usedSpace + 42 or width > usedSpace + 70

            if addrDiffer and isExpansionType and isExpandedAddrVisible and self.includeExpandedIpAddr and CONFIG[
                    "features.connection.showColumn.expandedIp"]:
                # include the internal address in the src (extra 28 characters)
                internalAddress = self.local.getIpAddr() + localPort

                # If this is an inbound connection then reverse ordering so it's:
                # <foreign> --> <external> --> <internal>
                # when the src and dst are swapped later

                if myType == Category.INBOUND:
                    src = "%-21s  -->  %s" % (src, internalAddress)
                else:
                    src = "%-21s  -->  %s" % (internalAddress, src)

                usedSpace += 28

            etc = self.getEtcContent(width - usedSpace, listingType)
            usedSpace += len(etc)
        elif listingType == entries.ListingType.HOSTNAME:
            # 15 characters for source, and a min of 40 reserved for the destination
            # TODO: when actually functional the src and dst need to be swapped for
            # SOCKS and CONTROL connections
            src = "localhost%-6s" % localPort
            usedSpace += len(src)
            minHostnameSpace = 40

            etc = self.getEtcContent(width - usedSpace - minHostnameSpace,
                                     listingType)
            usedSpace += len(etc)

            hostnameSpace = width - usedSpace
            usedSpace = width  # prevents padding at the end
            if self.isPrivate():
                dst = ("%%-%is" % hostnameSpace) % "<scrubbed>"
            else:
                hostname = self.foreign.getHostname(self.foreign.getIpAddr())
                portLabel = ":%-5s" % self.foreign.getPort(
                ) if self.includePort else ""

                # truncates long hostnames and sets dst to <hostname>:<port>
                hostname = uiTools.cropStr(hostname, hostnameSpace, 0)
                dst = ("%%-%is" % hostnameSpace) % (hostname + portLabel)
        elif listingType == entries.ListingType.FINGERPRINT:
            src = "localhost"
            if myType == Category.CONTROL: dst = "localhost"
            else: dst = self.foreign.getFingerprint()
            dst = "%-40s" % dst

            usedSpace += len(src) + len(
                dst)  # base data requires 49 characters

            etc = self.getEtcContent(width - usedSpace, listingType)
            usedSpace += len(etc)
        else:
            # base data requires 50 min characters
            src = self.local.getNickname()
            if myType == Category.CONTROL: dst = self.local.getNickname()
            else: dst = self.foreign.getNickname()
            minBaseSpace = 50

            etc = self.getEtcContent(width - usedSpace - minBaseSpace,
                                     listingType)
            usedSpace += len(etc)

            baseSpace = width - usedSpace
            usedSpace = width  # prevents padding at the end

            if len(src) + len(dst) > baseSpace:
                src = uiTools.cropStr(src, baseSpace / 3)
                dst = uiTools.cropStr(dst, baseSpace - len(src))

            # pads dst entry to its max space
            dst = ("%%-%is" % (baseSpace - len(src))) % dst

        if myType == Category.INBOUND: src, dst = dst, src
        padding = " " * (width - usedSpace + LABEL_MIN_PADDING)
        return LABEL_FORMAT % (src, dst, etc, padding)
示例#19
0
    def _getDetailContent(self, width):
        """
    Provides a list with detailed information for this connection.
    
    Arguments:
      width - max length of lines
    """

        lines = [""] * 7
        lines[0] = "address: %s" % self.getDestinationLabel(width - 11)
        lines[1] = "locale: %s" % ("??" if self.isPrivate() else
                                   self.foreign.getLocale("??"))

        # Remaining data concerns the consensus results, with three possible cases:
        # - if there's a single match then display its details
        # - if there's multiple potential relays then list all of the combinations
        #   of ORPorts / Fingerprints
        # - if no consensus data is available then say so (probably a client or
        #   exit connection)

        fingerprint = self.foreign.getFingerprint()
        conn = torTools.getConn()

        if fingerprint != "UNKNOWN":
            # single match - display information available about it
            nsEntry = conn.getConsensusEntry(fingerprint)
            descEntry = conn.getDescriptorEntry(fingerprint)

            # append the fingerprint to the second line
            lines[1] = "%-13sfingerprint: %s" % (lines[1], fingerprint)

            if nsEntry:
                # example consensus entry:
                # r murble R8sCM1ar1sS2GulQYFVmvN95xsk RJr6q+wkTFG+ng5v2bdCbVVFfA4 2011-02-21 00:25:32 195.43.157.85 443 0
                # s Exit Fast Guard Named Running Stable Valid
                # w Bandwidth=2540
                # p accept 20-23,43,53,79-81,88,110,143,194,443

                nsLines = nsEntry.split("\n")

                firstLineComp = nsLines[0].split(" ")
                if len(firstLineComp) >= 9:
                    _, nickname, _, _, pubDate, pubTime, _, orPort, dirPort = firstLineComp[:
                                                                                            9]
                else:
                    nickname, pubDate, pubTime, orPort, dirPort = "", "", "", "", ""

                flags = "unknown"
                if len(nsLines) >= 2 and nsLines[1].startswith("s "):
                    flags = nsLines[1][2:]

                exitPolicy = conn.getRelayExitPolicy(fingerprint)

                if exitPolicy: policyLabel = exitPolicy.getSummary()
                else: policyLabel = "unknown"

                dirPortLabel = "" if dirPort == "0" else "dirport: %s" % dirPort
                lines[2] = "nickname: %-25s orport: %-10s %s" % (
                    nickname, orPort, dirPortLabel)
                lines[3] = "published: %s %s" % (pubTime, pubDate)
                lines[4] = "flags: %s" % flags.replace(" ", ", ")
                lines[5] = "exit policy: %s" % policyLabel

            if descEntry:
                torVersion, platform, contact = "", "", ""

                for descLine in descEntry.split("\n"):
                    if descLine.startswith("platform"):
                        # has the tor version and platform, ex:
                        # platform Tor 0.2.1.29 (r318f470bc5f2ad43) on Linux x86_64

                        torVersion = descLine[13:descLine.find(" ", 13)]
                        platform = descLine[descLine.rfind(" on ") + 4:]
                    elif descLine.startswith("contact"):
                        contact = descLine[8:]

                        # clears up some highly common obscuring
                        for alias in (" at ", " AT "):
                            contact = contact.replace(alias, "@")
                        for alias in (" dot ", " DOT "):
                            contact = contact.replace(alias, ".")

                        break  # contact lines come after the platform

                lines[3] = "%-35s os: %-14s version: %s" % (lines[3], platform,
                                                            torVersion)

                # contact information is an optional field
                if contact: lines[6] = "contact: %s" % contact
        else:
            allMatches = conn.getRelayFingerprint(self.foreign.getIpAddr(),
                                                  getAllMatches=True)

            if allMatches:
                # multiple matches
                lines[2] = "Multiple matches, possible fingerprints are:"

                for i in range(len(allMatches)):
                    isLastLine = i == 3

                    relayPort, relayFingerprint = allMatches[i]
                    lineText = "%i. or port: %-5s fingerprint: %s" % (
                        i, relayPort, relayFingerprint)

                    # if there's multiple lines remaining at the end then give a count
                    remainingRelays = len(allMatches) - i
                    if isLastLine and remainingRelays > 1:
                        lineText = "... %i more" % remainingRelays

                    lines[3 + i] = lineText

                    if isLastLine: break
            else:
                # no consensus entry for this ip address
                lines[2] = "No consensus data found"

        # crops any lines that are too long
        for i in range(len(lines)):
            lines[i] = uiTools.cropStr(lines[i], width - 2)

        return lines
示例#20
0
    def getEtcContent(self, width, listingType):
        """
    Provides the optional content for the connection.
    
    Arguments:
      width       - maximum length of the line
      listingType - primary attribute we're listing connections by
    """

        # for applications show the command/pid
        if self.getType() in (Category.SOCKS, Category.HIDDEN,
                              Category.CONTROL):
            displayLabel = ""

            if self.appName:
                if self.appPid:
                    displayLabel = "%s (%s)" % (self.appName, self.appPid)
                else:
                    displayLabel = self.appName
            elif self.isAppResolving:
                displayLabel = "resolving..."
            else:
                displayLabel = "UNKNOWN"

            if len(displayLabel) < width:
                return ("%%-%is" % width) % displayLabel
            else:
                return ""

        # for everything else display connection/consensus information
        dstAddress = self.getDestinationLabel(26, includeLocale=True)
        etc, usedSpace = "", 0
        if listingType == entries.ListingType.IP_ADDRESS:
            if width > usedSpace + 42 and CONFIG[
                    "features.connection.showColumn.fingerprint"]:
                # show fingerprint (column width: 42 characters)
                etc += "%-40s  " % self.foreign.getFingerprint()
                usedSpace += 42

            if width > usedSpace + 10 and CONFIG[
                    "features.connection.showColumn.nickname"]:
                # show nickname (column width: remainder)
                nicknameSpace = width - usedSpace
                nicknameLabel = uiTools.cropStr(self.foreign.getNickname(),
                                                nicknameSpace, 0)
                etc += ("%%-%is  " % nicknameSpace) % nicknameLabel
                usedSpace += nicknameSpace + 2
        elif listingType == entries.ListingType.HOSTNAME:
            if width > usedSpace + 28 and CONFIG[
                    "features.connection.showColumn.destination"]:
                # show destination ip/port/locale (column width: 28 characters)
                etc += "%-26s  " % dstAddress
                usedSpace += 28

            if width > usedSpace + 42 and CONFIG[
                    "features.connection.showColumn.fingerprint"]:
                # show fingerprint (column width: 42 characters)
                etc += "%-40s  " % self.foreign.getFingerprint()
                usedSpace += 42

            if width > usedSpace + 17 and CONFIG[
                    "features.connection.showColumn.nickname"]:
                # show nickname (column width: min 17 characters, uses half of the remainder)
                nicknameSpace = 15 + (width - (usedSpace + 17)) / 2
                nicknameLabel = uiTools.cropStr(self.foreign.getNickname(),
                                                nicknameSpace, 0)
                etc += ("%%-%is  " % nicknameSpace) % nicknameLabel
                usedSpace += (nicknameSpace + 2)
        elif listingType == entries.ListingType.FINGERPRINT:
            if width > usedSpace + 17:
                # show nickname (column width: min 17 characters, consumes any remaining space)
                nicknameSpace = width - usedSpace - 2

                # if there's room then also show a column with the destination
                # ip/port/locale (column width: 28 characters)
                isIpLocaleIncluded = width > usedSpace + 45
                isIpLocaleIncluded &= CONFIG[
                    "features.connection.showColumn.destination"]
                if isIpLocaleIncluded: nicknameSpace -= 28

                if CONFIG["features.connection.showColumn.nickname"]:
                    nicknameLabel = uiTools.cropStr(self.foreign.getNickname(),
                                                    nicknameSpace, 0)
                    etc += ("%%-%is  " % nicknameSpace) % nicknameLabel
                    usedSpace += nicknameSpace + 2

                if isIpLocaleIncluded:
                    etc += "%-26s  " % dstAddress
                    usedSpace += 28
        else:
            if width > usedSpace + 42 and CONFIG[
                    "features.connection.showColumn.fingerprint"]:
                # show fingerprint (column width: 42 characters)
                etc += "%-40s  " % self.foreign.getFingerprint()
                usedSpace += 42

            if width > usedSpace + 28 and CONFIG[
                    "features.connection.showColumn.destination"]:
                # show destination ip/port/locale (column width: 28 characters)
                etc += "%-26s  " % dstAddress
                usedSpace += 28

        return ("%%-%is" % width) % etc
示例#21
0
def draw(popup, fingerprint, displayText, displayColor, scroll,
         showLineNumber):
    popup.win.erase()
    popup.win.box()
    xOffset = 2

    if fingerprint: title = "Consensus Descriptor (%s):" % fingerprint
    else: title = "Consensus Descriptor:"
    popup.addstr(0, 0, title, curses.A_STANDOUT)

    lineNumWidth = int(math.log10(len(displayText))) + 1
    isEncryptionBlock = False  # flag indicating if we're currently displaying a key

    # checks if first line is in an encryption block
    for i in range(0, scroll):
        lineText = displayText[i].strip()
        if lineText in SIG_START_KEYS: isEncryptionBlock = True
        elif lineText in SIG_END_KEYS: isEncryptionBlock = False

    drawLine, pageHeight = 1, popup.maxY - 2
    for i in range(scroll, scroll + pageHeight):
        lineText = displayText[i].strip()
        xOffset = 2

        if showLineNumber:
            lineNumLabel = ("%%%ii" % lineNumWidth) % (i + 1)
            lineNumFormat = curses.A_BOLD | uiTools.getColor(LINE_NUM_COLOR)

            popup.addstr(drawLine, xOffset, lineNumLabel, lineNumFormat)
            xOffset += lineNumWidth + 1

        # Most consensus and descriptor lines are keyword/value pairs. Both are
        # shown with the same color, but the keyword is bolded.

        keyword, value = lineText, ""
        drawFormat = uiTools.getColor(displayColor)

        if lineText.startswith(HEADER_PREFIX[0]) or lineText.startswith(
                HEADER_PREFIX[1]):
            keyword, value = lineText, ""
            drawFormat = uiTools.getColor(HEADER_COLOR)
        elif lineText == UNRESOLVED_MSG or lineText == ERROR_MSG:
            keyword, value = lineText, ""
        elif lineText in SIG_START_KEYS:
            keyword, value = lineText, ""
            isEncryptionBlock = True
            drawFormat = uiTools.getColor(SIG_COLOR)
        elif lineText in SIG_END_KEYS:
            keyword, value = lineText, ""
            isEncryptionBlock = False
            drawFormat = uiTools.getColor(SIG_COLOR)
        elif isEncryptionBlock:
            keyword, value = "", lineText
            drawFormat = uiTools.getColor(SIG_COLOR)
        elif " " in lineText:
            divIndex = lineText.find(" ")
            keyword, value = lineText[:divIndex], lineText[divIndex:]

        displayQueue = [(keyword, drawFormat | curses.A_BOLD),
                        (value, drawFormat)]
        cursorLoc = xOffset

        while displayQueue:
            msg, format = displayQueue.pop(0)
            if not msg: continue

            maxMsgSize = popup.maxX - 1 - cursorLoc
            if len(msg) >= maxMsgSize:
                # needs to split up the line
                msg, remainder = uiTools.cropStr(msg,
                                                 maxMsgSize,
                                                 None,
                                                 endType=None,
                                                 getRemainder=True)

                if xOffset == cursorLoc and msg == "":
                    # first word is longer than the line
                    msg = uiTools.cropStr(remainder, maxMsgSize)

                    if " " in remainder:
                        remainder = remainder.split(" ", 1)[1]
                    else:
                        remainder = ""

                popup.addstr(drawLine, cursorLoc, msg, format)
                cursorLoc = xOffset

                if remainder:
                    displayQueue.insert(0, (remainder.strip(), format))
                    drawLine += 1
            else:
                popup.addstr(drawLine, cursorLoc, msg, format)
                cursorLoc += len(msg)

            if drawLine > pageHeight: break

        drawLine += 1
        if drawLine > pageHeight: break

    popup.win.refresh()
示例#22
0
    def draw(self, width, height):
        self.valsLock.acquire()

        # If true, we assume that the cached value in self._lastContentHeight is
        # still accurate, and stop drawing when there's nothing more to display.
        # Otherwise the self._lastContentHeight is suspect, and we'll process all
        # the content to check if it's right (and redraw again with the corrected
        # height if not).
        trustLastContentHeight = self._lastContentHeightArgs == (width, height)

        # restricts scroll location to valid bounds
        self.scroll = max(
            0, min(self.scroll, self._lastContentHeight - height + 1))

        renderedContents, corrections, confLocation = None, {}, None
        if self.configType == Config.TORRC:
            loadedTorrc = torConfig.getTorrc()
            loadedTorrc.getLock().acquire()
            confLocation = loadedTorrc.getConfigLocation()

            if not loadedTorrc.isLoaded():
                renderedContents = ["### Unable to load the torrc ###"]
            else:
                renderedContents = loadedTorrc.getDisplayContents(
                    self.stripComments)

                # constructs a mapping of line numbers to the issue on it
                corrections = dict(
                    (lineNum, (issue, msg))
                    for lineNum, issue, msg in loadedTorrc.getCorrections())

            loadedTorrc.getLock().release()
        else:
            loadedArmrc = conf.getConfig("arm")
            confLocation = loadedArmrc.path
            renderedContents = list(loadedArmrc.rawContents)

        # offset to make room for the line numbers
        lineNumOffset = 0
        if self.showLineNum:
            if len(renderedContents) == 0: lineNumOffset = 2
            else: lineNumOffset = int(math.log10(len(renderedContents))) + 2

        # draws left-hand scroll bar if content's longer than the height
        scrollOffset = 0
        if self._config[
                "features.config.file.showScrollbars"] and self._lastContentHeight > height - 1:
            scrollOffset = 3
            self.addScrollBar(self.scroll, self.scroll + height - 1,
                              self._lastContentHeight, 1)

        displayLine = -self.scroll + 1  # line we're drawing on

        # draws the top label
        if self.isTitleVisible():
            sourceLabel = "Tor" if self.configType == Config.TORRC else "Arm"
            locationLabel = " (%s)" % confLocation if confLocation else ""
            self.addstr(
                0, 0,
                "%s Configuration File%s:" % (sourceLabel, locationLabel),
                curses.A_STANDOUT)

        isMultiline = False  # true if we're in the middle of a multiline torrc entry
        for lineNumber in range(0, len(renderedContents)):
            lineText = renderedContents[lineNumber]
            lineText = lineText.rstrip()  # remove ending whitespace

            # blank lines are hidden when stripping comments
            if self.stripComments and not lineText: continue

            # splits the line into its component (msg, format) tuples
            lineComp = {
                "option": ["", curses.A_BOLD | uiTools.getColor("green")],
                "argument": ["", curses.A_BOLD | uiTools.getColor("cyan")],
                "correction": ["", curses.A_BOLD | uiTools.getColor("cyan")],
                "comment": ["", uiTools.getColor("white")]
            }

            # parses the comment
            commentIndex = lineText.find("#")
            if commentIndex != -1:
                lineComp["comment"][0] = lineText[commentIndex:]
                lineText = lineText[:commentIndex]

            # splits the option and argument, preserving any whitespace around them
            strippedLine = lineText.strip()
            optionIndex = strippedLine.find(" ")
            if isMultiline:
                # part of a multiline entry started on a previous line so everything
                # is part of the argument
                lineComp["argument"][0] = lineText
            elif optionIndex == -1:
                # no argument provided
                lineComp["option"][0] = lineText
            else:
                optionText = strippedLine[:optionIndex]
                optionEnd = lineText.find(optionText) + len(optionText)
                lineComp["option"][0] = lineText[:optionEnd]
                lineComp["argument"][0] = lineText[optionEnd:]

            # flags following lines as belonging to this multiline entry if it ends
            # with a slash
            if strippedLine: isMultiline = strippedLine.endswith("\\")

            # gets the correction
            if lineNumber in corrections:
                lineIssue, lineIssueMsg = corrections[lineNumber]

                if lineIssue in (torConfig.ValidationError.DUPLICATE,
                                 torConfig.ValidationError.IS_DEFAULT):
                    lineComp["option"][1] = curses.A_BOLD | uiTools.getColor(
                        "blue")
                    lineComp["argument"][1] = curses.A_BOLD | uiTools.getColor(
                        "blue")
                elif lineIssue == torConfig.ValidationError.MISMATCH:
                    lineComp["argument"][1] = curses.A_BOLD | uiTools.getColor(
                        "red")
                    lineComp["correction"][0] = " (%s)" % lineIssueMsg
                else:
                    # For some types of configs the correction field is simply used to
                    # provide extra data (for instance, the type for tor state fields).
                    lineComp["correction"][0] = " (%s)" % lineIssueMsg
                    lineComp["correction"][
                        1] = curses.A_BOLD | uiTools.getColor("magenta")

            # draws the line number
            if self.showLineNum and displayLine < height and displayLine >= 1:
                lineNumStr = ("%%%ii" % (lineNumOffset - 1)) % (lineNumber + 1)
                self.addstr(displayLine, scrollOffset, lineNumStr,
                            curses.A_BOLD | uiTools.getColor("yellow"))

            # draws the rest of the components with line wrap
            cursorLoc, lineOffset = lineNumOffset + scrollOffset, 0
            maxLinesPerEntry = self._config[
                "features.config.file.maxLinesPerEntry"]
            displayQueue = [
                lineComp[entry]
                for entry in ("option", "argument", "correction", "comment")
            ]

            while displayQueue:
                msg, format = displayQueue.pop(0)

                maxMsgSize, includeBreak = width - cursorLoc, False
                if len(msg) >= maxMsgSize:
                    # message is too long - break it up
                    if lineOffset == maxLinesPerEntry - 1:
                        msg = uiTools.cropStr(msg, maxMsgSize)
                    else:
                        includeBreak = True
                        msg, remainder = uiTools.cropStr(
                            msg, maxMsgSize, 4, 4, uiTools.Ending.HYPHEN, True)
                        displayQueue.insert(0, (remainder.strip(), format))

                drawLine = displayLine + lineOffset
                if msg and drawLine < height and drawLine >= 1:
                    self.addstr(drawLine, cursorLoc, msg, format)

                # If we're done, and have added content to this line, then start
                # further content on the next line.
                cursorLoc += len(msg)
                includeBreak |= not displayQueue and cursorLoc != lineNumOffset + scrollOffset

                if includeBreak:
                    lineOffset += 1
                    cursorLoc = lineNumOffset + scrollOffset

            displayLine += max(lineOffset, 1)

            if trustLastContentHeight and displayLine >= height: break

        if not trustLastContentHeight:
            self._lastContentHeightArgs = (width, height)
            newContentHeight = displayLine + self.scroll - 1

            if self._lastContentHeight != newContentHeight:
                self._lastContentHeight = newContentHeight
                self.redraw(True)

        self.valsLock.release()
示例#23
0
 def _drawSelectionPanel(self, cursorSelection, width, detailPanelHeight, titleLabel):
   """
   Renders a panel for the selected configuration option.
   """
   
   # border (top)
   if width >= len(titleLabel):
     self.win.hline(0, len(titleLabel), curses.ACS_HLINE, width - len(titleLabel))
     self.win.addch(0, width, curses.ACS_URCORNER)
   
   # border (sides)
   self.win.vline(1, 0, curses.ACS_VLINE, detailPanelHeight - 1)
   self.win.vline(1, width, curses.ACS_VLINE, detailPanelHeight - 1)
   
   # border (bottom)
   self.win.addch(detailPanelHeight, 0, curses.ACS_LLCORNER)
   if width >= 2: self.win.addch(detailPanelHeight, 1, curses.ACS_TTEE)
   if width >= 3: self.win.hline(detailPanelHeight, 2, curses.ACS_HLINE, width - 2)
   self.win.addch(detailPanelHeight, width, curses.ACS_LRCORNER)
   
   selectionFormat = curses.A_BOLD | uiTools.getColor(CATEGORY_COLOR[cursorSelection.get(FIELD_CATEGORY)])
   
   # first entry:
   # <option> (<category> Option)
   optionLabel =" (%s Option)" % torConfig.OPTION_CATEGORY_STR[cursorSelection.get(FIELD_CATEGORY)]
   self.addstr(1, 2, cursorSelection.get(FIELD_OPTION) + optionLabel, selectionFormat)
   
   # second entry:
   # Value: <value> ([default|custom], <type>, usage: <argument usage>)
   if detailPanelHeight >= 3:
     valueAttr = []
     valueAttr.append("default" if cursorSelection.get(FIELD_IS_DEFAULT) else "custom")
     valueAttr.append(cursorSelection.get(FIELD_TYPE))
     valueAttr.append("usage: %s" % (cursorSelection.get(FIELD_ARG_USAGE)))
     valueAttrLabel = ", ".join(valueAttr)
     
     valueLabelWidth = width - 12 - len(valueAttrLabel)
     valueLabel = uiTools.cropStr(cursorSelection.get(FIELD_VALUE), valueLabelWidth)
     
     self.addstr(2, 2, "Value: %s (%s)" % (valueLabel, valueAttrLabel), selectionFormat)
   
   # remainder is filled with the man page description
   descriptionHeight = max(0, detailPanelHeight - 3)
   descriptionContent = "Description: " + cursorSelection.get(FIELD_DESCRIPTION)
   
   for i in range(descriptionHeight):
     # checks if we're done writing the description
     if not descriptionContent: break
     
     # there's a leading indent after the first line
     if i > 0: descriptionContent = "  " + descriptionContent
     
     # we only want to work with content up until the next newline
     if "\n" in descriptionContent:
       lineContent, descriptionContent = descriptionContent.split("\n", 1)
     else: lineContent, descriptionContent = descriptionContent, ""
     
     if i != descriptionHeight - 1:
       # there's more lines to display
       msg, remainder = uiTools.cropStr(lineContent, width - 2, 4, 4, uiTools.END_WITH_HYPHEN, True)
       descriptionContent = remainder.strip() + descriptionContent
     else:
       # this is the last line, end it with an ellipse
       msg = uiTools.cropStr(lineContent, width - 2, 4, 4)
     
     self.addstr(3 + i, 2, msg, selectionFormat)
示例#24
0
文件: wizard.py 项目: JustMe23/arm
def showConfirmationDialog(torrcContents, torrcLocation):
  """
  Shows a confirmation dialog with the given torrc contents, returning CANCEL,
  NEXT, or BACK based on the selection.
  
  Arguments:
    torrcContents - lines of torrc contents to be presented
    torrcLocation - path where the torrc will be placed
  """
  
  torrcLines = torrcContents.split("\n")
  options = ["Cancel", "Back to Setup", "Start Tor"]
  
  control = cli.controller.getController()
  screenHeight = control.getScreen().getmaxyx()[0]
  stickyHeight = sum([stickyPanel.getHeight() for stickyPanel in control.getStickyPanels()])
  isScrollbarVisible = len(torrcLines) + stickyHeight + 5 > screenHeight
  
  xOffset = 3 if isScrollbarVisible else 0
  popup, width, height = cli.popups.init(len(torrcLines) + 5, 84 + xOffset)
  if not popup: return False
  
  try:
    scroll, selection = 0, 2
    curses.cbreak()
    
    while True:
      popup.win.erase()
      popup.win.box()
      
      # renders the scrollbar
      if isScrollbarVisible:
        popup.addScrollBar(scroll, scroll + height - 5, len(torrcLines), 1, height - 4, 1)
      
      # shows the path where the torrc will be placed
      titleMsg = "The following will be placed at '%s':" % torrcLocation
      popup.addstr(0, 0, titleMsg, curses.A_STANDOUT)
      
      # renders the torrc contents
      for i in range(scroll, min(len(torrcLines), height - 5 + scroll)):
        # parses the argument and comment from options
        option, arg, comment = uiTools.cropStr(torrcLines[i], width - 4 - xOffset), "", ""
        
        div = option.find("#")
        if div != -1: option, comment = option[:div], option[div:]
        
        div = option.strip().find(" ")
        if div != -1: option, arg = option[:div], option[div:]
        
        drawX = 2 + xOffset
        popup.addstr(i + 1 - scroll, drawX, option, curses.A_BOLD | uiTools.getColor("green"))
        drawX += len(option)
        popup.addstr(i + 1 - scroll, drawX, arg, curses.A_BOLD | uiTools.getColor("cyan"))
        drawX += len(arg)
        popup.addstr(i + 1 - scroll, drawX, comment, uiTools.getColor("white"))
      
      # divider between the torrc and the options
      popup.addch(height - 4, 0, curses.ACS_LTEE)
      popup.addch(height - 4, width, curses.ACS_RTEE)
      popup.hline(height - 4, 1, width - 1)
      if isScrollbarVisible: popup.addch(height - 4, 2, curses.ACS_BTEE)
      
      # renders the selection options
      confirmationMsg = "Run tor with the above configuration?"
      popup.addstr(height - 3, width - len(confirmationMsg) - 1, confirmationMsg, uiTools.getColor("green") | curses.A_BOLD)
      
      drawX = width - 1
      for i in range(len(options) - 1, -1, -1):
        optionLabel = " %s " % options[i]
        drawX -= (len(optionLabel) + 4)
        
        selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
        popup.addstr(height - 2, drawX, "[", uiTools.getColor("green"))
        popup.addstr(height - 2, drawX + 1, optionLabel, uiTools.getColor("green") | selectionFormat | curses.A_BOLD)
        popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]", uiTools.getColor("green"))
        
        drawX -= 1 # space gap between the options
      
      popup.win.refresh()
      key = cli.controller.getController().getScreen().getch()
      
      if key == curses.KEY_LEFT:
        selection = (selection - 1) % len(options)
      elif key == curses.KEY_RIGHT:
        selection = (selection + 1) % len(options)
      elif uiTools.isScrollKey(key):
        scroll = uiTools.getScrollPosition(key, scroll, height - 5, len(torrcLines))
      elif uiTools.isSelectionKey(key):
        if selection == 0: return CANCEL
        elif selection == 1: return BACK
        else: return NEXT
      elif key in (27, ord('q'), ord('Q')): return CANCEL
  finally:
    cli.popups.finalize()
示例#25
0
    def draw(self, subwindow, width, height):
        self.valsLock.acquire()

        # draws the top label
        titleLabel = "%s Configuration:" % ("Tor" if self.configType
                                            == TOR_STATE else "Arm")
        self.addstr(0, 0, titleLabel, curses.A_STANDOUT)

        # panel with details for the current selection
        detailPanelHeight = self._config[
            "features.config.selectionDetails.height"]
        if detailPanelHeight == 0 or detailPanelHeight + 2 >= height:
            # no detail panel
            detailPanelHeight = 0
            scrollLoc = self.scroller.getScrollLoc(self.confContents,
                                                   height - 1)
            cursorSelection = self.getSelection()
        else:
            # Shrink detail panel if there isn't sufficient room for the whole
            # thing. The extra line is for the bottom border.
            detailPanelHeight = min(height - 1, detailPanelHeight + 1)
            scrollLoc = self.scroller.getScrollLoc(
                self.confContents, height - 1 - detailPanelHeight)
            cursorSelection = self.getSelection()

            self._drawSelectionPanel(cursorSelection, width, detailPanelHeight,
                                     titleLabel)

        # draws left-hand scroll bar if content's longer than the height
        scrollOffset = 0
        if len(self.confContents) > height - detailPanelHeight - 1:
            scrollOffset = 3
            self.addScrollBar(scrollLoc,
                              scrollLoc + height - detailPanelHeight - 1,
                              len(self.confContents), 1 + detailPanelHeight)

        optionWidth = self._config["features.config.state.colWidth.option"]
        valueWidth = self._config["features.config.state.colWidth.value"]
        descriptionWidth = max(
            0, width - scrollOffset - optionWidth - valueWidth - 2)

        for lineNum in range(scrollLoc, len(self.confContents)):
            entry = self.confContents[lineNum]
            drawLine = lineNum + detailPanelHeight + 1 - scrollLoc

            optionLabel = uiTools.cropStr(entry.get(FIELD_OPTION), optionWidth)
            valueLabel = uiTools.cropStr(entry.get(FIELD_VALUE), valueWidth)

            # ends description at the first newline
            descriptionLabel = uiTools.cropStr(
                entry.get(FIELD_DESCRIPTION).split("\n")[0], descriptionWidth,
                None)

            lineFormat = curses.A_NORMAL if entry.get(
                FIELD_IS_DEFAULT) else curses.A_BOLD
            if entry.get(FIELD_CATEGORY):
                lineFormat |= uiTools.getColor(
                    CATEGORY_COLOR[entry.get(FIELD_CATEGORY)])
            if entry == cursorSelection: lineFormat |= curses.A_STANDOUT

            lineTextLayout = "%%-%is %%-%is %%-%is" % (optionWidth, valueWidth,
                                                       descriptionWidth)
            lineText = lineTextLayout % (optionLabel, valueLabel,
                                         descriptionLabel)
            self.addstr(drawLine, scrollOffset, lineText, lineFormat)

            if drawLine >= height: break

        self.valsLock.release()
示例#26
0
 def doHelp(self, arg, outputEntry):
   """
   Performs the '/help' operation, giving usage information for the given
   argument or a general summary if there wasn't one.
   """
   
   arg = arg.upper()
   
   # If there's multiple arguments then just take the first. This is
   # particularly likely if they're trying to query a full command (for
   # instance "/help GETINFO version")
   
   arg = arg.split(" ")[0]
   
   # strip slash if someone enters an interpretor command (ex. "/help /help")
   if arg.startswith("/"): arg = arg[1:]
   
   if arg:
     if arg in HELP_OPTIONS:
       # Provides information for the tor or interpretor argument. This bolds
       # the usage information and indents the description after it.
       usage, description = HELP_OPTIONS[arg]
       
       outputEntry.append((usage + "\n", OUTPUT_FORMAT + (Attr.BOLD, )))
       
       for line in description.split("\n"):
         outputEntry.append(("  " + line + "\n", OUTPUT_FORMAT))
       
       if arg == "GETINFO":
         # if this is the GETINFO option then also list the valid options
         infoOptions = torTools.getConn().getInfo("info/names")
         
         if infoOptions:
           for line in infoOptions.split("\n"):
             if line.startswith("config/*") or line.startswith("dir-usage"):
               continue
             
             lineMatch = re.match("^(.+) -- (.+)$", line)
             
             if lineMatch:
               opt, description = lineMatch.groups()
               
               outputEntry.append(("%-33s" % opt, OUTPUT_FORMAT + (Attr.BOLD, )))
               outputEntry.append((" - %s\n" % description, OUTPUT_FORMAT))
       elif arg == "GETCONF":
         # lists all of the configuration options
         
         confOptions = torTools.getConn().getInfo("config/names")
         if confOptions:
           confEntries = [opt.split(" ", 1)[0] for opt in confOptions.split("\n")]
           
           # displays two columns of 42 characters
           for i in range(0, len(confEntries), 2):
             lineEntries = confEntries[i : i+2]
             
             lineContent = ""
             for entry in lineEntries:
               lineContent += "%-42s" % entry
             
             outputEntry.append((lineContent + "\n", OUTPUT_FORMAT))
           
           outputEntry.append(("For more information use '/help [CONFIG OPTION]'.", OUTPUT_FORMAT + (Attr.BOLD, )))
       elif arg == "SIGNAL":
         # lists descriptions for all of the signals
         for signal, description in SIGNAL_DESCRIPTIONS:
           outputEntry.append(("%-15s" % signal, OUTPUT_FORMAT + (Attr.BOLD, )))
           outputEntry.append((" - %s\n" % description, OUTPUT_FORMAT))
       elif arg == "SETEVENTS":
         # lists all of the event types
         eventOptions = torTools.getConn().getInfo("events/names")
         if eventOptions:
           eventEntries = eventOptions.split()
           
           # displays four columns of 20 characters
           for i in range(0, len(eventEntries), 4):
             lineEntries = eventEntries[i : i+4]
             
             lineContent = ""
             for entry in lineEntries:
               lineContent += "%-20s" % entry
             
             outputEntry.append((lineContent + "\n", OUTPUT_FORMAT))
       elif arg == "USEFEATURE":
         # lists the feature options
         featureOptions = torTools.getConn().getInfo("features/names")
         if featureOptions:
           outputEntry.append((featureOptions + "\n", OUTPUT_FORMAT))
       elif arg in ("LOADCONF", "POSTDESCRIPTOR"):
         # gives a warning that this option isn't yet implemented
         outputEntry.append(("\n" + MULTILINE_UNIMPLEMENTED_NOTICE + "\n", ERROR_FORMAT))
     else:
       # check if this is a configuration option
       manEntry = torConfig.getConfigDescription(arg)
       
       if manEntry:
         # provides basic usage information in bold, followed an indented
         # copy of the man page description (wrapped to eighty characters)
         
         helpTitle = "%s %s (category: %s)\n" % (manEntry.option, manEntry.argUsage, manEntry.category)
         outputEntry.append((helpTitle, OUTPUT_FORMAT + (Attr.BOLD, )))
         
         descLines = manEntry.description.split("\n")
         
         for line in descLines:
           if not line:
             outputEntry.append(("\n", OUTPUT_FORMAT))
           else:
             while line:
               drawPortion, line = uiTools.cropStr(line, 88, 4, 4, uiTools.Ending.HYPHEN, True)
               outputEntry.append(("  %s\n" % drawPortion.strip(), OUTPUT_FORMAT))
       else:
         outputEntry.append(("No help information available for '%s'..." % arg, ERROR_FORMAT))
   else:
     # provides the GENERAL_HELP with everything bolded except descriptions
     for line in GENERAL_HELP.split("\n"):
       cmdStart = line.find(" - ")
       
       if cmdStart != -1:
         outputEntry.append((line[:cmdStart], OUTPUT_FORMAT + (Attr.BOLD, )))
         outputEntry.append((line[cmdStart:] + "\n", OUTPUT_FORMAT))
       else:
         outputEntry.append((line + "\n", OUTPUT_FORMAT + (Attr.BOLD, )))
示例#27
0
文件: connEntry.py 项目: refnode/arm
 def getEtcContent(self, width, listingType):
   """
   Provides the optional content for the connection.
   
   Arguments:
     width       - maximum length of the line
     listingType - primary attribute we're listing connections by
   """
   
   # for applications show the command/pid
   if self.getType() in (Category.SOCKS, Category.HIDDEN, Category.CONTROL):
     displayLabel = ""
     
     if self.appName:
       if self.appPid: displayLabel = "%s (%s)" % (self.appName, self.appPid)
       else: displayLabel = self.appName
     elif self.isAppResolving:
       displayLabel = "resolving..."
     else: displayLabel = "UNKNOWN"
     
     if len(displayLabel) < width:
       return ("%%-%is" % width) % displayLabel
     else: return ""
   
   # for everything else display connection/consensus information
   dstAddress = self.getDestinationLabel(26, includeLocale = True)
   etc, usedSpace = "", 0
   if listingType == entries.ListingType.IP_ADDRESS:
     if width > usedSpace + 42 and CONFIG["features.connection.showColumn.fingerprint"]:
       # show fingerprint (column width: 42 characters)
       etc += "%-40s  " % self.foreign.getFingerprint()
       usedSpace += 42
     
     if width > usedSpace + 10 and CONFIG["features.connection.showColumn.nickname"]:
       # show nickname (column width: remainder)
       nicknameSpace = width - usedSpace
       nicknameLabel = uiTools.cropStr(self.foreign.getNickname(), nicknameSpace, 0)
       etc += ("%%-%is  " % nicknameSpace) % nicknameLabel
       usedSpace += nicknameSpace + 2
   elif listingType == entries.ListingType.HOSTNAME:
     if width > usedSpace + 28 and CONFIG["features.connection.showColumn.destination"]:
       # show destination ip/port/locale (column width: 28 characters)
       etc += "%-26s  " % dstAddress
       usedSpace += 28
     
     if width > usedSpace + 42 and CONFIG["features.connection.showColumn.fingerprint"]:
       # show fingerprint (column width: 42 characters)
       etc += "%-40s  " % self.foreign.getFingerprint()
       usedSpace += 42
     
     if width > usedSpace + 17 and CONFIG["features.connection.showColumn.nickname"]:
       # show nickname (column width: min 17 characters, uses half of the remainder)
       nicknameSpace = 15 + (width - (usedSpace + 17)) / 2
       nicknameLabel = uiTools.cropStr(self.foreign.getNickname(), nicknameSpace, 0)
       etc += ("%%-%is  " % nicknameSpace) % nicknameLabel
       usedSpace += (nicknameSpace + 2)
   elif listingType == entries.ListingType.FINGERPRINT:
     if width > usedSpace + 17:
       # show nickname (column width: min 17 characters, consumes any remaining space)
       nicknameSpace = width - usedSpace - 2
       
       # if there's room then also show a column with the destination
       # ip/port/locale (column width: 28 characters)
       isIpLocaleIncluded = width > usedSpace + 45
       isIpLocaleIncluded &= CONFIG["features.connection.showColumn.destination"]
       if isIpLocaleIncluded: nicknameSpace -= 28
       
       if CONFIG["features.connection.showColumn.nickname"]:
         nicknameLabel = uiTools.cropStr(self.foreign.getNickname(), nicknameSpace, 0)
         etc += ("%%-%is  " % nicknameSpace) % nicknameLabel
         usedSpace += nicknameSpace + 2
       
       if isIpLocaleIncluded:
         etc += "%-26s  " % dstAddress
         usedSpace += 28
   else:
     if width > usedSpace + 42 and CONFIG["features.connection.showColumn.fingerprint"]:
       # show fingerprint (column width: 42 characters)
       etc += "%-40s  " % self.foreign.getFingerprint()
       usedSpace += 42
     
     if width > usedSpace + 28 and CONFIG["features.connection.showColumn.destination"]:
       # show destination ip/port/locale (column width: 28 characters)
       etc += "%-26s  " % dstAddress
       usedSpace += 28
   
   return ("%%-%is" % width) % etc
示例#28
0
def showConfirmationDialog(torrcContents, torrcLocation):
    """
  Shows a confirmation dialog with the given torrc contents, returning CANCEL,
  NEXT, or BACK based on the selection.
  
  Arguments:
    torrcContents - lines of torrc contents to be presented
    torrcLocation - path where the torrc will be placed
  """

    torrcLines = torrcContents.split("\n")
    options = ["Cancel", "Back to Setup", "Start Tor"]

    control = cli.controller.getController()
    screenHeight = control.getScreen().getmaxyx()[0]
    stickyHeight = sum(
        [stickyPanel.getHeight() for stickyPanel in control.getStickyPanels()])
    isScrollbarVisible = len(torrcLines) + stickyHeight + 5 > screenHeight

    xOffset = 3 if isScrollbarVisible else 0
    popup, width, height = cli.popups.init(len(torrcLines) + 5, 84 + xOffset)
    if not popup: return False

    try:
        scroll, selection = 0, 2
        curses.cbreak()

        while True:
            popup.win.erase()
            popup.win.box()

            # renders the scrollbar
            if isScrollbarVisible:
                popup.addScrollBar(scroll, scroll + height - 5,
                                   len(torrcLines), 1, height - 4, 1)

            # shows the path where the torrc will be placed
            titleMsg = "The following will be placed at '%s':" % torrcLocation
            popup.addstr(0, 0, titleMsg, curses.A_STANDOUT)

            # renders the torrc contents
            for i in range(scroll, min(len(torrcLines), height - 5 + scroll)):
                # parses the argument and comment from options
                option, arg, comment = uiTools.cropStr(torrcLines[i], width -
                                                       4 - xOffset), "", ""

                div = option.find("#")
                if div != -1: option, comment = option[:div], option[div:]

                div = option.strip().find(" ")
                if div != -1: option, arg = option[:div], option[div:]

                drawX = 2 + xOffset
                popup.addstr(i + 1 - scroll, drawX, option,
                             curses.A_BOLD | uiTools.getColor("green"))
                drawX += len(option)
                popup.addstr(i + 1 - scroll, drawX, arg,
                             curses.A_BOLD | uiTools.getColor("cyan"))
                drawX += len(arg)
                popup.addstr(i + 1 - scroll, drawX, comment,
                             uiTools.getColor("white"))

            # divider between the torrc and the options
            popup.addch(height - 4, 0, curses.ACS_LTEE)
            popup.addch(height - 4, width, curses.ACS_RTEE)
            popup.hline(height - 4, 1, width - 1)
            if isScrollbarVisible: popup.addch(height - 4, 2, curses.ACS_BTEE)

            # renders the selection options
            confirmationMsg = "Run tor with the above configuration?"
            popup.addstr(height - 3, width - len(confirmationMsg) - 1,
                         confirmationMsg,
                         uiTools.getColor("green") | curses.A_BOLD)

            drawX = width - 1
            for i in range(len(options) - 1, -1, -1):
                optionLabel = " %s " % options[i]
                drawX -= (len(optionLabel) + 4)

                selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
                popup.addstr(height - 2, drawX, "[", uiTools.getColor("green"))
                popup.addstr(
                    height - 2, drawX + 1, optionLabel,
                    uiTools.getColor("green") | selectionFormat
                    | curses.A_BOLD)
                popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]",
                             uiTools.getColor("green"))

                drawX -= 1  # space gap between the options

            popup.win.refresh()
            key = cli.controller.getController().getScreen().getch()

            if key == curses.KEY_LEFT:
                selection = (selection - 1) % len(options)
            elif key == curses.KEY_RIGHT:
                selection = (selection + 1) % len(options)
            elif uiTools.isScrollKey(key):
                scroll = uiTools.getScrollPosition(key, scroll, height - 5,
                                                   len(torrcLines))
            elif uiTools.isSelectionKey(key):
                if selection == 0: return CANCEL
                elif selection == 1: return BACK
                else: return NEXT
            elif key in (27, ord('q'), ord('Q')): return CANCEL
    finally:
        cli.popups.finalize()
示例#29
0
文件: connEntry.py 项目: refnode/arm
 def _getDetailContent(self, width):
   """
   Provides a list with detailed information for this connection.
   
   Arguments:
     width - max length of lines
   """
   
   lines = [""] * 7
   lines[0] = "address: %s" % self.getDestinationLabel(width - 11)
   lines[1] = "locale: %s" % ("??" if self.isPrivate() else self.foreign.getLocale("??"))
   
   # Remaining data concerns the consensus results, with three possible cases:
   # - if there's a single match then display its details
   # - if there's multiple potential relays then list all of the combinations
   #   of ORPorts / Fingerprints
   # - if no consensus data is available then say so (probably a client or
   #   exit connection)
   
   fingerprint = self.foreign.getFingerprint()
   conn = torTools.getConn()
   
   if fingerprint != "UNKNOWN":
     # single match - display information available about it
     nsEntry = conn.getConsensusEntry(fingerprint)
     descEntry = conn.getDescriptorEntry(fingerprint)
     
     # append the fingerprint to the second line
     lines[1] = "%-13sfingerprint: %s" % (lines[1], fingerprint)
     
     if nsEntry:
       # example consensus entry:
       # r murble R8sCM1ar1sS2GulQYFVmvN95xsk RJr6q+wkTFG+ng5v2bdCbVVFfA4 2011-02-21 00:25:32 195.43.157.85 443 0
       # s Exit Fast Guard Named Running Stable Valid
       # w Bandwidth=2540
       # p accept 20-23,43,53,79-81,88,110,143,194,443
       
       nsLines = nsEntry.split("\n")
       
       firstLineComp = nsLines[0].split(" ")
       if len(firstLineComp) >= 9:
         _, nickname, _, _, pubDate, pubTime, _, orPort, dirPort = firstLineComp[:9]
       else: nickname, pubDate, pubTime, orPort, dirPort = "", "", "", "", ""
       
       flags = "unknown"
       if len(nsLines) >= 2 and nsLines[1].startswith("s "):
         flags = nsLines[1][2:]
       
       exitPolicy = conn.getRelayExitPolicy(fingerprint)
       
       if exitPolicy: policyLabel = exitPolicy.getSummary()
       else: policyLabel = "unknown"
       
       dirPortLabel = "" if dirPort == "0" else "dirport: %s" % dirPort
       lines[2] = "nickname: %-25s orport: %-10s %s" % (nickname, orPort, dirPortLabel)
       lines[3] = "published: %s %s" % (pubTime, pubDate)
       lines[4] = "flags: %s" % flags.replace(" ", ", ")
       lines[5] = "exit policy: %s" % policyLabel
     
     if descEntry:
       torVersion, platform, contact = "", "", ""
       
       for descLine in descEntry.split("\n"):
         if descLine.startswith("platform"):
           # has the tor version and platform, ex:
           # platform Tor 0.2.1.29 (r318f470bc5f2ad43) on Linux x86_64
           
           torVersion = descLine[13:descLine.find(" ", 13)]
           platform = descLine[descLine.rfind(" on ") + 4:]
         elif descLine.startswith("contact"):
           contact = descLine[8:]
           
           # clears up some highly common obscuring
           for alias in (" at ", " AT "): contact = contact.replace(alias, "@")
           for alias in (" dot ", " DOT "): contact = contact.replace(alias, ".")
           
           break # contact lines come after the platform
       
       lines[3] = "%-35s os: %-14s version: %s" % (lines[3], platform, torVersion)
       
       # contact information is an optional field
       if contact: lines[6] = "contact: %s" % contact
   else:
     allMatches = conn.getRelayFingerprint(self.foreign.getIpAddr(), getAllMatches = True)
     
     if allMatches:
       # multiple matches
       lines[2] = "Multiple matches, possible fingerprints are:"
       
       for i in range(len(allMatches)):
         isLastLine = i == 3
         
         relayPort, relayFingerprint = allMatches[i]
         lineText = "%i. or port: %-5s fingerprint: %s" % (i, relayPort, relayFingerprint)
         
         # if there's multiple lines remaining at the end then give a count
         remainingRelays = len(allMatches) - i
         if isLastLine and remainingRelays > 1:
           lineText = "... %i more" % remainingRelays
         
         lines[3 + i] = lineText
         
         if isLastLine: break
     else:
       # no consensus entry for this ip address
       lines[2] = "No consensus data found"
   
   # crops any lines that are too long
   for i in range(len(lines)):
     lines[i] = uiTools.cropStr(lines[i], width - 2)
   
   return lines
示例#30
0
文件: logPanel.py 项目: zhou-kz/arm
    def _getTitle(self, width):
        """
    Provides the label used for the panel, looking like:
      Events (ARM NOTICE - ERR, BW - filter: prepopulate):
    
    This truncates the attributes (with an ellipse) if too long, and condenses
    runlevel ranges if there's three or more in a row (for instance ARM_INFO,
    ARM_NOTICE, and ARM_WARN becomes "ARM_INFO - WARN").
    
    Arguments:
      width - width constraint the label needs to fix in
    """

        # usually the attributes used to make the label are decently static, so
        # provide cached results if they're unchanged
        self.valsLock.acquire()
        currentPattern = self.regexFilter.pattern if self.regexFilter else None
        isUnchanged = self._titleArgs[0] == self.loggedEvents
        isUnchanged &= self._titleArgs[1] == currentPattern
        isUnchanged &= self._titleArgs[2] == width
        if isUnchanged:
            self.valsLock.release()
            return self._titleCache

        eventsList = list(self.loggedEvents)
        if not eventsList:
            if not currentPattern:
                panelLabel = "Events:"
            else:
                labelPattern = uiTools.cropStr(currentPattern, width - 18)
                panelLabel = "Events (filter: %s):" % labelPattern
        else:
            # does the following with all runlevel types (tor, arm, and torctl):
            # - pulls to the start of the list
            # - condenses range if there's three or more in a row (ex. "ARM_INFO - WARN")
            # - condense further if there's identical runlevel ranges for multiple
            #   types (ex. "NOTICE - ERR, ARM_NOTICE - ERR" becomes "TOR/ARM NOTICE - ERR")
            tmpRunlevels = [
            ]  # runlevels pulled from the list (just the runlevel part)
            runlevelRanges = [
            ]  # tuple of type, startLevel, endLevel for ranges to be consensed

            # reverses runlevels and types so they're appended in the right order
            reversedRunlevels = list(RUNLEVELS)
            reversedRunlevels.reverse()
            for prefix in ("TORCTL_", "ARM_", ""):
                # blank ending runlevel forces the break condition to be reached at the end
                for runlevel in reversedRunlevels + [""]:
                    eventType = prefix + runlevel
                    if runlevel and eventType in eventsList:
                        # runlevel event found, move to the tmp list
                        eventsList.remove(eventType)
                        tmpRunlevels.append(runlevel)
                    elif tmpRunlevels:
                        # adds all tmp list entries to the start of eventsList
                        if len(tmpRunlevels) >= 3:
                            # save condense sequential runlevels to be added later
                            runlevelRanges.append(
                                (prefix, tmpRunlevels[-1], tmpRunlevels[0]))
                        else:
                            # adds runlevels individaully
                            for tmpRunlevel in tmpRunlevels:
                                eventsList.insert(0, prefix + tmpRunlevel)

                        tmpRunlevels = []

            # adds runlevel ranges, condensing if there's identical ranges
            for i in range(len(runlevelRanges)):
                if runlevelRanges[i]:
                    prefix, startLevel, endLevel = runlevelRanges[i]

                    # check for matching ranges
                    matches = []
                    for j in range(i + 1, len(runlevelRanges)):
                        if runlevelRanges[j] and runlevelRanges[j][
                                1] == startLevel and runlevelRanges[j][
                                    2] == endLevel:
                            matches.append(runlevelRanges[j])
                            runlevelRanges[j] = None

                    if matches:
                        # strips underscores and replaces empty entries with "TOR"
                        prefixes = [entry[0] for entry in matches] + [prefix]
                        for k in range(len(prefixes)):
                            if prefixes[k] == "": prefixes[k] = "TOR"
                            else: prefixes[k] = prefixes[k].replace("_", "")

                        eventsList.insert(
                            0, "%s %s - %s" %
                            ("/".join(prefixes), startLevel, endLevel))
                    else:
                        eventsList.insert(
                            0, "%s%s - %s" % (prefix, startLevel, endLevel))

            # truncates to use an ellipsis if too long, for instance:
            attrLabel = ", ".join(eventsList)
            if currentPattern: attrLabel += " - filter: %s" % currentPattern
            attrLabel = uiTools.cropStr(attrLabel, width - 10, 1)
            if attrLabel: attrLabel = " (%s)" % attrLabel
            panelLabel = "Events%s:" % attrLabel

        # cache results and return
        self._titleCache = panelLabel
        self._titleArgs = (list(self.loggedEvents), currentPattern, width)
        self.valsLock.release()
        return panelLabel
示例#31
0
 def draw(self, subwindow, width, height):
   self.valsLock.acquire()
   isWide = width + 1 >= MIN_DUAL_COL_WIDTH
   
   # space available for content
   if isWide:
     leftWidth = max(width / 2, 77)
     rightWidth = width - leftWidth
   else: leftWidth = rightWidth = width
   
   # Line 1 / Line 1 Left (system and tor version information)
   sysNameLabel = "arm - %s" % self.vals["sys/hostname"]
   contentSpace = min(leftWidth, 40)
   
   if len(sysNameLabel) + 10 <= contentSpace:
     sysTypeLabel = "%s %s" % (self.vals["sys/os"], self.vals["sys/version"])
     sysTypeLabel = uiTools.cropStr(sysTypeLabel, contentSpace - len(sysNameLabel) - 3, 4)
     self.addstr(0, 0, "%s (%s)" % (sysNameLabel, sysTypeLabel))
   else:
     self.addstr(0, 0, uiTools.cropStr(sysNameLabel, contentSpace))
   
   contentSpace = leftWidth - 43
   if 7 + len(self.vals["tor/version"]) + len(self.vals["tor/versionStatus"]) <= contentSpace:
     versionColor = VERSION_STATUS_COLORS[self.vals["tor/versionStatus"]] if \
         self.vals["tor/versionStatus"] in VERSION_STATUS_COLORS else "white"
     versionStatusMsg = "<%s>%s</%s>" % (versionColor, self.vals["tor/versionStatus"], versionColor)
     self.addfstr(0, 43, "Tor %s (%s)" % (self.vals["tor/version"], versionStatusMsg))
   elif 11 <= contentSpace:
     self.addstr(0, 43, uiTools.cropStr("Tor %s" % self.vals["tor/version"], contentSpace, 4))
   
   # Line 2 / Line 2 Left (tor ip/port information)
   if self.vals["tor/orPort"]:
     # acting as a relay (we can assume certain parameters are set
     entry = ""
     dirPortLabel = ", Dir Port: %s" % self.vals["tor/dirPort"] if self.vals["tor/dirPort"] != "0" else ""
     for label in (self.vals["tor/nickname"], " - " + self.vals["tor/address"], ":" + self.vals["tor/orPort"], dirPortLabel):
       if len(entry) + len(label) <= leftWidth: entry += label
       else: break
   else:
     # non-relay (client only)
     # TODO: not sure what sort of stats to provide...
     entry = "<red><b>Relaying Disabled</b></red>"
   
   if self.vals["tor/isAuthPassword"]: authType = "password"
   elif self.vals["tor/isAuthCookie"]: authType = "cookie"
   else: authType = "open"
   
   if len(entry) + 19 + len(self.vals["tor/controlPort"]) + len(authType) <= leftWidth:
     authColor = "red" if authType == "open" else "green"
     authLabel = "<%s>%s</%s>" % (authColor, authType, authColor)
     self.addfstr(1, 0, "%s, Control Port (%s): %s" % (entry, authLabel, self.vals["tor/controlPort"]))
   elif len(entry) + 16 + len(self.vals["tor/controlPort"]) <= leftWidth:
     self.addstr(1, 0, "%s, Control Port: %s" % (entry, self.vals["tor/controlPort"]))
   else: self.addstr(1, 0, entry)
   
   # Line 3 / Line 1 Right (system usage info)
   y, x = (0, leftWidth) if isWide else (2, 0)
   if self.vals["stat/rss"] != "0": memoryLabel = uiTools.getSizeLabel(int(self.vals["stat/rss"]) * 1024)
   else: memoryLabel = "0"
   
   sysFields = ((0, "cpu: %s%% tor, %s%% arm" % (self.vals["stat/%torCpu"], self.vals["stat/%armCpu"])),
                (27, "mem: %s (%s%%)" % (memoryLabel, self.vals["stat/%mem"])),
                (47, "pid: %s" % (self.vals["stat/pid"] if self._isTorConnected else "")),
                (59, "uptime: %s" % self.vals["stat/etime"]))
   
   for (start, label) in sysFields:
     if start + len(label) <= rightWidth: self.addstr(y, x + start, label)
     else: break
   
   if self.vals["tor/orPort"]:
     # Line 4 / Line 2 Right (fingerprint)
     y, x = (1, leftWidth) if isWide else (3, 0)
     fingerprintLabel = uiTools.cropStr("fingerprint: %s" % self.vals["tor/fingerprint"], width)
     self.addstr(y, x, fingerprintLabel)
     
     # Line 5 / Line 3 Left (flags)
     if self._isTorConnected:
       flagLine = "flags: "
       for flag in self.vals["tor/flags"]:
         flagColor = FLAG_COLORS[flag] if flag in FLAG_COLORS.keys() else "white"
         flagLine += "<b><%s>%s</%s></b>, " % (flagColor, flag, flagColor)
       
       if len(self.vals["tor/flags"]) > 0: flagLine = flagLine[:-2]
       else: flagLine += "<b><cyan>none</cyan></b>"
       
       self.addfstr(2 if isWide else 4, 0, flagLine)
     else:
       statusTime = torTools.getConn().getStatus()[1]
       statusTimeLabel = time.strftime("%H:%M %m/%d/%Y", time.localtime(statusTime))
       self.addfstr(2 if isWide else 4, 0, "<b><red>Tor Disconnected</red></b> (%s)" % statusTimeLabel)
     
     # Undisplayed / Line 3 Right (exit policy)
     if isWide:
       exitPolicy = self.vals["tor/exitPolicy"]
       
       # adds note when default exit policy is appended
       if exitPolicy == "": exitPolicy = "<default>"
       elif not exitPolicy.endswith((" *:*", " *")): exitPolicy += ", <default>"
       
       # color codes accepts to be green, rejects to be red, and default marker to be cyan
       isSimple = len(exitPolicy) > rightWidth - 13
       policies = exitPolicy.split(", ")
       for i in range(len(policies)):
         policy = policies[i].strip()
         displayedPolicy = policy.replace("accept", "").replace("reject", "").strip() if isSimple else policy
         if policy.startswith("accept"): policy = "<green><b>%s</b></green>" % displayedPolicy
         elif policy.startswith("reject"): policy = "<red><b>%s</b></red>" % displayedPolicy
         elif policy.startswith("<default>"): policy = "<cyan><b>%s</b></cyan>" % displayedPolicy
         policies[i] = policy
       
       self.addfstr(2, leftWidth, "exit policy: %s" % ", ".join(policies))
   else:
     # Client only
     # TODO: not sure what information to provide here...
     pass
   
   self._isLastDrawWide = isWide
   self._isChanged = False
   self.valsLock.release()
示例#32
0
 def showWriteDialog(self):
   """
   Provies an interface to confirm if the configuration is saved and, if so,
   where.
   """
   
   # display a popup for saving the current configuration
   configLines = torConfig.getCustomOptions(True)
   popup, width, height = popups.init(len(configLines) + 2)
   if not popup: return
   
   try:
     # displayed options (truncating the labels if there's limited room)
     if width >= 30: selectionOptions = ("Save", "Save As...", "Cancel")
     else: selectionOptions = ("Save", "Save As", "X")
     
     # checks if we can show options beside the last line of visible content
     isOptionLineSeparate = False
     lastIndex = min(height - 2, len(configLines) - 1)
     
     # if we don't have room to display the selection options and room to
     # grow then display the selection options on its own line
     if width < (30 + len(configLines[lastIndex])):
       popup.setHeight(height + 1)
       popup.redraw(True) # recreates the window instance
       newHeight, _ = popup.getPreferredSize()
       
       if newHeight > height:
         height = newHeight
         isOptionLineSeparate = True
     
     key, selection = 0, 2
     while not uiTools.isSelectionKey(key):
       # if the popup has been resized then recreate it (needed for the
       # proper border height)
       newHeight, newWidth = popup.getPreferredSize()
       if (height, width) != (newHeight, newWidth):
         height, width = newHeight, newWidth
         popup.redraw(True)
       
       # if there isn't room to display the popup then cancel it
       if height <= 2:
         selection = 2
         break
       
       popup.win.erase()
       popup.win.box()
       popup.addstr(0, 0, "Configuration being saved:", curses.A_STANDOUT)
       
       visibleConfigLines = height - 3 if isOptionLineSeparate else height - 2
       for i in range(visibleConfigLines):
         line = uiTools.cropStr(configLines[i], width - 2)
         
         if " " in line:
           option, arg = line.split(" ", 1)
           popup.addstr(i + 1, 1, option, curses.A_BOLD | uiTools.getColor("green"))
           popup.addstr(i + 1, len(option) + 2, arg, curses.A_BOLD | uiTools.getColor("cyan"))
         else:
           popup.addstr(i + 1, 1, line, curses.A_BOLD | uiTools.getColor("green"))
       
       # draws selection options (drawn right to left)
       drawX = width - 1
       for i in range(len(selectionOptions) - 1, -1, -1):
         optionLabel = selectionOptions[i]
         drawX -= (len(optionLabel) + 2)
         
         # if we've run out of room then drop the option (this will only
         # occure on tiny displays)
         if drawX < 1: break
         
         selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
         popup.addstr(height - 2, drawX, "[")
         popup.addstr(height - 2, drawX + 1, optionLabel, selectionFormat | curses.A_BOLD)
         popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]")
         
         drawX -= 1 # space gap between the options
       
       popup.win.refresh()
       
       key = cli.controller.getController().getScreen().getch()
       if key == curses.KEY_LEFT: selection = max(0, selection - 1)
       elif key == curses.KEY_RIGHT: selection = min(len(selectionOptions) - 1, selection + 1)
     
     if selection in (0, 1):
       loadedTorrc, promptCanceled = torConfig.getTorrc(), False
       try: configLocation = loadedTorrc.getConfigLocation()
       except IOError: configLocation = ""
       
       if selection == 1:
         # prompts user for a configuration location
         configLocation = popups.inputPrompt("Save to (esc to cancel): ", configLocation)
         if not configLocation: promptCanceled = True
       
       if not promptCanceled:
         try:
           torConfig.saveConf(configLocation, configLines)
           msg = "Saved configuration to %s" % configLocation
         except IOError, exc:
           msg = "Unable to save configuration (%s)" % sysTools.getFileErrorMsg(exc)
         
         popups.showMsg(msg, 2)
   finally: popups.finalize()
示例#33
0
文件: logPanel.py 项目: refnode/arm
 def draw(self, width, height):
   """
   Redraws message log. Entries stretch to use available space and may
   contain up to two lines. Starts with newest entries.
   """
   
   currentLog = self.getAttr("msgLog")
   
   self.valsLock.acquire()
   self._lastLoggedEvents, self._lastUpdate = list(currentLog), time.time()
   
   # draws the top label
   if self.isTitleVisible():
     self.addstr(0, 0, self._getTitle(width), curses.A_STANDOUT)
   
   # restricts scroll location to valid bounds
   self.scroll = max(0, min(self.scroll, self.lastContentHeight - height + 1))
   
   # draws left-hand scroll bar if content's longer than the height
   msgIndent, dividerIndent = 1, 0 # offsets for scroll bar
   isScrollBarVisible = self.lastContentHeight > height - 1
   if isScrollBarVisible:
     msgIndent, dividerIndent = 3, 2
     self.addScrollBar(self.scroll, self.scroll + height - 1, self.lastContentHeight, 1)
   
   # draws log entries
   lineCount = 1 - self.scroll
   seenFirstDateDivider = False
   dividerAttr, duplicateAttr = curses.A_BOLD | uiTools.getColor("yellow"), curses.A_BOLD | uiTools.getColor("green")
   
   isDatesShown = self.regexFilter == None and self._config["features.log.showDateDividers"]
   eventLog = getDaybreaks(currentLog, self.isPaused()) if isDatesShown else list(currentLog)
   if not self.showDuplicates:
     deduplicatedLog = getDuplicates(eventLog)
     
     if deduplicatedLog == None:
       msg = "Deduplication took too long. Its current implementation has difficulty handling large logs so disabling it to keep the interface responsive."
       log.log(log.WARN, msg)
       self.showDuplicates = True
       deduplicatedLog = [(entry, 0) for entry in eventLog]
   else: deduplicatedLog = [(entry, 0) for entry in eventLog]
   
   # determines if we have the minimum width to show date dividers
   showDaybreaks = width - dividerIndent >= 3
   
   while deduplicatedLog:
     entry, duplicateCount = deduplicatedLog.pop(0)
     
     if self.regexFilter and not self.regexFilter.search(entry.getDisplayMessage()):
       continue  # filter doesn't match log message - skip
     
     # checks if we should be showing a divider with the date
     if entry.type == DAYBREAK_EVENT:
       # bottom of the divider
       if seenFirstDateDivider:
         if lineCount >= 1 and lineCount < height and showDaybreaks:
           self.addch(lineCount, dividerIndent, curses.ACS_LLCORNER,  dividerAttr)
           self.hline(lineCount, dividerIndent + 1, width - dividerIndent - 2, dividerAttr)
           self.addch(lineCount, width - 1, curses.ACS_LRCORNER, dividerAttr)
         
         lineCount += 1
       
       # top of the divider
       if lineCount >= 1 and lineCount < height and showDaybreaks:
         timeLabel = time.strftime(" %B %d, %Y ", time.localtime(entry.timestamp))
         self.addch(lineCount, dividerIndent, curses.ACS_ULCORNER, dividerAttr)
         self.addch(lineCount, dividerIndent + 1, curses.ACS_HLINE, dividerAttr)
         self.addstr(lineCount, dividerIndent + 2, timeLabel, curses.A_BOLD | dividerAttr)
         
         lineLength = width - dividerIndent - len(timeLabel) - 3
         self.hline(lineCount, dividerIndent + len(timeLabel) + 2, lineLength, dividerAttr)
         self.addch(lineCount, dividerIndent + len(timeLabel) + 2 + lineLength, curses.ACS_URCORNER, dividerAttr)
       
       seenFirstDateDivider = True
       lineCount += 1
     else:
       # entry contents to be displayed, tuples of the form:
       # (msg, formatting, includeLinebreak)
       displayQueue = []
       
       msgComp = entry.getDisplayMessage().split("\n")
       for i in range(len(msgComp)):
         font = curses.A_BOLD if "ERR" in entry.type else curses.A_NORMAL # emphasizes ERR messages
         displayQueue.append((msgComp[i].strip(), font | uiTools.getColor(entry.color), i != len(msgComp) - 1))
       
       if duplicateCount:
         pluralLabel = "s" if duplicateCount > 1 else ""
         duplicateMsg = DUPLICATE_MSG % (duplicateCount, pluralLabel)
         displayQueue.append((duplicateMsg, duplicateAttr, False))
       
       cursorLoc, lineOffset = msgIndent, 0
       maxEntriesPerLine = self._config["features.log.maxLinesPerEntry"]
       while displayQueue:
         msg, format, includeBreak = displayQueue.pop(0)
         drawLine = lineCount + lineOffset
         if lineOffset == maxEntriesPerLine: break
         
         maxMsgSize = width - cursorLoc - 1
         if len(msg) > maxMsgSize:
           # message is too long - break it up
           if lineOffset == maxEntriesPerLine - 1:
             msg = uiTools.cropStr(msg, maxMsgSize)
           else:
             msg, remainder = uiTools.cropStr(msg, maxMsgSize, 4, 4, uiTools.Ending.HYPHEN, True)
             displayQueue.insert(0, (remainder.strip(), format, includeBreak))
           
           includeBreak = True
         
         if drawLine < height and drawLine >= 1:
           if seenFirstDateDivider and width - dividerIndent >= 3 and showDaybreaks:
             self.addch(drawLine, dividerIndent, curses.ACS_VLINE, dividerAttr)
             self.addch(drawLine, width - 1, curses.ACS_VLINE, dividerAttr)
           
           self.addstr(drawLine, cursorLoc, msg, format)
         
         cursorLoc += len(msg)
         
         if includeBreak or not displayQueue:
           lineOffset += 1
           cursorLoc = msgIndent + ENTRY_INDENT
       
       lineCount += lineOffset
     
     # if this is the last line and there's room, then draw the bottom of the divider
     if not deduplicatedLog and seenFirstDateDivider:
       if lineCount < height and showDaybreaks:
         self.addch(lineCount, dividerIndent, curses.ACS_LLCORNER, dividerAttr)
         self.hline(lineCount, dividerIndent + 1, width - dividerIndent - 2, dividerAttr)
         self.addch(lineCount, width - 1, curses.ACS_LRCORNER, dividerAttr)
       
       lineCount += 1
   
   # redraw the display if...
   # - lastContentHeight was off by too much
   # - we're off the bottom of the page
   newContentHeight = lineCount + self.scroll - 1
   contentHeightDelta = abs(self.lastContentHeight - newContentHeight)
   forceRedraw, forceRedrawReason = True, ""
   
   if contentHeightDelta >= CONTENT_HEIGHT_REDRAW_THRESHOLD:
     forceRedrawReason = "estimate was off by %i" % contentHeightDelta
   elif newContentHeight > height and self.scroll + height - 1 > newContentHeight:
     forceRedrawReason = "scrolled off the bottom of the page"
   elif not isScrollBarVisible and newContentHeight > height - 1:
     forceRedrawReason = "scroll bar wasn't previously visible"
   elif isScrollBarVisible and newContentHeight <= height - 1:
     forceRedrawReason = "scroll bar shouldn't be visible"
   else: forceRedraw = False
   
   self.lastContentHeight = newContentHeight
   if forceRedraw:
     forceRedrawReason = "redrawing the log panel with the corrected content height (%s)" % forceRedrawReason
     log.log(self._config["log.logPanel.forceDoubleRedraw"], forceRedrawReason)
     self.redraw(True)
   
   self.valsLock.release()
示例#34
0
文件: headerPanel.py 项目: twilde/arm
 def draw(self, width, height):
   self.valsLock.acquire()
   isWide = width + 1 >= MIN_DUAL_COL_WIDTH
   
   # space available for content
   if isWide:
     leftWidth = max(width / 2, 77)
     rightWidth = width - leftWidth
   else: leftWidth = rightWidth = width
   
   # Line 1 / Line 1 Left (system and tor version information)
   sysNameLabel = "arm - %s" % self.vals["sys/hostname"]
   contentSpace = min(leftWidth, 40)
   
   if len(sysNameLabel) + 10 <= contentSpace:
     sysTypeLabel = "%s %s" % (self.vals["sys/os"], self.vals["sys/version"])
     sysTypeLabel = uiTools.cropStr(sysTypeLabel, contentSpace - len(sysNameLabel) - 3, 4)
     self.addstr(0, 0, "%s (%s)" % (sysNameLabel, sysTypeLabel))
   else:
     self.addstr(0, 0, uiTools.cropStr(sysNameLabel, contentSpace))
   
   contentSpace = leftWidth - 43
   if 7 + len(self.vals["tor/version"]) + len(self.vals["tor/versionStatus"]) <= contentSpace:
     if self.vals["tor/version"] != "Unknown":
       versionColor = VERSION_STATUS_COLORS[self.vals["tor/versionStatus"]] if \
           self.vals["tor/versionStatus"] in VERSION_STATUS_COLORS else "white"
       labelPrefix = "Tor %s (" % self.vals["tor/version"]
       self.addstr(0, 43, labelPrefix)
       self.addstr(0, 43 + len(labelPrefix), self.vals["tor/versionStatus"], uiTools.getColor(versionColor))
       self.addstr(0, 43 + len(labelPrefix) + len(self.vals["tor/versionStatus"]), ")")
   elif 11 <= contentSpace:
     self.addstr(0, 43, uiTools.cropStr("Tor %s" % self.vals["tor/version"], contentSpace, 4))
   
   # Line 2 / Line 2 Left (tor ip/port information)
   x, includeControlPort = 0, True
   if self.vals["tor/orPort"]:
     myAddress = "Unknown"
     if self.vals["tor/orListenAddr"]: myAddress = self.vals["tor/orListenAddr"]
     elif self.vals["tor/address"]: myAddress = self.vals["tor/address"]
     
     # acting as a relay (we can assume certain parameters are set
     dirPortLabel = ", Dir Port: %s" % self.vals["tor/dirPort"] if self.vals["tor/dirPort"] != "0" else ""
     for label in (self.vals["tor/nickname"], " - " + myAddress, ":" + self.vals["tor/orPort"], dirPortLabel):
       if x + len(label) <= leftWidth:
         self.addstr(1, x, label)
         x += len(label)
       else: break
   else:
     # non-relay (client only)
     if self._isTorConnected:
       self.addstr(1, x, "Relaying Disabled", uiTools.getColor("cyan"))
       x += 17
     else:
       statusTime = torTools.getConn().getStatus()[1]
       
       if statusTime:
         statusTimeLabel = time.strftime("%H:%M %m/%d/%Y, ", time.localtime(statusTime))
       else: statusTimeLabel = "" # never connected to tor
       
       self.addstr(1, x, "Tor Disconnected", curses.A_BOLD | uiTools.getColor("red"))
       self.addstr(1, x + 16, " (%spress r to reconnect)" % statusTimeLabel)
       x += 39 + len(statusTimeLabel)
       includeControlPort = False
   
   if includeControlPort:
     if self.vals["tor/controlPort"] == "0":
       # connected via a control socket
       self.addstr(1, x, ", Control Socket: %s" % self.vals["tor/socketPath"])
     else:
       if self.vals["tor/isAuthPassword"]: authType = "password"
       elif self.vals["tor/isAuthCookie"]: authType = "cookie"
       else: authType = "open"
       
       if x + 19 + len(self.vals["tor/controlPort"]) + len(authType) <= leftWidth:
         authColor = "red" if authType == "open" else "green"
         self.addstr(1, x, ", Control Port (")
         self.addstr(1, x + 16, authType, uiTools.getColor(authColor))
         self.addstr(1, x + 16 + len(authType), "): %s" % self.vals["tor/controlPort"])
       elif x + 16 + len(self.vals["tor/controlPort"]) <= leftWidth:
         self.addstr(1, 0, ", Control Port: %s" % self.vals["tor/controlPort"])
   
   # Line 3 / Line 1 Right (system usage info)
   y, x = (0, leftWidth) if isWide else (2, 0)
   if self.vals["stat/rss"] != "0": memoryLabel = uiTools.getSizeLabel(int(self.vals["stat/rss"]))
   else: memoryLabel = "0"
   
   uptimeLabel = ""
   if self.vals["tor/startTime"]:
     if self.isPaused() or not self._isTorConnected:
       # freeze the uptime when paused or the tor process is stopped
       uptimeLabel = uiTools.getShortTimeLabel(self.getPauseTime() - self.vals["tor/startTime"])
     else:
       uptimeLabel = uiTools.getShortTimeLabel(time.time() - self.vals["tor/startTime"])
   
   sysFields = ((0, "cpu: %s%% tor, %s%% arm" % (self.vals["stat/%torCpu"], self.vals["stat/%armCpu"])),
                (27, "mem: %s (%s%%)" % (memoryLabel, self.vals["stat/%mem"])),
                (47, "pid: %s" % (self.vals["tor/pid"] if self._isTorConnected else "")),
                (59, "uptime: %s" % uptimeLabel))
   
   for (start, label) in sysFields:
     if start + len(label) <= rightWidth: self.addstr(y, x + start, label)
     else: break
   
   if self.vals["tor/orPort"]:
     # Line 4 / Line 2 Right (fingerprint, and possibly file descriptor usage)
     y, x = (1, leftWidth) if isWide else (3, 0)
     
     fingerprintLabel = uiTools.cropStr("fingerprint: %s" % self.vals["tor/fingerprint"], width)
     self.addstr(y, x, fingerprintLabel)
     
     # if there's room and we're able to retrieve both the file descriptor
     # usage and limit then it might be presented
     if width - x - 59 >= 20 and self.vals["tor/fdUsed"] and self.vals["tor/fdLimit"]:
       # display file descriptor usage if we're either configured to do so or
       # running out
       
       fdPercent = 100 * self.vals["tor/fdUsed"] / self.vals["tor/fdLimit"]
       
       if fdPercent >= 60 or self._config["features.showFdUsage"]:
         fdPercentLabel, fdPercentFormat = "%i%%" % fdPercent, curses.A_NORMAL
         if fdPercent >= 95:
           fdPercentFormat = curses.A_BOLD | uiTools.getColor("red")
         elif fdPercent >= 90:
           fdPercentFormat = uiTools.getColor("red")
         elif fdPercent >= 60:
           fdPercentFormat = uiTools.getColor("yellow")
         
         estimateChar = "?" if self.vals["tor/isFdLimitEstimate"] else ""
         baseLabel = "file desc: %i / %i%s (" % (self.vals["tor/fdUsed"], self.vals["tor/fdLimit"], estimateChar)
         
         self.addstr(y, x + 59, baseLabel)
         self.addstr(y, x + 59 + len(baseLabel), fdPercentLabel, fdPercentFormat)
         self.addstr(y, x + 59 + len(baseLabel) + len(fdPercentLabel), ")")
     
     # Line 5 / Line 3 Left (flags)
     if self._isTorConnected:
       y, x = (2 if isWide else 4, 0)
       self.addstr(y, x, "flags: ")
       x += 7
       
       if len(self.vals["tor/flags"]) > 0:
         for i in range(len(self.vals["tor/flags"])):
           flag = self.vals["tor/flags"][i]
           flagColor = FLAG_COLORS[flag] if flag in FLAG_COLORS.keys() else "white"
           
           self.addstr(y, x, flag, curses.A_BOLD | uiTools.getColor(flagColor))
           x += len(flag)
           
           if i < len(self.vals["tor/flags"]) - 1:
             self.addstr(y, x, ", ")
             x += 2
       else:
         self.addstr(y, x, "none", curses.A_BOLD | uiTools.getColor("cyan"))
     else:
       y = 2 if isWide else 4
       statusTime = torTools.getConn().getStatus()[1]
       statusTimeLabel = time.strftime("%H:%M %m/%d/%Y", time.localtime(statusTime))
       self.addstr(y, 0, "Tor Disconnected", curses.A_BOLD | uiTools.getColor("red"))
       self.addstr(y, 16, " (%s) - press r to reconnect" % statusTimeLabel)
     
     # Undisplayed / Line 3 Right (exit policy)
     if isWide:
       exitPolicy = self.vals["tor/exitPolicy"]
       
       # adds note when default exit policy is appended
       if exitPolicy == "": exitPolicy = "<default>"
       elif not exitPolicy.endswith((" *:*", " *")): exitPolicy += ", <default>"
       
       self.addstr(2, leftWidth, "exit policy: ")
       x = leftWidth + 13
       
       # color codes accepts to be green, rejects to be red, and default marker to be cyan
       isSimple = len(exitPolicy) > rightWidth - 13
       policies = exitPolicy.split(", ")
       for i in range(len(policies)):
         policy = policies[i].strip()
         policyLabel = policy.replace("accept", "").replace("reject", "").strip() if isSimple else policy
         
         policyColor = "white"
         if policy.startswith("accept"): policyColor = "green"
         elif policy.startswith("reject"): policyColor = "red"
         elif policy.startswith("<default>"): policyColor = "cyan"
         
         self.addstr(2, x, policyLabel, curses.A_BOLD | uiTools.getColor(policyColor))
         x += len(policyLabel)
         
         if i < len(policies) - 1:
           self.addstr(2, x, ", ")
           x += 2
   else:
     # (Client only) Undisplayed / Line 2 Right (new identity option)
     if isWide:
       conn = torTools.getConn()
       newnymWait = conn.getNewnymWait()
       
       msg = "press 'n' for a new identity"
       if newnymWait > 0:
         pluralLabel = "s" if newnymWait > 1 else ""
         msg = "building circuits, available again in %i second%s" % (newnymWait, pluralLabel)
       
       self.addstr(1, leftWidth, msg)
   
   self.valsLock.release()
示例#35
0
 def showWriteDialog(self):
   """
   Provies an interface to confirm if the configuration is saved and, if so,
   where.
   """
   
   # display a popup for saving the current configuration
   configLines = torConfig.getCustomOptions(True)
   popup, width, height = popups.init(len(configLines) + 2)
   if not popup: return
   
   try:
     # displayed options (truncating the labels if there's limited room)
     if width >= 30: selectionOptions = ("Save", "Save As...", "Cancel")
     else: selectionOptions = ("Save", "Save As", "X")
     
     # checks if we can show options beside the last line of visible content
     isOptionLineSeparate = False
     lastIndex = min(height - 2, len(configLines) - 1)
     
     # if we don't have room to display the selection options and room to
     # grow then display the selection options on its own line
     if width < (30 + len(configLines[lastIndex])):
       popup.setHeight(height + 1)
       popup.redraw(True) # recreates the window instance
       newHeight, _ = popup.getPreferredSize()
       
       if newHeight > height:
         height = newHeight
         isOptionLineSeparate = True
     
     key, selection = 0, 2
     while not uiTools.isSelectionKey(key):
       # if the popup has been resized then recreate it (needed for the
       # proper border height)
       newHeight, newWidth = popup.getPreferredSize()
       if (height, width) != (newHeight, newWidth):
         height, width = newHeight, newWidth
         popup.redraw(True)
       
       # if there isn't room to display the popup then cancel it
       if height <= 2:
         selection = 2
         break
       
       popup.win.erase()
       popup.win.box()
       popup.addstr(0, 0, "Configuration being saved:", curses.A_STANDOUT)
       
       visibleConfigLines = height - 3 if isOptionLineSeparate else height - 2
       for i in range(visibleConfigLines):
         line = uiTools.cropStr(configLines[i], width - 2)
         
         if " " in line:
           option, arg = line.split(" ", 1)
           popup.addstr(i + 1, 1, option, curses.A_BOLD | uiTools.getColor("green"))
           popup.addstr(i + 1, len(option) + 2, arg, curses.A_BOLD | uiTools.getColor("cyan"))
         else:
           popup.addstr(i + 1, 1, line, curses.A_BOLD | uiTools.getColor("green"))
       
       # draws selection options (drawn right to left)
       drawX = width - 1
       for i in range(len(selectionOptions) - 1, -1, -1):
         optionLabel = selectionOptions[i]
         drawX -= (len(optionLabel) + 2)
         
         # if we've run out of room then drop the option (this will only
         # occure on tiny displays)
         if drawX < 1: break
         
         selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
         popup.addstr(height - 2, drawX, "[")
         popup.addstr(height - 2, drawX + 1, optionLabel, selectionFormat | curses.A_BOLD)
         popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]")
         
         drawX -= 1 # space gap between the options
       
       popup.win.refresh()
       
       key = cli.controller.getController().getScreen().getch()
       if key == curses.KEY_LEFT: selection = max(0, selection - 1)
       elif key == curses.KEY_RIGHT: selection = min(len(selectionOptions) - 1, selection + 1)
     
     if selection in (0, 1):
       loadedTorrc, promptCanceled = torConfig.getTorrc(), False
       try: configLocation = loadedTorrc.getConfigLocation()
       except IOError: configLocation = ""
       
       if selection == 1:
         # prompts user for a configuration location
         configLocation = popups.inputPrompt("Save to (esc to cancel): ", configLocation)
         if not configLocation: promptCanceled = True
       
       if not promptCanceled:
         try:
           torConfig.saveConf(configLocation, configLines)
           msg = "Saved configuration to %s" % configLocation
         except IOError, exc:
           msg = "Unable to save configuration (%s)" % sysTools.getFileErrorMsg(exc)
         
         popups.showMsg(msg, 2)
   finally: popups.finalize()
示例#36
0
 def draw(self, subwindow, width, height):
   self.valsLock.acquire()
   
   # If true, we assume that the cached value in self._lastContentHeight is
   # still accurate, and stop drawing when there's nothing more to display.
   # Otherwise the self._lastContentHeight is suspect, and we'll process all
   # the content to check if it's right (and redraw again with the corrected
   # height if not).
   trustLastContentHeight = self._lastContentHeightArgs == (width, height)
   
   # restricts scroll location to valid bounds
   self.scroll = max(0, min(self.scroll, self._lastContentHeight - height + 1))
   
   renderedContents, corrections, confLocation = None, {}, None
   if self.configType == TORRC:
     loadedTorrc = torConfig.getTorrc()
     loadedTorrc.getLock().acquire()
     confLocation = loadedTorrc.getConfigLocation()
     
     if not loadedTorrc.isLoaded():
       renderedContents = ["### Unable to load the torrc ###"]
     else:
       renderedContents = loadedTorrc.getDisplayContents(self.stripComments)
       
       # constructs a mapping of line numbers to the issue on it
       corrections = dict((lineNum, (issue, msg)) for lineNum, issue, msg in loadedTorrc.getCorrections())
     
     loadedTorrc.getLock().release()
   else:
     loadedArmrc = conf.getConfig("arm")
     confLocation = loadedArmrc.path
     renderedContents = list(loadedArmrc.rawContents)
   
   # offset to make room for the line numbers
   lineNumOffset = 0
   if self.showLineNum:
     if len(renderedContents) == 0: lineNumOffset = 2
     else: lineNumOffset = int(math.log10(len(renderedContents))) + 2
   
   # draws left-hand scroll bar if content's longer than the height
   scrollOffset = 0
   if self._config["features.config.file.showScrollbars"] and self._lastContentHeight > height - 1:
     scrollOffset = 3
     self.addScrollBar(self.scroll, self.scroll + height - 1, self._lastContentHeight, 1)
   
   displayLine = -self.scroll + 1 # line we're drawing on
   
   # draws the top label
   if self.showLabel:
     sourceLabel = "Tor" if self.configType == TORRC else "Arm"
     locationLabel = " (%s)" % confLocation if confLocation else ""
     self.addstr(0, 0, "%s Configuration File%s:" % (sourceLabel, locationLabel), curses.A_STANDOUT)
   
   isMultiline = False # true if we're in the middle of a multiline torrc entry
   for lineNumber in range(0, len(renderedContents)):
     lineText = renderedContents[lineNumber]
     lineText = lineText.rstrip() # remove ending whitespace
     
     # blank lines are hidden when stripping comments
     if self.stripComments and not lineText: continue
     
     # splits the line into its component (msg, format) tuples
     lineComp = {"option": ["", curses.A_BOLD | uiTools.getColor("green")],
                 "argument": ["", curses.A_BOLD | uiTools.getColor("cyan")],
                 "correction": ["", curses.A_BOLD | uiTools.getColor("cyan")],
                 "comment": ["", uiTools.getColor("white")]}
     
     # parses the comment
     commentIndex = lineText.find("#")
     if commentIndex != -1:
       lineComp["comment"][0] = lineText[commentIndex:]
       lineText = lineText[:commentIndex]
     
     # splits the option and argument, preserving any whitespace around them
     strippedLine = lineText.strip()
     optionIndex = strippedLine.find(" ")
     if isMultiline:
       # part of a multiline entry started on a previous line so everything
       # is part of the argument
       lineComp["argument"][0] = lineText
     elif optionIndex == -1:
       # no argument provided
       lineComp["option"][0] = lineText
     else:
       optionText = strippedLine[:optionIndex]
       optionEnd = lineText.find(optionText) + len(optionText)
       lineComp["option"][0] = lineText[:optionEnd]
       lineComp["argument"][0] = lineText[optionEnd:]
     
     # flags following lines as belonging to this multiline entry if it ends
     # with a slash
     if strippedLine: isMultiline = strippedLine.endswith("\\")
     
     # gets the correction
     if lineNumber in corrections:
       lineIssue, lineIssueMsg = corrections[lineNumber]
       
       if lineIssue in (torConfig.VAL_DUPLICATE, torConfig.VAL_IS_DEFAULT):
         lineComp["option"][1] = curses.A_BOLD | uiTools.getColor("blue")
         lineComp["argument"][1] = curses.A_BOLD | uiTools.getColor("blue")
       elif lineIssue == torConfig.VAL_MISMATCH:
         lineComp["argument"][1] = curses.A_BOLD | uiTools.getColor("red")
         lineComp["correction"][0] = " (%s)" % lineIssueMsg
       else:
         # For some types of configs the correction field is simply used to
         # provide extra data (for instance, the type for tor state fields).
         lineComp["correction"][0] = " (%s)" % lineIssueMsg
         lineComp["correction"][1] = curses.A_BOLD | uiTools.getColor("magenta")
     
     # draws the line number
     if self.showLineNum and displayLine < height and displayLine >= 1:
       lineNumStr = ("%%%ii" % (lineNumOffset - 1)) % (lineNumber + 1)
       self.addstr(displayLine, scrollOffset, lineNumStr, curses.A_BOLD | uiTools.getColor("yellow"))
     
     # draws the rest of the components with line wrap
     cursorLoc, lineOffset = lineNumOffset + scrollOffset, 0
     maxLinesPerEntry = self._config["features.config.file.maxLinesPerEntry"]
     displayQueue = [lineComp[entry] for entry in ("option", "argument", "correction", "comment")]
     
     while displayQueue:
       msg, format = displayQueue.pop(0)
       
       maxMsgSize, includeBreak = width - cursorLoc, False
       if len(msg) >= maxMsgSize:
         # message is too long - break it up
         if lineOffset == maxLinesPerEntry - 1:
           msg = uiTools.cropStr(msg, maxMsgSize)
         else:
           includeBreak = True
           msg, remainder = uiTools.cropStr(msg, maxMsgSize, 4, 4, uiTools.END_WITH_HYPHEN, True)
           displayQueue.insert(0, (remainder.strip(), format))
       
       drawLine = displayLine + lineOffset
       if msg and drawLine < height and drawLine >= 1:
         self.addstr(drawLine, cursorLoc, msg, format)
       
       # If we're done, and have added content to this line, then start
       # further content on the next line.
       cursorLoc += len(msg)
       includeBreak |= not displayQueue and cursorLoc != lineNumOffset + scrollOffset
       
       if includeBreak:
         lineOffset += 1
         cursorLoc = lineNumOffset + scrollOffset
     
     displayLine += max(lineOffset, 1)
     
     if trustLastContentHeight and displayLine >= height: break
   
   if not trustLastContentHeight:
     self._lastContentHeightArgs = (width, height)
     newContentHeight = displayLine + self.scroll - 1
     
     if self._lastContentHeight != newContentHeight:
       self._lastContentHeight = newContentHeight
       self.redraw(True)
   
   self.valsLock.release()
示例#37
0
    def draw(self, width, height):
        self.valsLock.acquire()
        isWide = width + 1 >= MIN_DUAL_COL_WIDTH

        # space available for content
        if isWide:
            leftWidth = max(width / 2, 77)
            rightWidth = width - leftWidth
        else:
            leftWidth = rightWidth = width

        # Line 1 / Line 1 Left (system and tor version information)
        sysNameLabel = "arm - %s" % self.vals["sys/hostname"]
        contentSpace = min(leftWidth, 40)

        if len(sysNameLabel) + 10 <= contentSpace:
            sysTypeLabel = "%s %s" % (self.vals["sys/os"],
                                      self.vals["sys/version"])
            sysTypeLabel = uiTools.cropStr(
                sysTypeLabel, contentSpace - len(sysNameLabel) - 3, 4)
            self.addstr(0, 0, "%s (%s)" % (sysNameLabel, sysTypeLabel))
        else:
            self.addstr(0, 0, uiTools.cropStr(sysNameLabel, contentSpace))

        contentSpace = leftWidth - 43
        if 7 + len(self.vals["tor/version"]) + len(
                self.vals["tor/versionStatus"]) <= contentSpace:
            if self.vals["tor/version"] != "Unknown":
                versionColor = VERSION_STATUS_COLORS[self.vals["tor/versionStatus"]] if \
                    self.vals["tor/versionStatus"] in VERSION_STATUS_COLORS else "white"
                labelPrefix = "Tor %s (" % self.vals["tor/version"]
                self.addstr(0, 43, labelPrefix)
                self.addstr(0, 43 + len(labelPrefix),
                            self.vals["tor/versionStatus"],
                            uiTools.getColor(versionColor))
                self.addstr(
                    0, 43 + len(labelPrefix) +
                    len(self.vals["tor/versionStatus"]), ")")
        elif 11 <= contentSpace:
            self.addstr(
                0, 43,
                uiTools.cropStr("Tor %s" % self.vals["tor/version"],
                                contentSpace, 4))

        # Line 2 / Line 2 Left (tor ip/port information)
        x, includeControlPort = 0, True
        if self.vals["tor/orPort"]:
            myAddress = "Unknown"
            if self.vals["tor/orListenAddr"]:
                myAddress = self.vals["tor/orListenAddr"]
            elif self.vals["tor/address"]:
                myAddress = self.vals["tor/address"]

            # acting as a relay (we can assume certain parameters are set
            dirPortLabel = ", Dir Port: %s" % self.vals[
                "tor/dirPort"] if self.vals["tor/dirPort"] != "0" else ""
            for label in (self.vals["tor/nickname"], " - " + myAddress,
                          ":" + self.vals["tor/orPort"], dirPortLabel):
                if x + len(label) <= leftWidth:
                    self.addstr(1, x, label)
                    x += len(label)
                else:
                    break
        else:
            # non-relay (client only)
            if self._isTorConnected:
                self.addstr(1, x, "Relaying Disabled",
                            uiTools.getColor("cyan"))
                x += 17
            else:
                statusTime = torTools.getConn().getHeartbeat()

                if statusTime:
                    statusTimeLabel = time.strftime("%H:%M %m/%d/%Y, ",
                                                    time.localtime(statusTime))
                else:
                    statusTimeLabel = ""  # never connected to tor

                self.addstr(1, x, "Tor Disconnected",
                            curses.A_BOLD | uiTools.getColor("red"))
                self.addstr(1, x + 16,
                            " (%spress r to reconnect)" % statusTimeLabel)
                x += 39 + len(statusTimeLabel)
                includeControlPort = False

        if includeControlPort:
            if self.vals["tor/controlPort"] == "0":
                # connected via a control socket
                self.addstr(
                    1, x, ", Control Socket: %s" % self.vals["tor/socketPath"])
            else:
                if self.vals["tor/isAuthPassword"]: authType = "password"
                elif self.vals["tor/isAuthCookie"]: authType = "cookie"
                else: authType = "open"

                if x + 19 + len(self.vals["tor/controlPort"]) + len(
                        authType) <= leftWidth:
                    authColor = "red" if authType == "open" else "green"
                    self.addstr(1, x, ", Control Port (")
                    self.addstr(1, x + 16, authType,
                                uiTools.getColor(authColor))
                    self.addstr(1, x + 16 + len(authType),
                                "): %s" % self.vals["tor/controlPort"])
                elif x + 16 + len(self.vals["tor/controlPort"]) <= leftWidth:
                    self.addstr(
                        1, 0,
                        ", Control Port: %s" % self.vals["tor/controlPort"])

        # Line 3 / Line 1 Right (system usage info)
        y, x = (0, leftWidth) if isWide else (2, 0)
        if self.vals["stat/rss"] != "0":
            memoryLabel = str_tools.get_size_label(int(self.vals["stat/rss"]))
        else:
            memoryLabel = "0"

        uptimeLabel = ""
        if self.vals["tor/startTime"]:
            if self.isPaused() or not self._isTorConnected:
                # freeze the uptime when paused or the tor process is stopped
                uptimeLabel = str_tools.get_short_time_label(
                    self.getPauseTime() - self.vals["tor/startTime"])
            else:
                uptimeLabel = str_tools.get_short_time_label(
                    time.time() - self.vals["tor/startTime"])

        sysFields = ((0, "cpu: %s%% tor, %s%% arm" %
                      (self.vals["stat/%torCpu"], self.vals["stat/%armCpu"])),
                     (27, "mem: %s (%s%%)" %
                      (memoryLabel, self.vals["stat/%mem"])),
                     (47, "pid: %s" %
                      (self.vals["tor/pid"] if self._isTorConnected else "")),
                     (59, "uptime: %s" % uptimeLabel))

        for (start, label) in sysFields:
            if start + len(label) <= rightWidth:
                self.addstr(y, x + start, label)
            else:
                break

        if self.vals["tor/orPort"]:
            # Line 4 / Line 2 Right (fingerprint, and possibly file descriptor usage)
            y, x = (1, leftWidth) if isWide else (3, 0)

            fingerprintLabel = uiTools.cropStr(
                "fingerprint: %s" % self.vals["tor/fingerprint"], width)
            self.addstr(y, x, fingerprintLabel)

            # if there's room and we're able to retrieve both the file descriptor
            # usage and limit then it might be presented
            if width - x - 59 >= 20 and self.vals["tor/fdUsed"] and self.vals[
                    "tor/fdLimit"]:
                # display file descriptor usage if we're either configured to do so or
                # running out

                fdPercent = 100 * self.vals["tor/fdUsed"] / self.vals[
                    "tor/fdLimit"]

                if fdPercent >= 60 or CONFIG["features.showFdUsage"]:
                    fdPercentLabel, fdPercentFormat = "%i%%" % fdPercent, curses.A_NORMAL
                    if fdPercent >= 95:
                        fdPercentFormat = curses.A_BOLD | uiTools.getColor(
                            "red")
                    elif fdPercent >= 90:
                        fdPercentFormat = uiTools.getColor("red")
                    elif fdPercent >= 60:
                        fdPercentFormat = uiTools.getColor("yellow")

                    estimateChar = "?" if self.vals[
                        "tor/isFdLimitEstimate"] else ""
                    baseLabel = "file desc: %i / %i%s (" % (
                        self.vals["tor/fdUsed"], self.vals["tor/fdLimit"],
                        estimateChar)

                    self.addstr(y, x + 59, baseLabel)
                    self.addstr(y, x + 59 + len(baseLabel), fdPercentLabel,
                                fdPercentFormat)
                    self.addstr(y,
                                x + 59 + len(baseLabel) + len(fdPercentLabel),
                                ")")

            # Line 5 / Line 3 Left (flags)
            if self._isTorConnected:
                y, x = (2 if isWide else 4, 0)
                self.addstr(y, x, "flags: ")
                x += 7

                if len(self.vals["tor/flags"]) > 0:
                    for i in range(len(self.vals["tor/flags"])):
                        flag = self.vals["tor/flags"][i]
                        flagColor = FLAG_COLORS[
                            flag] if flag in FLAG_COLORS.keys() else "white"

                        self.addstr(
                            y, x, flag,
                            curses.A_BOLD | uiTools.getColor(flagColor))
                        x += len(flag)

                        if i < len(self.vals["tor/flags"]) - 1:
                            self.addstr(y, x, ", ")
                            x += 2
                else:
                    self.addstr(y, x, "none",
                                curses.A_BOLD | uiTools.getColor("cyan"))
            else:
                y = 2 if isWide else 4
                statusTime = torTools.getConn().getHeartbeat()
                statusTimeLabel = time.strftime("%H:%M %m/%d/%Y",
                                                time.localtime(statusTime))
                self.addstr(y, 0, "Tor Disconnected",
                            curses.A_BOLD | uiTools.getColor("red"))
                self.addstr(y, 16,
                            " (%s) - press r to reconnect" % statusTimeLabel)

            # Undisplayed / Line 3 Right (exit policy)
            if isWide:
                exitPolicy = self.vals["tor/exitPolicy"]

                # adds note when default exit policy is appended
                if exitPolicy == "": exitPolicy = "<default>"
                elif not exitPolicy.endswith((" *:*", " *")):
                    exitPolicy += ", <default>"

                self.addstr(2, leftWidth, "exit policy: ")
                x = leftWidth + 13

                # color codes accepts to be green, rejects to be red, and default marker to be cyan
                isSimple = len(exitPolicy) > rightWidth - 13
                policies = exitPolicy.split(", ")
                for i in range(len(policies)):
                    policy = policies[i].strip()
                    policyLabel = policy.replace("accept", "").replace(
                        "reject", "").strip() if isSimple else policy

                    policyColor = "white"
                    if policy.startswith("accept"): policyColor = "green"
                    elif policy.startswith("reject"): policyColor = "red"
                    elif policy.startswith("<default>"): policyColor = "cyan"

                    self.addstr(2, x, policyLabel,
                                curses.A_BOLD | uiTools.getColor(policyColor))
                    x += len(policyLabel)

                    if i < len(policies) - 1:
                        self.addstr(2, x, ", ")
                        x += 2
        else:
            # (Client only) Undisplayed / Line 2 Right (new identity option)
            if isWide:
                conn = torTools.getConn()
                newnymWait = conn.getNewnymWait()

                msg = "press 'n' for a new identity"
                if newnymWait > 0:
                    pluralLabel = "s" if newnymWait > 1 else ""
                    msg = "building circuits, available again in %i second%s" % (
                        newnymWait, pluralLabel)

                self.addstr(1, leftWidth, msg)

        self.valsLock.release()
示例#38
0
文件: logPanel.py 项目: refnode/arm
 def _getTitle(self, width):
   """
   Provides the label used for the panel, looking like:
     Events (ARM NOTICE - ERR, BW - filter: prepopulate):
   
   This truncates the attributes (with an ellipse) if too long, and condenses
   runlevel ranges if there's three or more in a row (for instance ARM_INFO,
   ARM_NOTICE, and ARM_WARN becomes "ARM_INFO - WARN").
   
   Arguments:
     width - width constraint the label needs to fix in
   """
   
   # usually the attributes used to make the label are decently static, so
   # provide cached results if they're unchanged
   self.valsLock.acquire()
   currentPattern = self.regexFilter.pattern if self.regexFilter else None
   isUnchanged = self._titleArgs[0] == self.loggedEvents
   isUnchanged &= self._titleArgs[1] == currentPattern
   isUnchanged &= self._titleArgs[2] == width
   if isUnchanged:
     self.valsLock.release()
     return self._titleCache
   
   eventsList = list(self.loggedEvents)
   if not eventsList:
     if not currentPattern:
       panelLabel = "Events:"
     else:
       labelPattern = uiTools.cropStr(currentPattern, width - 18)
       panelLabel = "Events (filter: %s):" % labelPattern
   else:
     # does the following with all runlevel types (tor, arm, and stem):
     # - pulls to the start of the list
     # - condenses range if there's three or more in a row (ex. "ARM_INFO - WARN")
     # - condense further if there's identical runlevel ranges for multiple
     #   types (ex. "NOTICE - ERR, ARM_NOTICE - ERR" becomes "TOR/ARM NOTICE - ERR")
     tmpRunlevels = [] # runlevels pulled from the list (just the runlevel part)
     runlevelRanges = [] # tuple of type, startLevel, endLevel for ranges to be consensed
     
     # reverses runlevels and types so they're appended in the right order
     reversedRunlevels = log.Runlevel.values()
     reversedRunlevels.reverse()
     for prefix in ("STEM_", "ARM_", ""):
       # blank ending runlevel forces the break condition to be reached at the end
       for runlevel in reversedRunlevels + [""]:
         eventType = prefix + runlevel
         if runlevel and eventType in eventsList:
           # runlevel event found, move to the tmp list
           eventsList.remove(eventType)
           tmpRunlevels.append(runlevel)
         elif tmpRunlevels:
           # adds all tmp list entries to the start of eventsList
           if len(tmpRunlevels) >= 3:
             # save condense sequential runlevels to be added later
             runlevelRanges.append((prefix, tmpRunlevels[-1], tmpRunlevels[0]))
           else:
             # adds runlevels individaully
             for tmpRunlevel in tmpRunlevels:
               eventsList.insert(0, prefix + tmpRunlevel)
           
           tmpRunlevels = []
     
     # adds runlevel ranges, condensing if there's identical ranges
     for i in range(len(runlevelRanges)):
       if runlevelRanges[i]:
         prefix, startLevel, endLevel = runlevelRanges[i]
         
         # check for matching ranges
         matches = []
         for j in range(i + 1, len(runlevelRanges)):
           if runlevelRanges[j] and runlevelRanges[j][1] == startLevel and runlevelRanges[j][2] == endLevel:
             matches.append(runlevelRanges[j])
             runlevelRanges[j] = None
         
         if matches:
           # strips underscores and replaces empty entries with "TOR"
           prefixes = [entry[0] for entry in matches] + [prefix]
           for k in range(len(prefixes)):
             if prefixes[k] == "": prefixes[k] = "TOR"
             else: prefixes[k] = prefixes[k].replace("_", "")
           
           eventsList.insert(0, "%s %s - %s" % ("/".join(prefixes), startLevel, endLevel))
         else:
           eventsList.insert(0, "%s%s - %s" % (prefix, startLevel, endLevel))
     
     # truncates to use an ellipsis if too long, for instance:
     attrLabel = ", ".join(eventsList)
     if currentPattern: attrLabel += " - filter: %s" % currentPattern
     attrLabel = uiTools.cropStr(attrLabel, width - 10, 1)
     if attrLabel: attrLabel = " (%s)" % attrLabel
     panelLabel = "Events%s:" % attrLabel
   
   # cache results and return
   self._titleCache = panelLabel
   self._titleArgs = (list(self.loggedEvents), currentPattern, width)
   self.valsLock.release()
   return panelLabel