/ src / bitmessagecurses / __init__.py
__init__.py
   1  """
   2  Bitmessage commandline interface
   3  """
   4  # Copyright (c) 2014 Luke Montalvo <lukemontalvo@gmail.com>
   5  # This file adds a alternative commandline interface, feel free to critique and fork
   6  #
   7  # This has only been tested on Arch Linux and Linux Mint
   8  # Dependencies:
   9  #  * from python2-pip
  10  #     * python2-pythondialog
  11  #  * dialog
  12  
  13  import configparser
  14  import curses
  15  import os
  16  import sys
  17  import time
  18  from textwrap import fill
  19  from threading import Timer
  20  
  21  from dialog import Dialog
  22  import helper_sent
  23  import l10n
  24  import network.stats
  25  import queues
  26  import shared
  27  import shutdown
  28  import state
  29  
  30  from addresses import addBMIfNotPresent, decodeAddress
  31  from bmconfigparser import config
  32  from helper_sql import sqlExecute, sqlQuery
  33  
  34  # pylint: disable=global-statement
  35  
  36  quit_ = False
  37  menutab = 1
  38  menu = ["Inbox", "Send", "Sent", "Your Identities", "Subscriptions", "Address Book", "Blacklist", "Network Status"]
  39  naptime = 100
  40  log = ""
  41  logpad = None
  42  inventorydata = 0
  43  startuptime = time.time()
  44  
  45  inbox = []
  46  inboxcur = 0
  47  sentbox = []
  48  sentcur = 0
  49  addresses = []
  50  addrcur = 0
  51  addrcopy = 0
  52  subscriptions = []
  53  subcur = 0
  54  addrbook = []
  55  abookcur = 0
  56  blacklist = []
  57  blackcur = 0
  58  bwtype = "black"
  59  
  60  BROADCAST_STR = "[Broadcast subscribers]"
  61  
  62  
  63  class printLog(object):
  64      """Printing logs"""
  65      # pylint: disable=no-self-use
  66  
  67      def write(self, output):
  68          """Write logs"""
  69          global log
  70          log += output
  71  
  72      def flush(self):
  73          """Flush logs"""
  74          pass
  75  
  76  
  77  class errLog(object):
  78      """Error logs"""
  79      # pylint: disable=no-self-use
  80  
  81      def write(self, output):
  82          """Write error logs"""
  83          global log
  84          log += "!" + output
  85  
  86      def flush(self):
  87          """Flush error logs"""
  88          pass
  89  
  90  
  91  printlog = printLog()
  92  errlog = errLog()
  93  
  94  
  95  def cpair(a):
  96      """Color pairs"""
  97      r = curses.color_pair(a)
  98      if r not in list(range(1, curses.COLOR_PAIRS - 1)):
  99          r = curses.color_pair(0)
 100      return r
 101  
 102  
 103  def ascii(s):
 104      """ASCII values"""
 105      r = ""
 106      for c in s:
 107          if ord(c) in range(128):
 108              r += c
 109      return r
 110  
 111  
 112  def drawmenu(stdscr):
 113      """Creating menu's"""
 114      menustr = " "
 115      for i, _ in enumerate(menu):
 116          if menutab == i + 1:
 117              menustr = menustr[:-1]
 118              menustr += "["
 119          menustr += str(i + 1) + menu[i]
 120          if menutab == i + 1:
 121              menustr += "] "
 122          elif i != len(menu) - 1:
 123              menustr += "  "
 124      stdscr.addstr(2, 5, menustr, curses.A_UNDERLINE)
 125  
 126  
 127  def set_background_title(d, title):
 128      """Setting background title"""
 129      try:
 130          d.set_background_title(title)
 131      except:  # noqa:E722
 132          d.add_persistent_args(("--backtitle", title))
 133  
 134  
 135  def scrollbox(d, text, height=None, width=None):
 136      """Setting scroll box"""
 137      try:
 138          d.scrollbox(text, height, width, exit_label="Continue")
 139      except:  # noqa:E722
 140          d.msgbox(text, height or 0, width or 0, ok_label="Continue")
 141  
 142  
 143  def resetlookups():
 144      """Reset the Inventory Lookups"""
 145      global inventorydata
 146      inventorydata = state.Inventory.numberOfInventoryLookupsPerformed
 147      state.Inventory.numberOfInventoryLookupsPerformed = 0
 148      Timer(1, resetlookups, ()).start()
 149  
 150  
 151  def drawtab(stdscr):
 152      """Method for drawing different tabs"""
 153      # pylint: disable=too-many-branches, too-many-statements
 154      if menutab in range(1, len(menu) + 1):
 155          if menutab == 1:		# Inbox
 156              stdscr.addstr(3, 5, "To", curses.A_BOLD)
 157              stdscr.addstr(3, 40, "From", curses.A_BOLD)
 158              stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
 159              stdscr.addstr(3, 120, "Time Received", curses.A_BOLD)
 160              stdscr.hline(4, 5, '-', 121)
 161              for i, item in enumerate(inbox[max(min(len(inbox) - curses.LINES + 6, inboxcur - 5), 0):]):
 162                  if 6 + i < curses.LINES:
 163                      a = 0
 164                      if i == inboxcur - max(min(len(inbox) - curses.LINES + 6, inboxcur - 5), 0):
 165                          # Highlight current address
 166                          a = a | curses.A_REVERSE
 167                      if item[7] is False:		# If not read, highlight
 168                          a = a | curses.A_BOLD
 169                      stdscr.addstr(5 + i, 5, item[1][:34], a)
 170                      stdscr.addstr(5 + i, 40, item[3][:39], a)
 171                      stdscr.addstr(5 + i, 80, item[5][:39], a)
 172                      stdscr.addstr(5 + i, 120, item[6][:39], a)
 173          elif menutab == 3:		 # Sent
 174              stdscr.addstr(3, 5, "To", curses.A_BOLD)
 175              stdscr.addstr(3, 40, "From", curses.A_BOLD)
 176              stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
 177              stdscr.addstr(3, 120, "Status", curses.A_BOLD)
 178              stdscr.hline(4, 5, '-', 121)
 179              for i, item in enumerate(sentbox[max(min(len(sentbox) - curses.LINES + 6, sentcur - 5), 0):]):
 180                  if 6 + i < curses.LINES:
 181                      a = 0
 182                      if i == sentcur - max(min(len(sentbox) - curses.LINES + 6, sentcur - 5), 0):
 183                          # Highlight current address
 184                          a = a | curses.A_REVERSE
 185                      stdscr.addstr(5 + i, 5, item[0][:34], a)
 186                      stdscr.addstr(5 + i, 40, item[2][:39], a)
 187                      stdscr.addstr(5 + i, 80, item[4][:39], a)
 188                      stdscr.addstr(5 + i, 120, item[5][:39], a)
 189          elif menutab == 2 or menutab == 4:		 # Send or Identities
 190              stdscr.addstr(3, 5, "Label", curses.A_BOLD)
 191              stdscr.addstr(3, 40, "Address", curses.A_BOLD)
 192              stdscr.addstr(3, 80, "Stream", curses.A_BOLD)
 193              stdscr.hline(4, 5, '-', 81)
 194              for i, item in enumerate(addresses[max(min(len(addresses) - curses.LINES + 6, addrcur - 5), 0):]):
 195                  if 6 + i < curses.LINES:
 196                      a = 0
 197                      if i == addrcur - max(min(len(addresses) - curses.LINES + 6, addrcur - 5), 0):
 198                          # Highlight current address
 199                          a = a | curses.A_REVERSE
 200                      if item[1] and item[3] not in [8, 9]:		 # Embolden enabled, non-special addresses
 201                          a = a | curses.A_BOLD
 202                      stdscr.addstr(5 + i, 5, item[0][:34], a)
 203                      stdscr.addstr(5 + i, 40, item[2][:39], cpair(item[3]) | a)
 204                      stdscr.addstr(5 + i, 80, str(1)[:39], a)
 205          elif menutab == 5:		 # Subscriptions
 206              stdscr.addstr(3, 5, "Label", curses.A_BOLD)
 207              stdscr.addstr(3, 80, "Address", curses.A_BOLD)
 208              stdscr.addstr(3, 120, "Enabled", curses.A_BOLD)
 209              stdscr.hline(4, 5, '-', 121)
 210              for i, item in enumerate(subscriptions[max(min(len(subscriptions) - curses.LINES + 6, subcur - 5), 0):]):
 211                  if 6 + i < curses.LINES:
 212                      a = 0
 213                      if i == subcur - max(min(len(subscriptions) - curses.LINES + 6, subcur - 5), 0):
 214                          # Highlight current address
 215                          a = a | curses.A_REVERSE
 216                      if item[2]:		 # Embolden enabled subscriptions
 217                          a = a | curses.A_BOLD
 218                      stdscr.addstr(5 + i, 5, item[0][:74], a)
 219                      stdscr.addstr(5 + i, 80, item[1][:39], a)
 220                      stdscr.addstr(5 + i, 120, str(item[2]), a)
 221          elif menutab == 6:		 # Address book
 222              stdscr.addstr(3, 5, "Label", curses.A_BOLD)
 223              stdscr.addstr(3, 40, "Address", curses.A_BOLD)
 224              stdscr.hline(4, 5, '-', 41)
 225              for i, item in enumerate(addrbook[max(min(len(addrbook) - curses.LINES + 6, abookcur - 5), 0):]):
 226                  if 6 + i < curses.LINES:
 227                      a = 0
 228                      if i == abookcur - max(min(len(addrbook) - curses.LINES + 6, abookcur - 5), 0):
 229                          # Highlight current address
 230                          a = a | curses.A_REVERSE
 231                      stdscr.addstr(5 + i, 5, item[0][:34], a)
 232                      stdscr.addstr(5 + i, 40, item[1][:39], a)
 233          elif menutab == 7:		 # Blacklist
 234              stdscr.addstr(3, 5, "Type: " + bwtype)
 235              stdscr.addstr(4, 5, "Label", curses.A_BOLD)
 236              stdscr.addstr(4, 80, "Address", curses.A_BOLD)
 237              stdscr.addstr(4, 120, "Enabled", curses.A_BOLD)
 238              stdscr.hline(5, 5, '-', 121)
 239              for i, item in enumerate(blacklist[max(min(len(blacklist) - curses.LINES + 6, blackcur - 5), 0):]):
 240                  if 7 + i < curses.LINES:
 241                      a = 0
 242                      if i == blackcur - max(min(len(blacklist) - curses.LINES + 6, blackcur - 5), 0):
 243                          # Highlight current address
 244                          a = a | curses.A_REVERSE
 245                      if item[2]:		 # Embolden enabled subscriptions
 246                          a = a | curses.A_BOLD
 247                      stdscr.addstr(6 + i, 5, item[0][:74], a)
 248                      stdscr.addstr(6 + i, 80, item[1][:39], a)
 249                      stdscr.addstr(6 + i, 120, str(item[2]), a)
 250          elif menutab == 8:		 # Network status
 251              # Connection data
 252              connected_hosts = network.stats.connectedHostsList()
 253              stdscr.addstr(
 254                  4, 5, "Total Connections: " +
 255                  str(len(connected_hosts)).ljust(2)
 256              )
 257              stdscr.addstr(6, 6, "Stream #", curses.A_BOLD)
 258              stdscr.addstr(6, 18, "Connections", curses.A_BOLD)
 259              stdscr.hline(7, 6, '-', 23)
 260              streamcount = []
 261              for host, stream in connected_hosts:
 262                  if stream >= len(streamcount):
 263                      streamcount.append(1)
 264                  else:
 265                      streamcount[stream] += 1
 266              for i, item in enumerate(streamcount):
 267                  if i < 4:
 268                      if i == 0:
 269                          stdscr.addstr(8 + i, 6, "?")
 270                      else:
 271                          stdscr.addstr(8 + i, 6, str(i))
 272                      stdscr.addstr(8 + i, 18, str(item).ljust(2))
 273  
 274              # Uptime and processing data
 275              stdscr.addstr(
 276                  6, 35, "Since startup on " + l10n.formatTimestamp(startuptime))
 277              stdscr.addstr(7, 40, "Processed " + str(
 278                  state.numberOfMessagesProcessed).ljust(4) + " person-to-person messages.")
 279              stdscr.addstr(8, 40, "Processed " + str(
 280                  state.numberOfBroadcastsProcessed).ljust(4) + " broadcast messages.")
 281              stdscr.addstr(9, 40, "Processed " + str(
 282                  state.numberOfPubkeysProcessed).ljust(4) + " public keys.")
 283  
 284              # Inventory data
 285              stdscr.addstr(11, 35, "Inventory lookups per second: " + str(inventorydata).ljust(3))
 286  
 287              # Log
 288              stdscr.addstr(13, 6, "Log", curses.A_BOLD)
 289              n = log.count('\n')
 290              if n > 0:
 291                  lg = log.split('\n')
 292                  if n > 512:
 293                      del lg[:(n - 256)]
 294                      logpad.erase()
 295                      n = len(lg)
 296                  for i, item in enumerate(lg):
 297                      a = 0
 298                      if item and item[0] == '!':
 299                          a = curses.color_pair(1)
 300                          item = item[1:]
 301                      logpad.addstr(i, 0, item, a)
 302                  logpad.refresh(n - curses.LINES + 2, 0, 14, 6, curses.LINES - 2, curses.COLS - 7)
 303      stdscr.refresh()
 304  
 305  
 306  def redraw(stdscr):
 307      """Redraw menu"""
 308      stdscr.erase()
 309      stdscr.border()
 310      drawmenu(stdscr)
 311      stdscr.refresh()
 312  
 313  
 314  def dialogreset(stdscr):
 315      """Resetting dialogue"""
 316      stdscr.clear()
 317      stdscr.keypad(1)
 318      curses.curs_set(0)
 319  
 320  
 321  # pylint: disable=too-many-branches, too-many-statements
 322  def handlech(c, stdscr):
 323      """Handle character given on the command-line interface"""
 324      # pylint: disable=redefined-outer-name, too-many-nested-blocks, too-many-locals
 325      if c != curses.ERR:
 326          global inboxcur, addrcur, sentcur, subcur, abookcur, blackcur
 327          if c in range(256):
 328              if chr(c) in '12345678':
 329                  global menutab
 330                  menutab = int(chr(c))
 331              elif chr(c) == 'q':
 332                  global quit_
 333                  quit_ = True
 334              elif chr(c) == '\n':
 335                  curses.curs_set(1)
 336                  d = Dialog(dialog="dialog")
 337                  if menutab == 1:
 338                      set_background_title(d, "Inbox Message Dialog Box")
 339                      r, t = d.menu(
 340                          "Do what with \"" + inbox[inboxcur][5] + "\" from \"" + inbox[inboxcur][3] + "\"?",
 341                          choices=[
 342                              ("1", "View message"),
 343                              ("2", "Mark message as unread"),
 344                              ("3", "Reply"),
 345                              ("4", "Add sender to Address Book"),
 346                              ("5", "Save message as text file"),
 347                              ("6", "Move to trash")])
 348                      if r == d.DIALOG_OK:
 349                          if t == "1":  # View
 350                              set_background_title(
 351                                  d,
 352                                  "\"" +
 353                                  inbox[inboxcur][5] +
 354                                  "\" from \"" +
 355                                  inbox[inboxcur][3] +
 356                                  "\" to \"" +
 357                                  inbox[inboxcur][1] +
 358                                  "\"")
 359                              data = ""       # pyint: disable=redefined-outer-name
 360                              ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
 361                              if ret != []:
 362                                  for row in ret:
 363                                      data, = row
 364                                  data = shared.fixPotentiallyInvalidUTF8Data(data)
 365                                  msg = ""
 366                                  for i, item in enumerate(data.split("\n")):
 367                                      msg += fill(item, replace_whitespace=False) + "\n"
 368                                  scrollbox(d, str(ascii(msg)), 30, 80)
 369                                  sqlExecute("UPDATE inbox SET read=1 WHERE msgid=?", inbox[inboxcur][0])
 370                                  inbox[inboxcur][7] = 1
 371                              else:
 372                                  scrollbox(d, str("Could not fetch message."))
 373                          elif t == "2":       # Mark unread
 374                              sqlExecute("UPDATE inbox SET read=0 WHERE msgid=?", inbox[inboxcur][0])
 375                              inbox[inboxcur][7] = 0
 376                          elif t == "3":       # Reply
 377                              curses.curs_set(1)
 378                              m = inbox[inboxcur]
 379                              fromaddr = m[4]
 380                              ischan = False
 381                              for i, item in enumerate(addresses):
 382                                  if fromaddr == item[2] and item[3] != 0:
 383                                      ischan = True
 384                                      break
 385                              if not addresses[i][1]:  # pylint: disable=undefined-loop-variable
 386                                  scrollbox(d, str(
 387                                      "Sending address disabled, please either enable it"
 388                                      "or choose a different address."))
 389                                  return
 390                              toaddr = m[2]
 391                              if ischan:
 392                                  toaddr = fromaddr
 393  
 394                              subject = m[5]
 395                              if not m[5][:4] == "Re: ":
 396                                  subject = "Re: " + m[5]
 397                              body = ""
 398                              ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", m[0])
 399                              if ret != []:
 400                                  body = "\n\n------------------------------------------------------\n"
 401                                  for row in ret:
 402                                      body, = row
 403  
 404                              sendMessage(fromaddr, toaddr, ischan, subject, body, True)
 405                              dialogreset(stdscr)
 406                          elif t == "4":       # Add to Address Book
 407                              addr = inbox[inboxcur][4]
 408                              if addr not in [item[1] for i, item in enumerate(addrbook)]:
 409                                  r, t = d.inputbox("Label for address \"" + addr + "\"")
 410                                  if r == d.DIALOG_OK:
 411                                      label = t
 412                                      sqlExecute("INSERT INTO addressbook VALUES (?,?)", label, addr)
 413                                      # Prepend entry
 414                                      addrbook.reverse()
 415                                      addrbook.append([label, addr])
 416                                      addrbook.reverse()
 417                              else:
 418                                  scrollbox(d, str("The selected address is already in the Address Book."))
 419                          elif t == "5":      # Save message
 420                              set_background_title(d, "Save \"" + inbox[inboxcur][5] + "\" as text file")
 421                              r, t = d.inputbox("Filename", init=inbox[inboxcur][5] + ".txt")
 422                              if r == d.DIALOG_OK:
 423                                  msg = ""
 424                                  ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
 425                                  if ret != []:
 426                                      for row in ret:
 427                                          msg, = row
 428                                      fh = open(t, "a")       # Open in append mode just in case
 429                                      fh.write(msg)
 430                                      fh.close()
 431                                  else:
 432                                      scrollbox(d, str("Could not fetch message."))
 433                          elif t == "6":       # Move to trash
 434                              sqlExecute("UPDATE inbox SET folder='trash' WHERE msgid=?", inbox[inboxcur][0])
 435                              del inbox[inboxcur]
 436                              scrollbox(d, str(
 437                                  "Message moved to trash. There is no interface to view your trash,"
 438                                  " \nbut the message is still on disk if you are desperate to recover it."))
 439                  elif menutab == 2:
 440                      a = ""
 441                      if addresses[addrcur][3] != 0:      # if current address is a chan
 442                          a = addresses[addrcur][2]
 443                      sendMessage(addresses[addrcur][2], a)
 444                  elif menutab == 3:
 445                      set_background_title(d, "Sent Messages Dialog Box")
 446                      r, t = d.menu(
 447                          "Do what with \"" + sentbox[sentcur][4] + "\" to \"" + sentbox[sentcur][0] + "\"?",
 448                          choices=[
 449                              ("1", "View message"),
 450                              ("2", "Move to trash")])
 451                      if r == d.DIALOG_OK:
 452                          if t == "1":  # View
 453                              set_background_title(
 454                                  d,
 455                                  "\"" +
 456                                  sentbox[sentcur][4] +
 457                                  "\" from \"" +
 458                                  sentbox[sentcur][3] +
 459                                  "\" to \"" +
 460                                  sentbox[sentcur][1] +
 461                                  "\"")
 462                              data = ""
 463                              ret = sqlQuery(
 464                                  "SELECT message FROM sent WHERE subject=? AND ackdata=?",
 465                                  sentbox[sentcur][4],
 466                                  sentbox[sentcur][6])
 467                              if ret != []:
 468                                  for row in ret:
 469                                      data, = row
 470                                  data = shared.fixPotentiallyInvalidUTF8Data(data)
 471                                  msg = ""
 472                                  for i, item in enumerate(data.split("\n")):
 473                                      msg += fill(item, replace_whitespace=False) + "\n"
 474                                  scrollbox(d, str(ascii(msg)), 30, 80)
 475                              else:
 476                                  scrollbox(d, str("Could not fetch message."))
 477                          elif t == "2":       # Move to trash
 478                              sqlExecute(
 479                                  "UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?",
 480                                  sentbox[sentcur][4],
 481                                  sentbox[sentcur][6])
 482                              del sentbox[sentcur]
 483                              scrollbox(d, str(
 484                                  "Message moved to trash. There is no interface to view your trash"
 485                                  " \nbut the message is still on disk if you are desperate to recover it."))
 486                  elif menutab == 4:
 487                      set_background_title(d, "Your Identities Dialog Box")
 488                      if len(addresses) <= addrcur:
 489                          r, t = d.menu(
 490                              "Do what with addresses?",
 491                              choices=[
 492                                  ("1", "Create new address")])
 493                      else:
 494                          r, t = d.menu(
 495                              "Do what with \"" + addresses[addrcur][0] + "\" : \"" + addresses[addrcur][2] + "\"?",
 496                              choices=[
 497                                  ("1", "Create new address"),
 498                                  ("2", "Send a message from this address"),
 499                                  ("3", "Rename"),
 500                                  ("4", "Enable"),
 501                                  ("5", "Disable"),
 502                                  ("6", "Delete"),
 503                                  ("7", "Special address behavior")])
 504                      if r == d.DIALOG_OK:
 505                          if t == "1":         # Create new address
 506                              set_background_title(d, "Create new address")
 507                              scrollbox(
 508                                  d, str(
 509                                      "Here you may generate as many addresses as you like.\n"
 510                                      "Indeed, creating and abandoning addresses is encouraged.\n"
 511                                      "Deterministic addresses have several pros and cons:\n"
 512                                      "\nPros:\n"
 513                                      "  * You can recreate your addresses on any computer from memory\n"
 514                                      "  * You need not worry about backing up your keys.dat file as long as you"
 515                                      " \n    can remember your passphrase\n"
 516                                      "Cons:\n"
 517                                      "  * You must remember (or write down) your passphrase in order to recreate"
 518                                      " \n    your keys if they are lost\n"
 519                                      "  * You must also remember the address version and stream numbers\n"
 520                                      "  * If you choose a weak passphrase someone may be able to brute-force it"
 521                                      " \n    and then send and receive messages as you"))
 522                              r, t = d.menu(
 523                                  "Choose an address generation technique",
 524                                  choices=[
 525                                      ("1", "Use a random number generator"),
 526                                      ("2", "Use a passphrase")])
 527                              if r == d.DIALOG_OK:
 528                                  if t == "1":
 529                                      set_background_title(d, "Randomly generate address")
 530                                      r, t = d.inputbox("Label (not shown to anyone except you)")
 531                                      label = ""
 532                                      if r == d.DIALOG_OK and t:
 533                                          label = t
 534                                      r, t = d.menu(
 535                                          "Choose a stream",
 536                                          choices=[("1", "Use the most available stream"),
 537                                                   ("", "(Best if this is the first of many addresses you will create)"),
 538                                                   ("2", "Use the same stream as an existing address"),
 539                                                   ("", "(Saves you some bandwidth and processing power)")])
 540                                      if r == d.DIALOG_OK:
 541                                          if t == "1":
 542                                              stream = 1
 543                                          elif t == "2":
 544                                              addrs = []
 545                                              for i, item in enumerate(addresses):
 546                                                  addrs.append([str(i), item[2]])
 547                                              r, t = d.menu("Choose an existing address's stream", choices=addrs)
 548                                              if r == d.DIALOG_OK:
 549                                                  stream = decodeAddress(addrs[int(t)][1])[2]
 550                                          shorten = False
 551                                          r, t = d.checklist(
 552                                              "Miscellaneous options",
 553                                              choices=[(
 554                                                  "1",
 555                                                  "Spend time shortening the address",
 556                                                  1 if shorten else 0)])
 557                                          if r == d.DIALOG_OK and "1" in t:
 558                                              shorten = True
 559                                          queues.addressGeneratorQueue.put((
 560                                              "createRandomAddress",
 561                                              4,
 562                                              stream,
 563                                              label,
 564                                              1,
 565                                              "",
 566                                              shorten))
 567                                  elif t == "2":
 568                                      set_background_title(d, "Make deterministic addresses")
 569                                      r, t = d.passwordform(
 570                                          "Enter passphrase",
 571                                          [
 572                                              ("Passphrase", 1, 1, "", 2, 1, 64, 128),
 573                                              ("Confirm passphrase", 3, 1, "", 4, 1, 64, 128)],
 574                                          form_height=4, insecure=True)
 575                                      if r == d.DIALOG_OK:
 576                                          if t[0] == t[1]:
 577                                              passphrase = t[0]
 578                                              r, t = d.rangebox(
 579                                                  "Number of addresses to generate",
 580                                                  width=48,
 581                                                  min=1,
 582                                                  max=99,
 583                                                  init=8)
 584                                              if r == d.DIALOG_OK:
 585                                                  number = t
 586                                                  stream = 1
 587                                                  shorten = False
 588                                                  r, t = d.checklist(
 589                                                      "Miscellaneous options",
 590                                                      choices=[(
 591                                                          "1",
 592                                                          "Spend time shortening the address",
 593                                                          1 if shorten else 0)])
 594                                                  if r == d.DIALOG_OK and "1" in t:
 595                                                      shorten = True
 596                                                  scrollbox(
 597                                                      d, str(
 598                                                          "In addition to your passphrase, be sure to remember the"
 599                                                          " following numbers:\n"
 600                                                          "\n  * Address version number: " + str(4) + "\n"
 601                                                          "  * Stream number: " + str(stream)))
 602                                                  queues.addressGeneratorQueue.put(
 603                                                      ('createDeterministicAddresses', 4, stream,
 604                                                       "unused deterministic address", number,
 605                                                       str(passphrase), shorten))
 606                                          else:
 607                                              scrollbox(d, str("Passphrases do not match"))
 608                          elif t == "2":      # Send a message
 609                              a = ""
 610                              if addresses[addrcur][3] != 0:       # if current address is a chan
 611                                  a = addresses[addrcur][2]
 612                              sendMessage(addresses[addrcur][2], a)
 613                          elif t == "3":      # Rename address label
 614                              a = addresses[addrcur][2]
 615                              label = addresses[addrcur][0]
 616                              r, t = d.inputbox("New address label", init=label)
 617                              if r == d.DIALOG_OK:
 618                                  label = t
 619                                  config.set(a, "label", label)
 620                                  # Write config
 621                                  config.save()
 622                                  addresses[addrcur][0] = label
 623                          elif t == "4":      # Enable address
 624                              a = addresses[addrcur][2]
 625                              config.set(a, "enabled", "true")       # Set config
 626                              # Write config
 627                              config.save()
 628                              # Change color
 629                              if config.safeGetBoolean(a, 'chan'):
 630                                  addresses[addrcur][3] = 9       # orange
 631                              elif config.safeGetBoolean(a, 'mailinglist'):
 632                                  addresses[addrcur][3] = 5       # magenta
 633                              else:
 634                                  addresses[addrcur][3] = 0       # black
 635                              addresses[addrcur][1] = True
 636                              shared.reloadMyAddressHashes()      # Reload address hashes
 637                          elif t == "5":       # Disable address
 638                              a = addresses[addrcur][2]
 639                              config.set(a, "enabled", "false")     # Set config
 640                              addresses[addrcur][3] = 8       # Set color to gray
 641                              # Write config
 642                              config.save()
 643                              addresses[addrcur][1] = False
 644                              shared.reloadMyAddressHashes()      # Reload address hashes
 645                          elif t == "6":      # Delete address
 646                              r, t = d.inputbox("Type in \"I want to delete this address\"", width=50)
 647                              if r == d.DIALOG_OK and t == "I want to delete this address":
 648                                  config.remove_section(addresses[addrcur][2])
 649                                  config.save()
 650                                  del addresses[addrcur]
 651                          elif t == "7":      # Special address behavior
 652                              a = addresses[addrcur][2]
 653                              set_background_title(d, "Special address behavior")
 654                              if config.safeGetBoolean(a, "chan"):
 655                                  scrollbox(d, str(
 656                                      "This is a chan address. You cannot use it as a pseudo-mailing list."))
 657                              else:
 658                                  m = config.safeGetBoolean(a, "mailinglist")
 659                                  r, t = d.radiolist(
 660                                      "Select address behavior",
 661                                      choices=[
 662                                          ("1", "Behave as a normal address", not m),
 663                                          ("2", "Behave as a pseudo-mailing-list address", m)])
 664                                  if r == d.DIALOG_OK:
 665                                      if t == "1" and m:
 666                                          config.set(a, "mailinglist", "false")
 667                                          if addresses[addrcur][1]:
 668                                              addresses[addrcur][3] = 0       # Set color to black
 669                                          else:
 670                                              addresses[addrcur][3] = 8       # Set color to gray
 671                                      elif t == "2" and m is False:
 672                                          try:
 673                                              mn = config.get(a, "mailinglistname")
 674                                          except configparser.NoOptionError:
 675                                              mn = ""
 676                                          r, t = d.inputbox("Mailing list name", init=mn)
 677                                          if r == d.DIALOG_OK:
 678                                              mn = t
 679                                              config.set(a, "mailinglist", "true")
 680                                              config.set(a, "mailinglistname", mn)
 681                                              addresses[addrcur][3] = 6       # Set color to magenta
 682                                      # Write config
 683                                      config.save()
 684                  elif menutab == 5:
 685                      set_background_title(d, "Subscriptions Dialog Box")
 686                      if len(subscriptions) <= subcur:
 687                          r, t = d.menu(
 688                              "Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
 689                              choices=[
 690                                  ("1", "Add new subscription")])
 691                      else:
 692                          r, t = d.menu(
 693                              "Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
 694                              choices=[
 695                                  ("1", "Add new subscription"),
 696                                  ("2", "Delete this subscription"),
 697                                  ("3", "Enable"),
 698                                  ("4", "Disable")])
 699                      if r == d.DIALOG_OK:
 700                          if t == "1":
 701                              r, t = d.inputbox("New subscription address")
 702                              if r == d.DIALOG_OK:
 703                                  addr = addBMIfNotPresent(t)
 704                                  if not shared.isAddressInMySubscriptionsList(addr):
 705                                      r, t = d.inputbox("New subscription label")
 706                                      if r == d.DIALOG_OK:
 707                                          label = t
 708                                          # Prepend entry
 709                                          subscriptions.reverse()
 710                                          subscriptions.append([label, addr, True])
 711                                          subscriptions.reverse()
 712  
 713                                          sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True)
 714                                          shared.reloadBroadcastSendersForWhichImWatching()
 715                          elif t == "2":
 716                              r, t = d.inputbox("Type in \"I want to delete this subscription\"")
 717                              if r == d.DIALOG_OK and t == "I want to delete this subscription":
 718                                  sqlExecute(
 719                                      "DELETE FROM subscriptions WHERE label=? AND address=?",
 720                                      subscriptions[subcur][0],
 721                                      subscriptions[subcur][1])
 722                                  shared.reloadBroadcastSendersForWhichImWatching()
 723                                  del subscriptions[subcur]
 724                          elif t == "3":
 725                              sqlExecute(
 726                                  "UPDATE subscriptions SET enabled=1 WHERE label=? AND address=?",
 727                                  subscriptions[subcur][0],
 728                                  subscriptions[subcur][1])
 729                              shared.reloadBroadcastSendersForWhichImWatching()
 730                              subscriptions[subcur][2] = True
 731                          elif t == "4":
 732                              sqlExecute(
 733                                  "UPDATE subscriptions SET enabled=0 WHERE label=? AND address=?",
 734                                  subscriptions[subcur][0],
 735                                  subscriptions[subcur][1])
 736                              shared.reloadBroadcastSendersForWhichImWatching()
 737                              subscriptions[subcur][2] = False
 738                  elif menutab == 6:
 739                      set_background_title(d, "Address Book Dialog Box")
 740                      if len(addrbook) <= abookcur:
 741                          r, t = d.menu(
 742                              "Do what with addressbook?",
 743                              choices=[("3", "Add new address to Address Book")])
 744                      else:
 745                          r, t = d.menu(
 746                              "Do what with \"" + addrbook[abookcur][0] + "\" : \"" + addrbook[abookcur][1] + "\"",
 747                              choices=[
 748                                  ("1", "Send a message to this address"),
 749                                  ("2", "Subscribe to this address"),
 750                                  ("3", "Add new address to Address Book"),
 751                                  ("4", "Delete this address")])
 752                      if r == d.DIALOG_OK:
 753                          if t == "1":
 754                              sendMessage(recv=addrbook[abookcur][1])
 755                          elif t == "2":
 756                              r, t = d.inputbox("New subscription label")
 757                              if r == d.DIALOG_OK:
 758                                  label = t
 759                                  # Prepend entry
 760                                  subscriptions.reverse()
 761                                  subscriptions.append([label, addr, True])
 762                                  subscriptions.reverse()
 763  
 764                                  sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True)
 765                                  shared.reloadBroadcastSendersForWhichImWatching()
 766                          elif t == "3":
 767                              r, t = d.inputbox("Input new address")
 768                              if r == d.DIALOG_OK:
 769                                  addr = t
 770                                  if addr not in [item[1] for i, item in enumerate(addrbook)]:
 771                                      r, t = d.inputbox("Label for address \"" + addr + "\"")
 772                                      if r == d.DIALOG_OK:
 773                                          sqlExecute("INSERT INTO addressbook VALUES (?,?)", t, addr)
 774                                          # Prepend entry
 775                                          addrbook.reverse()
 776                                          addrbook.append([t, addr])
 777                                          addrbook.reverse()
 778                                  else:
 779                                      scrollbox(d, str("The selected address is already in the Address Book."))
 780                          elif t == "4":
 781                              r, t = d.inputbox("Type in \"I want to delete this Address Book entry\"")
 782                              if r == d.DIALOG_OK and t == "I want to delete this Address Book entry":
 783                                  sqlExecute(
 784                                      "DELETE FROM addressbook WHERE label=? AND address=?",
 785                                      addrbook[abookcur][0],
 786                                      addrbook[abookcur][1])
 787                                  del addrbook[abookcur]
 788                  elif menutab == 7:
 789                      set_background_title(d, "Blacklist Dialog Box")
 790                      r, t = d.menu(
 791                          "Do what with \"" + blacklist[blackcur][0] + "\" : \"" + blacklist[blackcur][1] + "\"?",
 792                          choices=[
 793                              ("1", "Delete"),
 794                              ("2", "Enable"),
 795                              ("3", "Disable")])
 796                      if r == d.DIALOG_OK:
 797                          if t == "1":
 798                              r, t = d.inputbox("Type in \"I want to delete this Blacklist entry\"")
 799                              if r == d.DIALOG_OK and t == "I want to delete this Blacklist entry":
 800                                  sqlExecute(
 801                                      "DELETE FROM blacklist WHERE label=? AND address=?",
 802                                      blacklist[blackcur][0],
 803                                      blacklist[blackcur][1])
 804                                  del blacklist[blackcur]
 805                          elif t == "2":
 806                              sqlExecute(
 807                                  "UPDATE blacklist SET enabled=1 WHERE label=? AND address=?",
 808                                  blacklist[blackcur][0],
 809                                  blacklist[blackcur][1])
 810                              blacklist[blackcur][2] = True
 811                          elif t == "3":
 812                              sqlExecute(
 813                                  "UPDATE blacklist SET enabled=0 WHERE label=? AND address=?",
 814                                  blacklist[blackcur][0],
 815                                  blacklist[blackcur][1])
 816                              blacklist[blackcur][2] = False
 817                  dialogreset(stdscr)
 818          else:
 819              if c == curses.KEY_UP:
 820                  if menutab == 1 and inboxcur > 0:
 821                      inboxcur -= 1
 822                  if (menutab == 2 or menutab == 4) and addrcur > 0:
 823                      addrcur -= 1
 824                  if menutab == 3 and sentcur > 0:
 825                      sentcur -= 1
 826                  if menutab == 5 and subcur > 0:
 827                      subcur -= 1
 828                  if menutab == 6 and abookcur > 0:
 829                      abookcur -= 1
 830                  if menutab == 7 and blackcur > 0:
 831                      blackcur -= 1
 832              elif c == curses.KEY_DOWN:
 833                  if menutab == 1 and inboxcur < len(inbox) - 1:
 834                      inboxcur += 1
 835                  if (menutab == 2 or menutab == 4) and addrcur < len(addresses) - 1:
 836                      addrcur += 1
 837                  if menutab == 3 and sentcur < len(sentbox) - 1:
 838                      sentcur += 1
 839                  if menutab == 5 and subcur < len(subscriptions) - 1:
 840                      subcur += 1
 841                  if menutab == 6 and abookcur < len(addrbook) - 1:
 842                      abookcur += 1
 843                  if menutab == 7 and blackcur < len(blacklist) - 1:
 844                      blackcur += 1
 845              elif c == curses.KEY_HOME:
 846                  if menutab == 1:
 847                      inboxcur = 0
 848                  if menutab == 2 or menutab == 4:
 849                      addrcur = 0
 850                  if menutab == 3:
 851                      sentcur = 0
 852                  if menutab == 5:
 853                      subcur = 0
 854                  if menutab == 6:
 855                      abookcur = 0
 856                  if menutab == 7:
 857                      blackcur = 0
 858              elif c == curses.KEY_END:
 859                  if menutab == 1:
 860                      inboxcur = len(inbox) - 1
 861                  if menutab == 2 or menutab == 4:
 862                      addrcur = len(addresses) - 1
 863                  if menutab == 3:
 864                      sentcur = len(sentbox) - 1
 865                  if menutab == 5:
 866                      subcur = len(subscriptions) - 1
 867                  if menutab == 6:
 868                      abookcur = len(addrbook) - 1
 869                  if menutab == 7:
 870                      blackcur = len(blackcur) - 1
 871          redraw(stdscr)
 872  
 873  
 874  # pylint: disable=too-many-locals, too-many-arguments
 875  def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=False):
 876      """Method for message sending"""
 877      if sender == "":
 878          return
 879      d = Dialog(dialog="dialog")
 880      set_background_title(d, "Send a message")
 881      if recv == "":
 882          r, t = d.inputbox(
 883              "Recipient address (Cancel to load from the Address Book or leave blank to broadcast)",
 884              10,
 885              60)
 886          if r != d.DIALOG_OK:
 887              global menutab
 888              menutab = 6
 889              return
 890          recv = t
 891      if broadcast is None and sender != recv:
 892          r, t = d.radiolist(
 893              "How to send the message?",
 894              choices=[
 895                  ("1", "Send to one or more specific people", 1),
 896                  ("2", "Broadcast to everyone who is subscribed to your address", 0)])
 897          if r != d.DIALOG_OK:
 898              return
 899          broadcast = False
 900          if t == "2":        # Broadcast
 901              broadcast = True
 902      if subject == "" or reply:
 903          r, t = d.inputbox("Message subject", width=60, init=subject)
 904          if r != d.DIALOG_OK:
 905              return
 906          subject = t
 907      if body == "" or reply:
 908          r, t = d.inputbox("Message body", 10, 80, init=body)
 909          if r != d.DIALOG_OK:
 910              return
 911          body = t
 912          body = body.replace("\\n", "\n").replace("\\t", "\t")
 913  
 914      if not broadcast:
 915          recvlist = []
 916          for _, item in enumerate(recv.replace(",", ";").split(";")):
 917              recvlist.append(item.strip())
 918          list(set(recvlist))         # Remove exact duplicates
 919          for addr in recvlist:
 920              if addr != "":
 921                  status, version, stream = decodeAddress(addr)[:3]
 922                  if status != "success":
 923                      set_background_title(d, "Recipient address error")
 924                      err = "Could not decode" + addr + " : " + status + "\n\n"
 925                      if status == "missingbm":
 926                          err += "Bitmessage addresses should start with \"BM-\"."
 927                      elif status == "checksumfailed":
 928                          err += "The address was not typed or copied correctly."
 929                      elif status == "invalidcharacters":
 930                          err += "The address contains invalid characters."
 931                      elif status == "versiontoohigh":
 932                          err += ("The address version is too high. Either you need to upgrade your Bitmessage software"
 933                                  " or your acquaintance is doing something clever.")
 934                      elif status == "ripetooshort":
 935                          err += ("Some data encoded in the address is too short. There might be something wrong with"
 936                                  " the software of your acquaintance.")
 937                      elif status == "ripetoolong":
 938                          err += ("Some data encoded in the address is too long. There might be something wrong with"
 939                                  " the software of your acquaintance.")
 940                      elif status == "varintmalformed":
 941                          err += ("Some data encoded in the address is malformed. There might be something wrong with"
 942                                  " the software of your acquaintance.")
 943                      else:
 944                          err += "It is unknown what is wrong with the address."
 945                      scrollbox(d, str(err))
 946                  else:
 947                      addr = addBMIfNotPresent(addr)
 948                      if version > 4 or version <= 1:
 949                          set_background_title(d, "Recipient address error")
 950                          scrollbox(d, str(
 951                              "Could not understand version number " +
 952                              version +
 953                              "of address" +
 954                              addr +
 955                              "."))
 956                          continue
 957                      if stream > 1 or stream == 0:
 958                          set_background_title(d, "Recipient address error")
 959                          scrollbox(d, str(
 960                              "Bitmessage currently only supports stream numbers of 1,"
 961                              "unlike as requested for address " + addr + "."))
 962                          continue
 963                      if not network.stats.connectedHostsList():
 964                          set_background_title(d, "Not connected warning")
 965                          scrollbox(d, str("Because you are not currently connected to the network, "))
 966                      helper_sent.insert(
 967                          toAddress=addr, fromAddress=sender, subject=subject, message=body)
 968                      queues.workerQueue.put(("sendmessage", addr))
 969      else:       # Broadcast
 970          if recv == "":
 971              set_background_title(d, "Empty sender error")
 972              scrollbox(d, str("You must specify an address to send the message from."))
 973          else:
 974              # dummy ackdata, no need for stealth
 975              helper_sent.insert(
 976                  fromAddress=sender, subject=subject,
 977                  message=body, status='broadcastqueued')
 978              queues.workerQueue.put(('sendbroadcast', ''))
 979  
 980  
 981  # pylint: disable=redefined-outer-name, too-many-locals
 982  def loadInbox():
 983      """Load the list of messages"""
 984      sys.stdout = sys.__stdout__
 985      print("Loading inbox messages...")
 986      sys.stdout = printlog
 987  
 988      where = "toaddress || fromaddress || subject || message"
 989      what = "%%"
 990      ret = sqlQuery("""SELECT msgid, toaddress, fromaddress, subject, received, read
 991          FROM inbox WHERE folder='inbox' AND %s LIKE ?
 992          ORDER BY received
 993          """ % (where,), what)
 994      for row in ret:
 995          msgid, toaddr, fromaddr, subject, received, read = row
 996          subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
 997  
 998          # Set label for to address
 999          try:
