Beispiel #1
0
import nyx.popups

from nyx.util import panel, tor_config, tor_controller, ui_tools

from stem.control import State
from stem.util import conf, enum, str_tools


def conf_handler(key, value):
  if key == 'features.config.file.max_lines_per_entry':
    return max(1, value)


CONFIG = conf.config_dict('nyx', {
  'features.config.file.showScrollbars': True,
  'features.config.file.max_lines_per_entry': 8,
}, conf_handler)

# TODO: The nyxrc use case is incomplete. There should be equivilant reloading
# and validation capabilities to the torrc.
Config = enum.Enum('TORRC', 'NYXRC')  # configuration file types that can be displayed


class TorrcPanel(panel.Panel):
  """
  Renders the current torrc or nyxrc with syntax highlighting in a scrollable
  area.
  """

  def __init__(self, stdscr, config_type):
    panel.Panel.__init__(self, stdscr, 'torrc', 0)
Beispiel #2
0
import time
import threading

from stem.util import conf, log, proc, str_tools, system

PROCESS_NAME_CACHE = {}  # mapping of pids to their process names
RESOURCE_TRACKERS = {}  # mapping of pids to their resource tracker instances

# Runtimes for system calls, used to estimate cpu usage. Entries are tuples of
# the form:
# (time called, runtime)
RUNTIMES = []
SAMPLING_PERIOD = 5  # time of the sampling period

CONFIG = conf.config_dict("arm", {
    "queries.resourceUsage.rate": 5,
})

# TODO: This was a bit of a hack, and one that won't work now that we lack our
# call() method to populate RUNTIMES.


def getSysCpuUsage():
    """
  Provides an estimate of the cpu usage for system calls made through this
  module, based on a sampling period of five seconds. The os.times() function,
  unfortunately, doesn't seem to take popen calls into account. This returns a
  float representing the percentage used.
  """

    currentTime = time.time()
Beispiel #3
0
from stem.util import conf, enum, log, str_tools, system

def conf_handler(key, value):
  if key == "config.important":
    # stores lowercase entries to drop case sensitivity
    return [entry.lower() for entry in value]

CONFIG = conf.config_dict("arm", {
  "features.torrc.validate": True,
  "config.important": [],
  "torrc.alias": {},
  "torrc.label.size.b": [],
  "torrc.label.size.kb": [],
  "torrc.label.size.mb": [],
  "torrc.label.size.gb": [],
  "torrc.label.size.tb": [],
  "torrc.label.time.sec": [],
  "torrc.label.time.min": [],
  "torrc.label.time.hour": [],
  "torrc.label.time.day": [],
  "torrc.label.time.week": [],
}, conf_handler)

def general_conf_handler(config, key):
  value = config.get(key)
  
  if key.startswith("config.summary."):
    # we'll look for summary keys with a lowercase config name
    CONFIG[key.lower()] = value
  elif key.startswith("torrc.label.") and value:
Beispiel #4
0
import nyx.panel
import nyx.popups
import nyx.tracker

from stem.util import conf, log
from nyx import msg, tor_controller

from nyx.curses import RED, GREEN, YELLOW, CYAN, WHITE, BOLD, HIGHLIGHT

MIN_DUAL_COL_WIDTH = 141  # minimum width where we'll show two columns
SHOW_FD_THRESHOLD = 60  # show file descriptor usage if usage is over this percentage
UPDATE_RATE = 5  # rate in seconds at which we refresh

CONFIG = conf.config_dict('nyx', {
    'attr.flag_colors': {},
    'attr.version_status_colors': {},
    'tor.chroot': '',
})


class HeaderPanel(nyx.panel.DaemonPanel):
    """
  Top area containing tor settings and system information.
  """
    def __init__(self):
        nyx.panel.DaemonPanel.__init__(self, UPDATE_RATE)
        self._vals = Sampling.create()

        self._last_width = nyx.curses.screen_size().width
        self._reported_inactive = False
Beispiel #5
0
import cli.controller

from cli.graphing import graphPanel
from util import torTools, uiTools

from stem.control import State
from stem.util import conf, log, str_tools, system

def conf_handler(key, value):
  if key == "features.graph.bw.accounting.rate":
    return max(1, value)

CONFIG = conf.config_dict("arm", {
  "features.graph.bw.transferInBytes": False,
  "features.graph.bw.accounting.show": True,
  "features.graph.bw.accounting.rate": 10,
  "features.graph.bw.accounting.isTimeLong": False,
}, conf_handler)

DL_COLOR, UL_COLOR = "green", "cyan"

# width at which panel abandons placing optional stats (avg and total) with
# header in favor of replacing the x-axis label
COLLAPSE_WIDTH = 135

# valid keys for the accountingInfo mapping
ACCOUNTING_ARGS = ("status", "resetTime", "read", "written", "readLimit", "writtenLimit")

PREPOPULATE_SUCCESS_MSG = "Read the last day of bandwidth history from the state file"
PREPOPULATE_FAILURE_MSG = "Unable to prepopulate bandwidth information (%s)"
Beispiel #6
0
  :var float timestamp: unix timestamp for when this information was fetched
