Beispiel #1
0
    def reloadTorrc(self):
        """
    Reloads the torrc, displaying an indicator of success or failure.
    """

        try:
            torConfig.getTorrc().load()
            self._lastContentHeightArgs = None
            self.redraw(True)
            resultMsg = "torrc reloaded"
        except IOError:
            resultMsg = "failed to reload torrc"

        self._lastContentHeightArgs = None
        self.redraw(True)
        popups.showMsg(resultMsg, 1)
    def quit(self):
        """
    Terminates arm after the input is processed. Optionally if we're connected
    to a arm generated tor instance then this may check if that should be shut
    down too.
    """

        self._isDone = True

        # check if the torrc has a "ARM_SHUTDOWN" comment flag, if so then shut
        # down the instance

        isShutdownFlagPresent = False
        torrcContents = torConfig.getTorrc().getContents()

        if torrcContents:
            for line in torrcContents:
                if "# ARM_SHUTDOWN" in line:
                    isShutdownFlagPresent = True
                    break

        if isShutdownFlagPresent:
            try:
                torTools.getConn().shutdown()
            except IOError, exc:
                cli.popups.showMsg(str(exc), 3, curses.A_BOLD)
Beispiel #3
0
    def quit(self):
        """
    Terminates arm after the input is processed. Optionally if we're connected
    to a arm generated tor instance then this may check if that should be shut
    down too.
    """

        self._isDone = True

        # check if the torrc has a "ARM_SHUTDOWN" comment flag, if so then shut
        # down the instance

        isShutdownFlagPresent = False
        torrcContents = torConfig.getTorrc().getContents()

        if torrcContents:
            for line in torrcContents:
                if "# ARM_SHUTDOWN" in line:
                    isShutdownFlagPresent = True
                    break

        if isShutdownFlagPresent:
            try:
                torTools.getConn().shutdown()
            except IOError, exc:
                cli.popups.showMsg(str(exc), 3, curses.A_BOLD)
Beispiel #4
0
    def reloadTorrc(self):
        """
    Reloads the torrc, displaying an indicator of success or failure.
    """

        try:
            torConfig.getTorrc().load()
            self._lastContentHeightArgs = None
            self.redraw(True)
            resultMsg = "torrc reloaded"
        except IOError:
            resultMsg = "failed to reload torrc"

        self._lastContentHeightArgs = None
        self.redraw(True)
        popups.showMsg(resultMsg, 1)
Beispiel #5
0
    def resetListener(self, controller, eventType, _):
        """
    Reloads and displays the torrc on tor reload (sighup) events.
    """

        if eventType == State.INIT:
            # loads the torrc and provides warnings in case of validation errors
            try:
                loadedTorrc = torConfig.getTorrc()
                loadedTorrc.load(True)
                loadedTorrc.logValidationIssues()
                self.redraw(True)
            except:
                pass
        elif eventType == State.RESET:
            try:
                torConfig.getTorrc().load(True)
                self.redraw(True)
            except:
                pass
Beispiel #6
0
    def resetListener(self, controller, eventType, _):
        """
    Reloads and displays the torrc on tor reload (sighup) events.
    """

        if eventType == State.INIT:
            # loads the torrc and provides warnings in case of validation errors
            try:
                loadedTorrc = torConfig.getTorrc()
                loadedTorrc.load(True)
                loadedTorrc.logValidationIssues()
                self.redraw(True)
            except:
                pass
        elif eventType == State.RESET:
            try:
                torConfig.getTorrc().load(True)
                self.redraw(True)
            except:
                pass
def connResetListener(conn, eventType):
    """
  Pauses connection resolution when tor's shut down, and resumes with the new
  pid if started again.
  """

    if connections.isResolverAlive("tor"):
        resolver = connections.getResolver("tor")
        resolver.setPaused(eventType == torTools.State.CLOSED)

        if eventType in (torTools.State.INIT, torTools.State.RESET):
            # Reload the torrc contents. If the torrc panel is present then it will
            # do this instead since it wants to do validation and redraw _after_ the
            # new contents are loaded.

            if getController().getPanel("torrc") == None:
                torConfig.getTorrc().load(True)

            torPid = conn.getMyPid()

            if torPid and torPid != resolver.getPid():
                resolver.setPid(torPid)
Beispiel #8
0
def connResetListener(controller, eventType, _):
    """
  Pauses connection resolution when tor's shut down, and resumes with the new
  pid if started again.
  """

    if connections.isResolverAlive("tor"):
        resolver = connections.getResolver("tor")
        resolver.setPaused(eventType == State.CLOSED)

        if eventType in (State.INIT, State.RESET):
            # Reload the torrc contents. If the torrc panel is present then it will
            # do this instead since it wants to do validation and redraw _after_ the
            # new contents are loaded.

            if getController().getPanel("torrc") == None:
                torConfig.getTorrc().load(True)

            torPid = controller.get_info("process/pid", None)

            if torPid and torPid != resolver.getPid():
                resolver.setPid(torPid)
Beispiel #9
0
 def quit(self):
   """
   Terminates arm after the input is processed. Optionally if we're connected
   to a arm generated tor instance then this may check if that should be shut
   down too.
   """
   
   self._isDone = True
   
   # check if the torrc has a "ARM_SHUTDOWN" comment flag, if so then shut
   # down the instance
   
   isShutdownFlagPresent = False
   torrcContents = torConfig.getTorrc().getContents()
   
   if torrcContents:
     for line in torrcContents:
       if "# ARM_SHUTDOWN" in line:
         isShutdownFlagPresent = True
         break
   
   if isShutdownFlagPresent:
     try: torTools.getConn().shutdown()
     except IOError, exc: cli.popups.showMsg(str(exc), 3, curses.A_BOLD)
   
   if CONFIG["features.offerTorShutdownOnQuit"]:
     conn = torTools.getConn()
     
     if self.getTorManager().isManaged(conn):
       while True:
         msg = "Shut down the Tor instance arm started (y/n)?"
         confirmationKey = cli.popups.showMsg(msg, attr = curses.A_BOLD)
         
         if confirmationKey in (ord('y'), ord('Y')):
           # attempts a graceful shutdown of tor, showing the issue if
           # unsuccessful then continuing the shutdown
           try: conn.shutdown()
           except IOError, exc: cli.popups.showMsg(str(exc), 3, curses.A_BOLD)
           
           break
         elif confirmationKey in (ord('n'), ord('N')):
           break
Beispiel #10
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()
Beispiel #11
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()
Beispiel #12
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()
Beispiel #13
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()