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)
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)
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 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 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)
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)
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
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()
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()
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()
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()