"""

import collections
import os
import time
import threading

import stem.control

from nyx import log, tor_controller
from stem.util import conf, connection, enum, proc, str_tools, system

CONFIG = conf.config_dict('nyx', {
  'queries.connections.rate': 5,
  'queries.resources.rate': 5,
  'queries.port_usage.rate': 5,
})

CONNECTION_TRACKER = None
RESOURCE_TRACKER = None
PORT_USAGE_TRACKER = None
CONSENSUS_TRACKER = None

CustomResolver = enum.Enum(
  ('INFERENCE', 'by inference'),
)

# Extending stem's Connection tuple with attributes for the uptime of the
# connection.
Beispiel #7
0
def conf_handler(key, value):
    if key == "features.redrawRate":
        return max(1, value)
    elif key == "features.refreshRate":
        return max(0, value)


CONFIG = conf.config_dict(
    "arm", {
        "startup.events": "N3",
        "startup.dataDirectory": "~/.arm",
        "startup.blindModeEnabled": False,
        "features.panels.show.graph": True,
        "features.panels.show.log": True,
        "features.panels.show.connection": True,
        "features.panels.show.config": True,
        "features.panels.show.torrc": True,
        "features.redrawRate": 5,
        "features.refreshRate": 5,
        "features.confirmQuit": True,
        "features.graph.type": 1,
        "features.graph.bw.prepopulate": True,
    }, conf_handler)

GraphStat = enum.Enum("BANDWIDTH", "CONNECTIONS", "SYSTEM_RESOURCES")

# maps 'features.graph.type' config values to the initial types
GRAPH_INIT_STATS = {
    1: GraphStat.BANDWIDTH,
    2: GraphStat.CONNECTIONS,
    3: GraphStat.SYSTEM_RESOURCES
Beispiel #8
0
# listing types
Listing = enum.Enum(("IP_ADDRESS", "IP Address"), "HOSTNAME", "FINGERPRINT", "NICKNAME")

def conf_handler(key, value):
  if key == "features.connection.listingType":
    return conf.parse_enum(key, value, Listing)
  elif key == "features.connection.refreshRate":
    return max(1, value)
  elif key == "features.connection.order":
    return conf.parse_enum_csv(key, value[0], entries.SortAttr, 3)

CONFIG = conf.config_dict("arm", {
  "features.connection.resolveApps": True,
  "features.connection.listingType": Listing.IP_ADDRESS,
  "features.connection.order": [
    entries.SortAttr.CATEGORY,
    entries.SortAttr.LISTING,
    entries.SortAttr.UPTIME],
  "features.connection.refreshRate": 5,
  "features.connection.showIps": True,
}, conf_handler)

class ConnectionPanel(panel.Panel, threading.Thread):
  """
  Listing of connections tor is making, with information correlated against
  the current consensus and other data sources.
  """
  
  def __init__(self, stdscr):
    panel.Panel.__init__(self, stdscr, "connections", 0)
    threading.Thread.__init__(self)
    self.setDaemon(True)
Beispiel #9
0
    elif key == 'features.graph.bound':
        if value not in Bounds:
            log.warn("'%s' isn't a valid graph bounds, options are: %s" %
                     (value, ', '.join(Bounds)))
            return CONFIG['features.graph.bound']  # keep the default


CONFIG = conf.config_dict(
    'nyx',
    {
        'attr.hibernate_color': {},
        'attr.graph.title': {},
        'attr.graph.header.primary': {},
        'attr.graph.header.secondary': {},
        'features.graph.height': 7,
        'features.graph.type': GraphStat.BANDWIDTH,
        'features.graph.interval': Interval.EACH_SECOND,
        'features.graph.bound': Bounds.LOCAL_MAX,
        'features.graph.max_width':
        300,  # we need some sort of max size so we know how much graph data to retain
        'features.panels.show.connection': True,
        'features.graph.bw.transferInBytes': False,
        'features.graph.bw.accounting.show': True,
    },
    conf_handler)


class GraphData(object):
    """
  Graphable statistical information.

  :var int latest_value: last value we recorded
Beispiel #10
0
  if key == 'features.log.maxLinesPerEntry':
    return max(1, value)
  elif key == 'features.log.prepopulateReadLimit':
    return max(0, value)
  elif key == 'features.log.maxRefreshRate':
    return max(10, value)
  elif key == 'cache.log_panel.size':
    return max(1000, value)


CONFIG = conf.config_dict('nyx', {
  'features.logFile': '',
  'features.log.showDuplicateEntries': False,
  'features.log.maxLinesPerEntry': 6,
  'features.log.prepopulate': True,
  'features.log.prepopulateReadLimit': 5000,
  'features.log.maxRefreshRate': 300,
  'features.log.regex': [],
  'cache.log_panel.size': 1000,
  'msg.misc.event_types': '',
  'attr.log_color': {},
}, conf_handler)

# The height of the drawn content is estimated based on the last time we redrew
# the panel. It's chiefly used for scrolling and the bar indicating its
# position. Letting the estimate be too inaccurate results in a display bug, so
# redraws the display if it's off by this threshold.

CONTENT_HEIGHT_REDRAW_THRESHOLD = 3

# Log buffer so we start collecting stem/nyx events when imported. This is used
# to make our LogPanel when curses initializes.
Beispiel #11
0
def conf_handler(key, value):
  if key == "features.graph.height":
    return max(MIN_GRAPH_HEIGHT, value)
  elif key == "features.graph.maxWidth":
    return max(1, value)
  elif key == "features.graph.interval":
    return max(0, min(len(UPDATE_INTERVALS) - 1, value))
  elif key == "features.graph.bound":
    return max(0, min(2, value))

# used for setting defaults when initializing GraphStats and GraphPanel instances
CONFIG = conf.config_dict("arm", {
  "features.graph.height": 7,
  "features.graph.interval": 0,
  "features.graph.bound": 1,
  "features.graph.maxWidth": 150,
  "features.graph.showIntermediateBounds": True,
}, conf_handler)

class GraphStats:
  """
  Module that's expected to update dynamically and provide attributes to be
  graphed. Up to two graphs (a 'primary' and 'secondary') can be displayed at a
  time and timescale parameters use the labels defined in UPDATE_INTERVALS.
  """
  
  def __init__(self):
    """
    Initializes parameters needed to present a graph.
    """
Beispiel #12
0
# color support is unavailable
COLOR_ATTR_INITIALIZED = False
COLOR_ATTR = dict([(color, 0) for color in COLOR_LIST])

Ending = enum.Enum("ELLIPSE", "HYPHEN")
SCROLL_KEYS = (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE, curses.KEY_NPAGE, curses.KEY_HOME, curses.KEY_END)

def conf_handler(key, value):
  if key == "features.colorOverride" and value != "none":
    try: setColorOverride(value)
    except ValueError, exc:
      log.notice(exc)

CONFIG = conf.config_dict("arm", {
  "features.colorOverride": "none",
  "features.colorInterface": True,
  "features.acsSupport": True,
  "features.printUnicode": True,
}, conf_handler)

# Flag indicating if unicode is supported by curses. If None then this has yet
# to be determined.
IS_UNICODE_SUPPORTED = None

def demoGlyphs():
  """
  Displays all ACS options with their corresponding representation. These are
  undocumented in the pydocs. For more information see the following man page:
  http://www.mkssoftware.com/docs/man5/terminfo.5.asp
  """
  
  try: curses.wrapper(_showGlyphs)
Beispiel #13
0
        return max(0, value)
    elif key == "features.config.state.colWidth.option":
        return max(5, value)
    elif key == "features.config.state.colWidth.value":
        return max(5, value)
    elif key == "features.config.order":
        return conf.parse_enum_csv(key, value[0], Field, 3)


