This file is indexed.

/usr/share/arm/cli/connections/descriptorPopup.py is in tor-arm 1.4.5.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
"""
Popup providing the raw descriptor and consensus information for a relay.
"""

import math
import curses

import cli.popups
import cli.connections.connEntry

from util import panel, torTools, uiTools

# field keywords used to identify areas for coloring
LINE_NUM_COLOR = "yellow"
HEADER_COLOR = "cyan"
HEADER_PREFIX = ["ns/id/", "desc/id/"]

SIG_COLOR = "red"
SIG_START_KEYS = ["-----BEGIN RSA PUBLIC KEY-----", "-----BEGIN SIGNATURE-----"]
SIG_END_KEYS = ["-----END RSA PUBLIC KEY-----", "-----END SIGNATURE-----"]

UNRESOLVED_MSG = "No consensus data available"
ERROR_MSG = "Unable to retrieve data"

def showDescriptorPopup(connPanel):
  """
  Presents consensus descriptor in popup window with the following controls:
  Up, Down, Page Up, Page Down - scroll descriptor
  Right, Left - next / previous connection
  Enter, Space, d, D - close popup
  
  Arguments:
    connPanel - connection panel providing the dialog
  """
  
  # hides the title of the connection panel
  connPanel.setTitleVisible(False)
  connPanel.redraw(True)
  
  control = cli.controller.getController()
  panel.CURSES_LOCK.acquire()
  isDone = False
  
  try:
    while not isDone:
      selection = connPanel.getSelection()
      if not selection: break
      
      fingerprint = selection.foreign.getFingerprint()
      if fingerprint == "UNKNOWN": fingerprint = None
      
      displayText = getDisplayText(fingerprint)
      displayColor = cli.connections.connEntry.CATEGORY_COLOR[selection.getType()]
      showLineNumber = fingerprint != None
      
      # determines the maximum popup size the displayText can fill
      pHeight, pWidth = getPreferredSize(displayText, connPanel.maxX, showLineNumber)
      
      popup, _, height = cli.popups.init(pHeight, pWidth)
      if not popup: break
      scroll, isChanged = 0, True
      
      try:
        while not isDone:
          if isChanged:
            draw(popup, fingerprint, displayText, displayColor, scroll, showLineNumber)
            isChanged = False
          
          key = control.getScreen().getch()
          
          if uiTools.isScrollKey(key):
            # TODO: This is a bit buggy in that scrolling is by displayText
            # lines rather than the displayed lines, causing issues when
            # content wraps. The result is that we can't have a scrollbar and
            # can't scroll to the bottom if there's a multi-line being
            # displayed. However, trying to correct this introduces a big can
            # of worms and after hours decided that this isn't worth the
            # effort...
            
            newScroll = uiTools.getScrollPosition(key, scroll, height - 2, len(displayText))
            
            if scroll != newScroll:
              scroll, isChanged = newScroll, True
          elif uiTools.isSelectionKey(key) or key in (ord('d'), ord('D')):
            isDone = True # closes popup
          elif key in (curses.KEY_LEFT, curses.KEY_RIGHT):
            # navigation - pass on to connPanel and recreate popup
            connPanel.handleKey(curses.KEY_UP if key == curses.KEY_LEFT else curses.KEY_DOWN)
            break
      finally: cli.popups.finalize()
  finally:
    connPanel.setTitleVisible(True)
    connPanel.redraw(True)
    panel.CURSES_LOCK.release()

def getDisplayText(fingerprint):
  """
  Provides the descriptor and consensus entry for a relay. This is a list of
  lines to be displayed by the dialog.
  """
  
  if not fingerprint: return [UNRESOLVED_MSG]
  conn, description = torTools.getConn(), []
  
  description.append("ns/id/%s" % fingerprint)
  consensusEntry = conn.getConsensusEntry(fingerprint)
  
  if consensusEntry: description += consensusEntry.split("\n")
  else: description += [ERROR_MSG, ""]
  
  description.append("desc/id/%s" % fingerprint)
  descriptorEntry = conn.getDescriptorEntry(fingerprint)
  
  if descriptorEntry: description += descriptorEntry.split("\n")
  else: description += [ERROR_MSG]
  
  return description

def getPreferredSize(text, maxWidth, showLineNumber):
  """
  Provides the (height, width) tuple for the preferred size of the given text.
  """
  
  width, height = 0, len(text) + 2
  lineNumWidth = int(math.log10(len(text))) + 1
  for line in text:
    # width includes content, line number field, and border
    lineWidth = len(line) + 5
    if showLineNumber: lineWidth += lineNumWidth
    width = max(width, lineWidth)
    
    # tracks number of extra lines that will be taken due to text wrap
    height += (lineWidth - 2) / maxWidth
  
  return (height, width)

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