1000              if toaddr == BROADCAST_STR:
1001                  tolabel = BROADCAST_STR
1002              else:
1003                  tolabel = config.get(toaddr, "label")
1004          except:  # noqa:E722
1005              tolabel = ""
1006          if tolabel == "":
1007              tolabel = toaddr
1008          tolabel = shared.fixPotentiallyInvalidUTF8Data(tolabel)
1009  
1010          # Set label for from address
1011          fromlabel = ""
1012          if config.has_section(fromaddr):
1013              fromlabel = config.get(fromaddr, "label")
1014          if fromlabel == "":         # Check Address Book
1015              qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", fromaddr)
1016              if qr != []:
1017                  for r in qr:
1018                      fromlabel, = r
1019          if fromlabel == "":         # Check Subscriptions
1020              qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", fromaddr)
1021              if qr != []:
1022                  for r in qr:
1023                      fromlabel, = r
1024          if fromlabel == "":
1025              fromlabel = fromaddr
1026          fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel)
1027  
1028          # Load into array
1029          inbox.append([
1030              msgid, tolabel, toaddr, fromlabel, fromaddr, subject,
1031              l10n.formatTimestamp(received), read])
1032      inbox.reverse()
1033  
1034  
1035  def loadSent():
1036      """Load the messages that sent"""
1037      sys.stdout = sys.__stdout__
1038      print("Loading sent messages...")
1039      sys.stdout = printlog
1040  
1041      where = "toaddress || fromaddress || subject || message"
1042      what = "%%"
1043      ret = sqlQuery("""SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
1044          FROM sent WHERE folder='sent' AND %s LIKE ?
1045          ORDER BY lastactiontime
1046          """ % (where,), what)
1047      for row in ret:
1048          toaddr, fromaddr, subject, status, ackdata, lastactiontime = row
1049          subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
1050  
1051          # Set label for to address
1052          tolabel = ""
1053          qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", toaddr)
1054          if qr != []:
1055              for r in qr:
1056                  tolabel, = r
1057          if tolabel == "":
1058              qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", toaddr)
1059              if qr != []:
1060                  for r in qr:
1061                      tolabel, = r
1062          if tolabel == "":
1063              if config.has_section(toaddr):
1064                  tolabel = config.get(toaddr, "label")
1065          if tolabel == "":
1066              tolabel = toaddr
1067  
1068          # Set label for from address
1069          fromlabel = ""
1070          if config.has_section(fromaddr):
1071              fromlabel = config.get(fromaddr, "label")
1072          if fromlabel == "":
1073              fromlabel = fromaddr
1074  
1075          # Set status string
1076          if status == "awaitingpubkey":
1077              statstr = "Waiting for their public key. Will request it again soon"
1078          elif status == "doingpowforpubkey":
1079              statstr = "Encryption key request queued"
1080          elif status == "msgqueued":
1081              statstr = "Message queued"
1082          elif status == "msgsent":
1083              t = l10n.formatTimestamp(lastactiontime)
1084              statstr = "Message sent at " + t + ".Waiting for acknowledgement."
1085          elif status == "msgsentnoackexpected":
1086              t = l10n.formatTimestamp(lastactiontime)
1087              statstr = "Message sent at " + t + "."
1088          elif status == "doingmsgpow":
1089              statstr = "The proof of work required to send the message has been queued."
1090          elif status == "ackreceived":
1091              t = l10n.formatTimestamp(lastactiontime)
1092              statstr = "Acknowledgment of the message received at " + t + "."
1093          elif status == "broadcastqueued":
1094              statstr = "Broadcast queued."
1095          elif status == "broadcastsent":
1096              t = l10n.formatTimestamp(lastactiontime)
1097              statstr = "Broadcast sent at " + t + "."
1098          elif status == "forcepow":
1099              statstr = "Forced difficulty override. Message will start sending soon."
1100          elif status == "badkey":
1101              statstr = "Warning: Could not encrypt message because the recipient's encryption key is no good."
1102          elif status == "toodifficult":
1103              statstr = "Error: The work demanded by the recipient is more difficult than you are willing to do."
1104          else:
1105              t = l10n.formatTimestamp(lastactiontime)
1106              statstr = "Unknown status " + status + " at " + t + "."
1107  
1108          # Load into array
1109          sentbox.append([
1110              tolabel,
1111              toaddr,
1112              fromlabel,
1113              fromaddr,
1114              subject,
1115              statstr,
1116              ackdata,
1117              l10n.formatTimestamp(lastactiontime)])
1118      sentbox.reverse()
1119  
1120  
1121  def loadAddrBook():
1122      """Load address book"""
1123      sys.stdout = sys.__stdout__
1124      print("Loading address book...")
1125      sys.stdout = printlog
1126  
1127      ret = sqlQuery("SELECT label, address FROM addressbook")
1128      for row in ret:
1129          label, addr = row
1130          label = shared.fixPotentiallyInvalidUTF8Data(label)
1131          addrbook.append([label, addr])
1132      addrbook.reverse()
1133  
1134  
1135  def loadSubscriptions():
1136      """Load subscription functionality"""
1137      ret = sqlQuery("SELECT label, address, enabled FROM subscriptions")
1138      for row in ret:
1139          label, address, enabled = row
1140          subscriptions.append([label, address, enabled])
1141      subscriptions.reverse()
1142  
1143  
1144  def loadBlackWhiteList():
1145      """load black/white list"""
1146      global bwtype
1147      bwtype = config.get("bitmessagesettings", "blackwhitelist")
1148      if bwtype == "black":
1149          ret = sqlQuery("SELECT label, address, enabled FROM blacklist")
1150      else:
1151          ret = sqlQuery("SELECT label, address, enabled FROM whitelist")
1152      for row in ret:
1153          label, address, enabled = row
1154          blacklist.append([label, address, enabled])
1155      blacklist.reverse()
1156  
1157  
1158  def runwrapper():
1159      """Main method"""
1160      sys.stdout = printlog
1161      # sys.stderr = errlog
1162  
1163      loadInbox()
1164      loadSent()
1165      loadAddrBook()
1166      loadSubscriptions()
1167      loadBlackWhiteList()
1168  
1169      stdscr = curses.initscr()
1170  
1171      global logpad
1172      logpad = curses.newpad(1024, curses.COLS)
1173  
1174      stdscr.nodelay(0)
1175      curses.curs_set(0)
1176      stdscr.timeout(1000)
1177  
1178      curses.wrapper(run)
1179      doShutdown()
1180  
1181  
1182  def run(stdscr):
1183      """Main loop"""
1184      # Schedule inventory lookup data
1185      resetlookups()
1186  
1187      # Init color pairs
1188      if curses.has_colors():
1189          curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)       # red
1190          curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)         # green
1191          curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)        # yellow
1192          curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK)      # blue
1193          curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLACK)       # magenta
1194          curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK)      # cyan
1195          curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK)         # white
1196          if curses.can_change_color():
1197              curses.init_color(8, 500, 500, 500)         # gray
1198              curses.init_pair(8, 8, 0)
1199              curses.init_color(9, 844, 465, 0)       # orange
1200              curses.init_pair(9, 9, 0)
1201          else:
1202              curses.init_pair(8, curses.COLOR_WHITE, curses.COLOR_BLACK)         # grayish
1203              curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK)        # orangish
1204  
1205      # Init list of address in 'Your Identities' tab
1206      configSections = config.addresses()
1207      for addressInKeysFile in configSections:
1208          isEnabled = config.getboolean(addressInKeysFile, "enabled")
1209          addresses.append([config.get(addressInKeysFile, "label"), isEnabled, addressInKeysFile])
1210          # Set address color
1211          if not isEnabled:
1212              addresses[len(addresses) - 1].append(8)         # gray
1213          elif config.safeGetBoolean(addressInKeysFile, 'chan'):
1214              addresses[len(addresses) - 1].append(9)         # orange
1215          elif config.safeGetBoolean(addressInKeysFile, 'mailinglist'):
1216              addresses[len(addresses) - 1].append(5)         # magenta
1217          else:
1218              addresses[len(addresses) - 1].append(0)         # black
1219      addresses.reverse()
1220  
1221      stdscr.clear()
1222      redraw(stdscr)
1223      while quit_ is False:
1224          drawtab(stdscr)
1225          handlech(stdscr.getch(), stdscr)
1226  
1227  
1228  def doShutdown():
1229      """Shutting the app down"""
1230      sys.stdout = sys.__stdout__
1231      print("Shutting down...")
1232      sys.stdout = printlog
1233      shutdown.doCleanShutdown()
1234      sys.stdout = sys.__stdout__
1235      sys.stderr = sys.__stderr__
1236      os._exit(0)  # pylint: disable=protected-access