CONFIG = conf.config_dict(
    "arm",
    {
        "features.config.order": [Field.MAN_ENTRY, Field.OPTION, Field.IS_DEFAULT],
        "features.config.selectionDetails.height": 6,
        "features.config.prepopulateEditValues": True,
        "features.config.state.showPrivateOptions": False,
        "features.config.state.showVirtualOptions": False,
        "features.config.state.colWidth.option": 25,
        "features.config.state.colWidth.value": 15,
    },
    conf_handler,
)


def getFieldFromLabel(fieldLabel):
    """
  Converts field labels back to their enumeration, raising a ValueError if it
  doesn't exist.
  """

    for entryEnum in FIELD_ATTR:
Beispiel #14
0
    Category.CONTROL: "red"
}

# static data for listing format
# <src>  -->  <dst>  <etc><padding>
LABEL_FORMAT = "%s  -->  %s  %s%s"
LABEL_MIN_PADDING = 2  # min space between listing label and following data

# sort value for scrubbed ip addresses
SCRUBBED_IP_VAL = 255**4

CONFIG = conf.config_dict(
    "arm", {
        "features.connection.markInitialConnections": True,
        "features.connection.showIps": True,
        "features.connection.showExitPort": True,
        "features.connection.showColumn.fingerprint": True,
        "features.connection.showColumn.nickname": True,
        "features.connection.showColumn.destination": True,
        "features.connection.showColumn.expandedIp": True,
    })


class Endpoint:
    """
  Collection of attributes associated with a connection endpoint. This is a
  thin wrapper for torUtil functions, making use of its caching for
  performance.
  """
    def __init__(self, ipAddr, port):
        self.ipAddr = ipAddr
        self.port = port
Beispiel #15
0
DNS_ERROR_CODES = ("1(FORMERR)", "2(SERVFAIL)", "3(NXDOMAIN)", "4(NOTIMP)", "5(REFUSED)", "6(YXDOMAIN)",
                   "7(YXRRSET)", "8(NXRRSET)", "9(NOTAUTH)", "10(NOTZONE)", "16(BADVERS)")

def conf_handler(key, value):
  if key == "queries.hostnames.poolSize":
    return max(1, value)
  elif key == "cache.hostnames.size":
    return max(100, value)
  elif key == "cache.hostnames.trimSize":
    return max(10, value)
  elif key == "cache.hostnames.trimSize":
    return min(value, CONFIG["cache.hostnames.size"] / 2)

CONFIG = conf.config_dict("arm", {
  "queries.hostnames.poolSize": 5,
  "queries.hostnames.useSocketModule": False,
  "cache.hostnames.size": 700000,
  "cache.hostnames.trimSize": 200000,
}, conf_handler)

def start():
  """
  Primes the service to start resolving addresses. Calling this explicitly is
  not necessary since resolving any address will start the service if it isn't
  already running.
  """
  
  global RESOLVER
  RESOLVER_LOCK.acquire()
  if not isRunning(): RESOLVER = _Resolver()
  RESOLVER_LOCK.release()
Beispiel #16
0
import popups

from util import panel, torConfig, torTools, uiTools

from stem.control import State
from stem.util import conf, enum


def conf_handler(key, value):
    if key == "features.config.file.maxLinesPerEntry":
        return max(1, value)


CONFIG = conf.config_dict(
    "arm", {
        "features.config.file.showScrollbars": True,
        "features.config.file.maxLinesPerEntry": 8,
    }, conf_handler)

# TODO: The armrc use case is incomplete. There should be equivilant reloading
# and validation capabilities to the torrc.
Config = enum.Enum("TORRC",
                   "ARMRC")  # configuration file types that can be displayed


class TorrcPanel(panel.Panel):
    """
  Renders the current torrc or armrc with syntax highlighting in a scrollable
  area.
  """
    def __init__(self, stdscr, configType):
Beispiel #17
0
NYX_CONTROLLER = None


def conf_handler(key, value):
  if key == 'features.redrawRate':
    return max(1, value)
  elif key == 'features.refreshRate':
    return max(0, value)


CONFIG = conf.config_dict('nyx', {
  'features.acsSupport': True,
  'features.panels.show.graph': True,
  'features.panels.show.log': True,
  'features.panels.show.connection': True,
  'features.panels.show.config': True,
  'features.panels.show.torrc': True,
  'features.redrawRate': 5,
  'features.refreshRate': 5,
  'features.confirmQuit': True,
  'start_time': 0,
}, conf_handler)


def get_controller():
  """
  Provides the nyx controller instance.
  """

  return NYX_CONTROLLER

Beispiel #18
0

def conf_handler(key, value):
    if key == 'features.redrawRate':
        return max(1, value)
    elif key == 'features.refreshRate':
        return max(0, value)


CONFIG = conf.config_dict(
    'nyx', {
        'features.acsSupport': True,
        'features.panels.show.graph': True,
        'features.panels.show.log': True,
        'features.panels.show.connection': True,
        'features.panels.show.config': True,
        'features.panels.show.torrc': True,
        'features.redrawRate': 5,
        'features.refreshRate': 5,
        'features.confirmQuit': True,
        'start_time': 0,
    }, conf_handler)


def get_controller():
    """
  Provides the nyx controller instance.
  """

    return NYX_CONTROLLER
Beispiel #19
0
Generates the menu for arm, binding options with their related actions.
"""

import functools

import cli.popups
import cli.controller
import cli.menu.item
import cli.graphing.graphPanel

from util import connections, torTools, uiTools

from stem.util import conf, str_tools

CONFIG = conf.config_dict("arm", {
  "features.log.showDuplicateEntries": False,
})

def makeMenu():
  """
  Constructs the base menu and all of its contents.
  """
  
  baseMenu = cli.menu.item.Submenu("")
  baseMenu.add(makeActionsMenu())
  baseMenu.add(makeViewMenu())
  
  control = cli.controller.getController()
  
  for pagePanel in control.getDisplayPanels(includeSticky = False):
    if pagePanel.getName() == "graph":
Beispiel #20
0
def conf_handler(key, value):
    if key == "queries.hostnames.poolSize":
        return max(1, value)
    elif key == "cache.hostnames.size":
        return max(100, value)
    elif key == "cache.hostnames.trimSize":
        return max(10, value)
    elif key == "cache.hostnames.trimSize":
        return min(value, CONFIG["cache.hostnames.size"] / 2)


CONFIG = conf.config_dict(
    "arm", {
        "queries.hostnames.poolSize": 5,
        "queries.hostnames.useSocketModule": False,
        "cache.hostnames.size": 700000,
        "cache.hostnames.trimSize": 200000,
    }, conf_handler)


def start():
    """
  Primes the service to start resolving addresses. Calling this explicitly is
  not necessary since resolving any address will start the service if it isn't
  already running.
  """

    global RESOLVER
    RESOLVER_LOCK.acquire()
    if not isRunning(): RESOLVER = _Resolver()
Beispiel #21
0
import collections
import os
import time
import threading

import nyx
import stem.control
import stem.descriptor.router_status_entry
import stem.util.log

from nyx import tor_controller
from stem.util import conf, connection, enum, proc, str_tools, system

CONFIG = conf.config_dict('nyx', {
    'connection_rate': 5,
    'resource_rate': 5,
    'port_usage_rate': 5,
})

UNABLE_TO_USE_ANY_RESOLVER_MSG = """
We were unable to use any of your system's resolvers to get tor's connections.
This is fine, but means that the connections page will be empty. This is
usually permissions related so if you would like to fix this then run nyx with
the same user as tor (ie, "sudo -u <tor user> nyx").
""".strip()

CONNECTION_TRACKER = None
RESOURCE_TRACKER = None
PORT_USAGE_TRACKER = None
CONSENSUS_TRACKER = None
Beispiel #22
0
import popups

from util import panel, torConfig, torTools, uiTools

from stem.control import State
from stem.util import conf, enum


def conf_handler(key, value):
    if key == "features.config.file.maxLinesPerEntry":
        return max(1, value)


CONFIG = conf.config_dict(
    "arm", {"features.config.file.showScrollbars": True, "features.config.file.maxLinesPerEntry": 8}, conf_handler
)

# TODO: The armrc use case is incomplete. There should be equivilant reloading
# and validation capabilities to the torrc.
Config = enum.Enum("TORRC", "ARMRC")  # configuration file types that can be displayed


class TorrcPanel(panel.Panel):
    """
  Renders the current torrc or armrc with syntax highlighting in a scrollable
  area.
  """

    def __init__(self, stdscr, configType):
        panel.Panel.__init__(self, stdscr, "torrc", 0)
Beispiel #23
0
  'config',
  'connection',
  'graph',
  'header',
  'log',
  'torrc',
]


def conf_handler(key, value):
  if key == 'features.torrc.maxLineWrap':
    return max(1, value)


CONFIG = conf.config_dict('nyx', {
  'features.maxLineWrap': 8,
}, conf_handler)

HALT_ACTIVITY = False  # prevents curses redraws if set


class KeyHandler(collections.namedtuple('Help', ['key', 'description', 'current'])):
  """
  Action that can be taken via a given keybinding.

  :var str key: key the user can press
  :var str description: description of what it does
  :var str current: optional current value

  :param str key: key the user can press
  :param str description: description of what it does
Beispiel #24
0
SCROLL_KEYS = (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE,
               curses.KEY_NPAGE, curses.KEY_HOME, curses.KEY_END)


def conf_handler(key, value):
    if key == "features.colorOverride" and value != "none":
        try:
            setColorOverride(value)
        except ValueError, exc:
            log.notice(exc)


CONFIG = conf.config_dict(
    "arm", {
        "features.colorOverride": "none",
        "features.colorInterface": True,
        "features.acsSupport": True,
        "features.printUnicode": True,
    }, conf_handler)

# Flag indicating if unicode is supported by curses. If None then this has yet
# to be determined.
IS_UNICODE_SUPPORTED = None


def demoGlyphs():
    """
  Displays all ACS options with their corresponding representation. These are
  undocumented in the pydocs. For more information see the following man page:
  http://www.mkssoftware.com/docs/man5/terminfo.5.asp
  """
Beispiel #25
0
from __future__ import with_statement

import re
import os

from stem.util import conf, system

# if ran directly then run over everything one level up
DEFAULT_TARGET = os.path.sep.join(__file__.split(os.path.sep)[:-1])

# mapping of files to the issues that should be ignored
PYFLAKES_IGNORE = None

CONFIG = conf.config_dict("test", {
  "pyflakes.ignore": []
})


def pep8_issues(base_path = DEFAULT_TARGET):
  """
  Checks for stylistic issues that are an issue according to the parts of PEP8
  we conform to.

  :param str base_path: directory to be iterated over

  :returns: dict of the form ``path => [(line_number, message)...]``
  """

  # The pep8 command give output of the form...
  #
Beispiel #26
0
from __future__ import with_statement

import re
import os

from stem.util import conf, system

# if ran directly then run over everything one level up
DEFAULT_TARGET = os.path.sep.join(__file__.split(os.path.sep)[:-1])

# mapping of files to the issues that should be ignored
PYFLAKES_IGNORE = None

CONFIG = conf.config_dict("test", {
  "pyflakes.ignore": [],
  "integ.test_directory": "./test/data",
})


def pep8_issues(base_path = DEFAULT_TARGET):
  """
  Checks for stylistic issues that are an issue according to the parts of PEP8
  we conform to.

  :param str base_path: directory to be iterated over

  :returns: dict of the form ``path => [(line_number, message)...]``
  """

  # The pep8 command give output of the form...
  #
Beispiel #27
0
def conf_handler(key, value):
  if key == 'torrc.important':
    # stores lowercase entries to drop case sensitivity
    return [entry.lower() for entry in value]


CONFIG = conf.config_dict('nyx', {
  'features.torrc.validate': True,
  'torrc.important': [],
  'torrc.alias': {},
  'torrc.units.size.b': [],
  'torrc.units.size.kb': [],
  'torrc.units.size.mb': [],
  'torrc.units.size.gb': [],
  'torrc.units.size.tb': [],
  'torrc.units.time.sec': [],
  'torrc.units.time.min': [],
  'torrc.units.time.hour': [],
  'torrc.units.time.day': [],
  'torrc.units.time.week': [],
  'startup.data_directory': '~/.nyx',
  'features.config.descriptions.enabled': True,
  'features.config.descriptions.persist': True,
  'tor.chroot': '',
}, conf_handler)


def general_conf_handler(config, key):
  value = config.get(key)

  if key.startswith('torrc.summary.'):
Beispiel #28
0
Generates the menu for arm, binding options with their related actions.
"""

import functools

import cli.popups
import cli.controller
import cli.menu.item
import cli.graphing.graphPanel

from util import connections, torTools, uiTools

from stem.util import conf, str_tools

CONFIG = conf.config_dict("arm", {
    "features.log.showDuplicateEntries": False,
})


def makeMenu():
    """
  Constructs the base menu and all of its contents.
  """

    baseMenu = cli.menu.item.Submenu("")
    baseMenu.add(makeActionsMenu())
    baseMenu.add(makeViewMenu())

    control = cli.controller.getController()

    for pagePanel in control.getDisplayPanels(includeSticky=False):
Beispiel #29
0
  'circuit',
  'fingerprint',
  'nickname',
  'locale',
])


def conf_handler(key, value):
  if key == 'features.connection.order':
    return conf.parse_enum_csv(key, value[0], SortAttr, 3)


CONFIG = conf.config_dict('nyx', {
  'attr.connection.category_color': {},
  'attr.connection.sort_color': {},
  'features.connection.resolveApps': True,
  'features.connection.order': [SortAttr.CATEGORY, SortAttr.IP_ADDRESS, SortAttr.UPTIME],
  'features.connection.showIps': True,
}, conf_handler)


class Entry(object):
  @staticmethod
  @lru_cache()
  def from_connection(connection):
    return ConnectionEntry(connection)

  @staticmethod
  @lru_cache()
  def from_circuit(circuit):
    return CircuitEntry(circuit)
Beispiel #30
0
        log.notice(msg)
    else:
      try:
        # range of ports (inclusive)
        minPort = int(portEntry[:divIndex])
        maxPort = int(portEntry[divIndex + 1:])
        if minPort > maxPort: raise ValueError()
        
        for port in range(minPort, maxPort + 1):
          PORT_USAGE[str(port)] = value
      except ValueError:
        msg = "Unable to parse port range for entry: %s" % key
        log.notice(msg)

CONFIG = conf.config_dict("arm", {
  "queries.connections.minRate": 5,
}, conf_handler)

PORT_USAGE = {}

def isValidIpAddress(ipStr):
  """
  Returns true if input is a valid IPv4 address, false otherwise.
  """
  
  # checks if theres four period separated values
  if not ipStr.count(".") == 3: return False
  
  # checks that each value in the octet are decimal values between 0-255
  for ipComp in ipStr.split("."):
    if not ipComp.isdigit() or int(ipComp) < 0 or int(ipComp) > 255:
Beispiel #31
0
import nyx.panel.connection
import nyx.panel.graph
import nyx.panel.log
import nyx.panel.torrc
import nyx.controller
import nyx.tracker

import stem
import stem.util.connection

from nyx import tor_controller
from nyx.curses import RED, WHITE, NORMAL, BOLD, UNDERLINE
from stem.util import conf, str_tools

CONFIG = conf.config_dict('nyx', {
  'features.log.showDuplicateEntries': False,
})


def make_menu():
  """
  Constructs the base menu and all of its contents.
  """

  base_menu = Submenu('')
  base_menu.add(make_actions_menu())
  base_menu.add(make_view_menu())

  control = nyx.controller.get_controller()

  for page_panel in control.get_display_panels():
Beispiel #32
0
    if value not in Interval:
      log.warn("'%s' isn't a valid graphing interval, options are: %s" % (value, ', '.join(Interval)))
      return CONFIG['features.graph.interval']  # keep the default
  elif key == 'features.graph.bound':
    if value not in Bounds:
      log.warn("'%s' isn't a valid graph bounds, options are: %s" % (value, ', '.join(Bounds)))
      return CONFIG['features.graph.bound']  # keep the default


CONFIG = conf.config_dict('nyx', {
  'attr.hibernate_color': {},
  'attr.graph.title': {},
  'attr.graph.header.primary': {},
  'attr.graph.header.secondary': {},
  'features.graph.height': 7,
  'features.graph.type': GraphStat.BANDWIDTH,
  'features.graph.interval': Interval.EACH_SECOND,
  'features.graph.bound': Bounds.LOCAL_MAX,
  'features.graph.max_width': 300,  # we need some sort of max size so we know how much graph data to retain
  'features.panels.show.connection': True,
  'features.graph.bw.transferInBytes': False,
  'features.graph.bw.accounting.show': True,
}, conf_handler)


class GraphData(object):
  """
  Graphable statistical information.

  :var int latest_value: last value we recorded
  :var int total: sum of all values we've recorded
  :var int tick: number of events we've processed
Beispiel #33
0
    'nickname',
    'locale',
])


def conf_handler(key, value):
    if key == 'features.connection.order':
        return conf.parse_enum_csv(key, value[0], SortAttr, 3)


CONFIG = conf.config_dict(
    'nyx', {
        'attr.connection.category_color': {},
        'attr.connection.sort_color': {},
        'features.connection.resolveApps':
        True,
        'features.connection.order':
        [SortAttr.CATEGORY, SortAttr.IP_ADDRESS, SortAttr.UPTIME],
        'features.connection.showIps':
        True,
    }, conf_handler)


class Entry(object):
    @staticmethod
    @lru_cache()
    def from_connection(connection):
        return ConnectionEntry(connection)

    @staticmethod
    @lru_cache()
Beispiel #34
0
import stem

import nyx.controller
import nyx.popups

from stem.control import Listener
from stem.util import conf, log, proc, str_tools, system
from nyx.util import msg, tor_controller, panel, tracker

MIN_DUAL_COL_WIDTH = 141  # minimum width where we'll show two columns
SHOW_FD_THRESHOLD = 60  # show file descriptor usage if usage is over this percentage
UPDATE_RATE = 5  # rate in seconds at which we refresh

CONFIG = conf.config_dict('nyx', {
  'attr.flag_colors': {},
  'attr.version_status_colors': {},
})


class HeaderPanel(panel.Panel, threading.Thread):
  """
  Top area containing tor settings and system information.
  """

  def __init__(self, stdscr, start_time):
    panel.Panel.__init__(self, stdscr, 'header', 0)
    threading.Thread.__init__(self)
    self.setDaemon(True)

    self._vals = get_sampling()
Beispiel #35
0
DETAILS_HEIGHT = 8
NAME_WIDTH = 25
VALUE_WIDTH = 15


def conf_handler(key, value):
    if key == 'features.config.order':
        return conf.parse_enum_csv(key, value[0], SortAttr, 3)


CONFIG = conf.config_dict(
    'nyx', {
        'attr.config.category_color': {},
        'attr.config.sort_color': {},
        'features.config.order':
        [SortAttr.MAN_PAGE_ENTRY, SortAttr.NAME, SortAttr.IS_SET],
        'features.config.state.showPrivateOptions':
        False,
        'features.config.state.showVirtualOptions':
        False,
    }, conf_handler)


class ConfigEntry(object):
    """
  Configuration option presented in the panel.

  :var str name: name of the configuration option
  :var str value_type: type of value
  :var stem.manual.ConfigOption manual: manual information about the option
  """
Beispiel #36
0
DETAILS_HEIGHT = 8
NAME_WIDTH = 25
VALUE_WIDTH = 15


def conf_handler(key, value):
    if key == 'config_order':
        return conf.parse_enum_csv(key, value[0], SortAttr, 3)


CONFIG = conf.config_dict(
    'nyx', {
        'attr.config.category_color': {},
        'attr.config.sort_color': {},
        'config_order':
        [SortAttr.MAN_PAGE_ENTRY, SortAttr.NAME, SortAttr.IS_SET],
        'show_private_options': False,
        'show_virtual_options': False,
    }, conf_handler)


@lru_cache()
def manual(option):
    result = stem.manual.query(
        'SELECT category, usage, summary, description, position FROM torrc WHERE key=?',
        option.upper()).fetchone()

    if result:
        return ManualEntry(*result)
    else:
Beispiel #37
0
    'nickname',
    'locale',
])


def conf_handler(key, value):
    if key == 'connection_order':
        return conf.parse_enum_csv(key, value[0], SortAttr, 3)


CONFIG = conf.config_dict(
    'nyx', {
        'attr.connection.category_color': {},
        'attr.connection.sort_color': {},
        'connection_order':
        [SortAttr.CATEGORY, SortAttr.IP_ADDRESS, SortAttr.UPTIME],
        'resolve_processes':
        True,
        'show_addresses':
        True,
    }, conf_handler)


class Entry(object):
    @staticmethod
    def from_connection(connection):
        if connection not in ENTRY_CACHE:
            ENTRY_CACHE[connection] = ConnectionEntry(connection)

        ENTRY_CACHE_REFERENCED[connection] = time.time()
        return ENTRY_CACHE[connection]
Beispiel #38
0
                  Category.SOCKS: "yellow",       Category.CIRCUIT: "cyan",
                  Category.DIRECTORY: "magenta",  Category.CONTROL: "red"}

# static data for listing format
# <src>  -->  <dst>  <etc><padding>
LABEL_FORMAT = "%s  -->  %s  %s%s"
LABEL_MIN_PADDING = 2 # min space between listing label and following data

# sort value for scrubbed ip addresses
SCRUBBED_IP_VAL = 255 ** 4

CONFIG = conf.config_dict("arm", {
  "features.connection.markInitialConnections": True,
  "features.connection.showIps": True,
  "features.connection.showExitPort": True,
  "features.connection.showColumn.fingerprint": True,
  "features.connection.showColumn.nickname": True,
  "features.connection.showColumn.destination": True,
  "features.connection.showColumn.expandedIp": True,
})

class Endpoint:
  """
  Collection of attributes associated with a connection endpoint. This is a
  thin wrapper for torUtil functions, making use of its caching for
  performance.
  """
  
  def __init__(self, ipAddr, port):
    self.ipAddr = ipAddr
    self.port = port
Beispiel #39
0
from __future__ import with_statement

import re
import os

from stem.util import conf, system

# if ran directly then run over everything one level up
DEFAULT_TARGET = os.path.sep.join(__file__.split(os.path.sep)[:-1])

# mapping of files to the issues that should be ignored
PYFLAKES_IGNORE = None

CONFIG = conf.config_dict("test", {
    "pyflakes.ignore": [],
    "integ.test_directory": "./test/data",
})


def pep8_issues(base_path=DEFAULT_TARGET):
    """
  Checks for stylistic issues that are an issue according to the parts of PEP8
  we conform to.

  :param str base_path: directory to be iterated over

  :returns: dict of the form ``path => [(line_number, message)...]``
  """

    # The pep8 command give output of the form...
    #
Beispiel #40
0
from cli.graphing import graphPanel
from util import torTools, uiTools

from stem.control import State
from stem.util import conf, log, str_tools, system


def conf_handler(key, value):
    if key == "features.graph.bw.accounting.rate":
        return max(1, value)


CONFIG = conf.config_dict(
    "arm", {
        "features.graph.bw.transferInBytes": False,
        "features.graph.bw.accounting.show": True,
        "features.graph.bw.accounting.rate": 10,
        "features.graph.bw.accounting.isTimeLong": False,
    }, conf_handler)

DL_COLOR, UL_COLOR = "green", "cyan"

# width at which panel abandons placing optional stats (avg and total) with
# header in favor of replacing the x-axis label
COLLAPSE_WIDTH = 135

# valid keys for the accountingInfo mapping
ACCOUNTING_ARGS = ("status", "resetTime", "read", "written", "readLimit",
                   "writtenLimit")

PREPOPULATE_SUCCESS_MSG = "Read the last day of bandwidth history from the state file"
Beispiel #41
0
  :var float timestamp: unix timestamp for when this information was fetched
"""

import collections
import time
import threading

import stem.control

from stem.util import conf, connection, proc, str_tools, system

from nyx.util import log, tor_controller

CONFIG = conf.config_dict('nyx', {
  'queries.connections.rate': 5,
  'queries.resources.rate': 5,
  'queries.port_usage.rate': 5,
})

CONNECTION_TRACKER = None
RESOURCE_TRACKER = None
PORT_USAGE_TRACKER = None
CONSENSUS_TRACKER = None

# Extending stem's Connection tuple with attributes for the uptime of the
# connection.

Connection = collections.namedtuple('Connection', [
  'start_time',
  'is_legacy',  # boolean to indicate if the connection predated us
] + list(stem.util.connection.Connection._fields))
Beispiel #42
0
SortAttr = enum.Enum('NAME', 'VALUE', 'VALUE_TYPE', 'CATEGORY', 'USAGE', 'SUMMARY', 'DESCRIPTION', 'MAN_PAGE_ENTRY', 'IS_SET')

DETAILS_HEIGHT = 8
NAME_WIDTH = 25
VALUE_WIDTH = 15


def conf_handler(key, value):
  if key == 'features.config.order':
    return conf.parse_enum_csv(key, value[0], SortAttr, 3)


CONFIG = conf.config_dict('nyx', {
  'attr.config.category_color': {},
  'attr.config.sort_color': {},
  'features.config.order': [SortAttr.MAN_PAGE_ENTRY, SortAttr.NAME, SortAttr.IS_SET],
  'features.config.state.showPrivateOptions': False,
  'features.config.state.showVirtualOptions': False,
}, conf_handler)


class ConfigEntry(object):
  """
  Configuration option presented in the panel.

  :var str name: name of the configuration option
  :var str value_type: type of value
  :var stem.manual.ConfigOption manual: manual information about the option
  """

  def __init__(self, name, value_type, manual):
Beispiel #43
0
    if key == "features.redrawRate":
        return max(1, value)
    elif key == "features.refreshRate":
        return max(0, value)


CONFIG = conf.config_dict(
    "arm",
    {
        "startup.events": "N3",
        "startup.dataDirectory": "~/.arm",
        "startup.blindModeEnabled": False,
        "features.panels.show.graph": True,
        "features.panels.show.log": True,
        "features.panels.show.connection": True,
        "features.panels.show.config": True,
        "features.panels.show.torrc": True,
        "features.redrawRate": 5,
        "features.refreshRate": 5,
        "features.confirmQuit": True,
        "features.graph.type": 1,
        "features.graph.bw.prepopulate": True,
    },
    conf_handler,
)

GraphStat = enum.Enum("BANDWIDTH", "CONNECTIONS", "SYSTEM_RESOURCES")

# maps 'features.graph.type' config values to the initial types
GRAPH_INIT_STATS = {1: GraphStat.BANDWIDTH, 2: GraphStat.CONNECTIONS, 3: GraphStat.SYSTEM_RESOURCES}
Beispiel #44
0
    if key == "features.connection.listingType":
        return conf.parse_enum(key, value, Listing)
    elif key == "features.connection.refreshRate":
        return max(1, value)
    elif key == "features.connection.order":
        return conf.parse_enum_csv(key, value[0], entries.SortAttr, 3)


CONFIG = conf.config_dict(
    "arm", {
        "features.connection.resolveApps":
        True,
        "features.connection.listingType":
        Listing.IP_ADDRESS,
        "features.connection.order": [
            entries.SortAttr.CATEGORY, entries.SortAttr.LISTING,
            entries.SortAttr.UPTIME
        ],
        "features.connection.refreshRate":
        5,
        "features.connection.showIps":
        True,
    }, conf_handler)


class ConnectionPanel(panel.Panel, threading.Thread):
    """
  Listing of connections tor is making, with information correlated against
  the current consensus and other data sources.
  """
    def __init__(self, stdscr):
Beispiel #45
0

def conf_handler(key, value):
    if key == "config.important":
        # stores lowercase entries to drop case sensitivity
        return [entry.lower() for entry in value]


CONFIG = conf.config_dict(
    "arm", {
        "features.torrc.validate": True,
        "config.important": [],
        "torrc.alias": {},
        "torrc.label.size.b": [],
        "torrc.label.size.kb": [],
        "torrc.label.size.mb": [],
        "torrc.label.size.gb": [],
        "torrc.label.size.tb": [],
        "torrc.label.time.sec": [],
        "torrc.label.time.min": [],
        "torrc.label.time.hour": [],
        "torrc.label.time.day": [],
        "torrc.label.time.week": [],
    }, conf_handler)


def general_conf_handler(config, key):
    value = config.get(key)

    if key.startswith("config.summary."):
        # we'll look for summary keys with a lowercase config name
        CONFIG[key.lower()] = value
Beispiel #46
0
    if value not in Interval:
      log.warn("'%s' isn't a valid graphing interval, options are: %s" % (value, ', '.join(Interval)))
      return CONFIG['graph_interval']  # keep the default
  elif key == 'graph_bound':
    if value not in Bounds:
      log.warn("'%s' isn't a valid graph bounds, options are: %s" % (value, ', '.join(Bounds)))
      return CONFIG['graph_bound']  # keep the default


CONFIG = conf.config_dict('nyx', {
  'attr.hibernate_color': {},
  'attr.graph.title': {},
  'attr.graph.header.primary': {},
  'attr.graph.header.secondary': {},
  'graph_bound': Bounds.LOCAL_MAX,
  'graph_height': 7,
  'graph_interval': Interval.EACH_SECOND,
  'graph_stat': GraphStat.BANDWIDTH,
  'max_graph_width': 300,  # we need some sort of max size so we know how much graph data to retain
  'show_accounting': True,
  'show_bits': False,
  'show_connections': True,
}, conf_handler)


def _bandwidth_title_stats():
  controller = tor_controller()

  stats = []
  bw_rate = controller.get_effective_rate(None)
  bw_burst = controller.get_effective_rate(None, burst = True)
Beispiel #47
0
  'black': curses.COLOR_BLACK,
  'white': curses.COLOR_WHITE,
}

DEFAULT_COLOR_ATTR = dict([(color, 0) for color in COLOR_LIST])
COLOR_ATTR = None


def conf_handler(key, value):
  if key == 'features.color_override':
    if value not in COLOR_LIST.keys() and value != 'none':
      raise ValueError(msg('usage.unable_to_set_color_override', color = value))


CONFIG = conf.config_dict('nyx', {
  'features.color_override': 'none',
  'features.colorInterface': True,
}, conf_handler)


def is_color_supported():
  """
  Checks if curses currently supports rendering colors.

  :returns: **True** if colors can be rendered, **False** otherwise
  """

  return _color_attr() != DEFAULT_COLOR_ATTR


def get_color(color):
  """
Beispiel #48
0
from nyx.curses import GREEN, YELLOW, WHITE, NORMAL, BOLD, HIGHLIGHT
from stem.util import conf, log


def conf_handler(key, value):
  if key == 'features.log.prepopulateReadLimit':
    return max(0, value)
  elif key == 'cache.log_panel.size':
    return max(1000, value)


CONFIG = conf.config_dict('nyx', {
  'attr.log_color': {},
  'cache.log_panel.size': 1000,
  'features.logFile': '',
  'features.log.showDuplicateEntries': False,
  'features.log.prepopulate': True,
  'features.log.prepopulateReadLimit': 5000,
  'features.log.regex': [],
  'startup.events': 'NOTICE,WARN,ERR,NYX_NOTICE,NYX_WARNING,NYX_ERROR',
}, conf_handler)

UPDATE_RATE = 0.3

# The height of the drawn content is estimated based on the last time we redrew
# the panel. It's chiefly used for scrolling and the bar indicating its
# position. Letting the estimate be too inaccurate results in a display bug, so
# redraws the display if it's off by this threshold.

CONTENT_HEIGHT_REDRAW_THRESHOLD = 3

# Log buffer so we start collecting stem/nyx events when imported. This is used
Beispiel #49
0
from stem.util import conf, log


def conf_handler(key, value):
    if key == 'prepopulate_read_limit':
        return max(0, value)
    elif key == 'max_log_size':
        return max(1000, value)


CONFIG = conf.config_dict(
    'nyx', {
        'attr.log_color': {},
        'deduplicate_log': True,
        'logged_events': 'NOTICE,WARN,ERR,NYX_NOTICE,NYX_WARNING,NYX_ERROR',
        'logging_filter': [],
        'max_log_size': 1000,
        'prepopulate_log': True,
        'prepopulate_read_limit': 5000,
        'write_logs_to': '',
    }, conf_handler)

# Users may understanably mix up 'WARN/WARNING' and 'ERR/ERROR' in their --log
# argument or config, so fixing those.

EVENT_ALIASES = {
    'WARNING': 'WARN',
    'ERROR': 'ERR',
    'NYX_WARN': 'NYX_WARNING',
    'NYX_ERR': 'NYX_ERROR',
}
Beispiel #50
0
import time
import threading

from stem.util import conf, log, proc, str_tools, system

PROCESS_NAME_CACHE = {} # mapping of pids to their process names
RESOURCE_TRACKERS = {}  # mapping of pids to their resource tracker instances

# Runtimes for system calls, used to estimate cpu usage. Entries are tuples of
# the form:
# (time called, runtime)
RUNTIMES = []
SAMPLING_PERIOD = 5 # time of the sampling period

CONFIG = conf.config_dict("arm", {
  "queries.resourceUsage.rate": 5,
})

# TODO: This was a bit of a hack, and one that won't work now that we lack our
# call() method to populate RUNTIMES.

def getSysCpuUsage():
  """
  Provides an estimate of the cpu usage for system calls made through this
  module, based on a sampling period of five seconds. The os.times() function,
  unfortunately, doesn't seem to take popen calls into account. This returns a
  float representing the percentage used.
  """
  
  currentTime = time.time()
  
Beispiel #51
0
from nyx.curses import GREEN, YELLOW, WHITE, NORMAL, BOLD, HIGHLIGHT
from stem.util import conf, log


def conf_handler(key, value):
  if key == 'features.log.prepopulateReadLimit':
    return max(0, value)
  elif key == 'cache.log_panel.size':
    return max(1000, value)


CONFIG = conf.config_dict('nyx', {
  'attr.log_color': {},
  'cache.log_panel.size': 1000,
  'features.logFile': '',
  'features.log.showDuplicateEntries': False,
  'features.log.prepopulate': True,
  'features.log.prepopulateReadLimit': 5000,
  'features.log.regex': [],
  'startup.events': 'N3',
}, conf_handler)

UPDATE_RATE = 0.3

# The height of the drawn content is estimated based on the last time we redrew
# the panel. It's chiefly used for scrolling and the bar indicating its
# position. Letting the estimate be too inaccurate results in a display bug, so
# redraws the display if it's off by this threshold.

CONTENT_HEIGHT_REDRAW_THRESHOLD = 3

# Log buffer so we start collecting stem/nyx events when imported. This is used
Beispiel #52
0
def conf_handler(key, value):
  if key == "features.config.selectionDetails.height":
    return max(0, value)
  elif key == "features.config.state.colWidth.option":
    return max(5, value)
  elif key == "features.config.state.colWidth.value":
    return max(5, value)
  elif key == "features.config.order":
    return conf.parse_enum_csv(key, value[0], Field, 3)

CONFIG = conf.config_dict("arm", {
  "features.config.order": [Field.MAN_ENTRY, Field.OPTION, Field.IS_DEFAULT],
  "features.config.selectionDetails.height": 6,
  "features.config.prepopulateEditValues": True,
  "features.config.state.showPrivateOptions": False,
  "features.config.state.showVirtualOptions": False,
  "features.config.state.colWidth.option": 25,
  "features.config.state.colWidth.value": 15,
}, conf_handler)

def getFieldFromLabel(fieldLabel):
  """
  Converts field labels back to their enumeration, raising a ValueError if it
  doesn't exist.
  """
  
  for entryEnum in FIELD_ATTR:
    if fieldLabel == FIELD_ATTR[entryEnum][0]:
      return entryEnum
Beispiel #53
0
}

VERSION_STATUS_COLORS = {
    "new": "blue",
    "new in series": "blue",
    "obsolete": "red",
    "recommended": "green",
    "old": "red",
    "unrecommended": "red",
    "unknown": "cyan"
}

CONFIG = conf.config_dict(
    "arm", {
        "startup.interface.ipAddress": "127.0.0.1",
        "startup.interface.port": 9051,
        "startup.interface.socket": "/var/run/tor/control",
        "features.showFdUsage": False,
    })


class HeaderPanel(panel.Panel, threading.Thread):
    """
  Top area contenting tor settings and system information. Stats are stored in
  the vals mapping, keys including:
    tor/  version, versionStatus, nickname, orPort, dirPort, controlPort,
          socketPath, exitPolicy, isAuthPassword (bool), isAuthCookie (bool),
          orListenAddr, *address, *fingerprint, *flags, pid, startTime,
          *fdUsed, fdLimit, isFdLimitEstimate
    sys/  hostname, os, version
    stat/ *%torCpu, *%armCpu, *rss, *%mem
Beispiel #54
0
def conf_handler(key, value):
  if key == 'features.config.selectionDetails.height':
    return max(0, value)
  elif key == 'features.config.state.colWidth.option':
    return max(5, value)
  elif key == 'features.config.state.colWidth.value':
    return max(5, value)
  elif key == 'features.config.order':
    return conf.parse_enum_csv(key, value[0], Field, 3)


CONFIG = conf.config_dict('nyx', {
  'features.config.order': [Field.MAN_ENTRY, Field.OPTION, Field.IS_DEFAULT],
  'features.config.selectionDetails.height': 6,
  'features.config.prepopulateEditValues': True,
  'features.config.state.showPrivateOptions': False,
  'features.config.state.showVirtualOptions': False,
  'features.config.state.colWidth.option': 25,
  'features.config.state.colWidth.value': 15,
}, conf_handler)


def get_field_from_label(field_label):
  """
  Converts field labels back to their enumeration, raising a ValueError if it
  doesn't exist.
  """

  for entry_enum in FIELD_ATTR:
    if field_label == FIELD_ATTR[entry_enum][0]:
      return entry_enum