Machine.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # Copyright (C) 2007-2018 John Goerzen & contributors.
  2. #
  3. # This program is free software; you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation; either version 2 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. from urllib.parse import urlencode
  17. import sys
  18. import time
  19. import logging
  20. from threading import currentThread
  21. import offlineimap
  22. from offlineimap.ui.UIBase import UIBase
  23. protocol = '7.2.0'
  24. class MachineLogFormatter(logging.Formatter):
  25. """urlencodes any outputted line, to avoid multi-line output"""
  26. def format(self, record):
  27. # Mapping of log levels to historic tag names
  28. severity_map = {
  29. 'info': 'msg',
  30. 'warning': 'warn',
  31. }
  32. line = super(MachineLogFormatter, self).format(record)
  33. severity = record.levelname.lower()
  34. if severity in severity_map:
  35. severity = severity_map[severity]
  36. if hasattr(record, "machineui"):
  37. command = record.machineui["command"]
  38. whoami = record.machineui["id"]
  39. else:
  40. command = ""
  41. whoami = currentThread().getName()
  42. prefix = "%s:%s" % (command, urlencode([('', whoami)])[1:])
  43. return "%s:%s:%s" % (severity, prefix, urlencode([('', line)])[1:])
  44. class MachineUI(UIBase):
  45. def __init__(self, config, loglevel=logging.INFO):
  46. super(MachineUI, self).__init__(config, loglevel)
  47. self._log_con_handler.createLock()
  48. """lock needed to block on password input"""
  49. # Set up the formatter that urlencodes the strings...
  50. self._log_con_handler.setFormatter(MachineLogFormatter())
  51. # Arguments:
  52. # - handler: must be method from self.logger that reflects
  53. # the severity of the passed message
  54. # - command: command that produced this message
  55. # - msg: the message itself
  56. def _printData(self, handler, command, msg):
  57. handler(msg,
  58. extra={
  59. 'machineui': {
  60. 'command': command,
  61. 'id': currentThread().getName(),
  62. }
  63. })
  64. def _msg(self, msg):
  65. self._printData(self.logger.info, '_display', msg)
  66. def warn(self, msg, minor=0):
  67. # TODO, remove and cleanup the unused minor stuff
  68. self._printData(self.logger.warning, '', msg)
  69. def registerthread(self, account):
  70. super(MachineUI, self).registerthread(account)
  71. self._printData(self.logger.info, 'registerthread', account)
  72. def unregisterthread(self, thread):
  73. UIBase.unregisterthread(self, thread)
  74. self._printData(self.logger.info, 'unregisterthread', thread.getName())
  75. def debugging(self, debugtype):
  76. self._printData(self.logger.debug, 'debugging', debugtype)
  77. def acct(self, accountname):
  78. self._printData(self.logger.info, 'acct', accountname)
  79. def acctdone(self, accountname):
  80. self._printData(self.logger.info, 'acctdone', accountname)
  81. def validityproblem(self, folder):
  82. self._printData(self.logger.warning, 'validityproblem', "%s\n%s\n%s\n%s" %
  83. (folder.getname(), folder.getrepository().getname(),
  84. folder.get_saveduidvalidity(), folder.get_uidvalidity()))
  85. def connecting(self, reposname, hostname, port):
  86. self._printData(self.logger.info, 'connecting', "%s\n%s\n%s" % (hostname,
  87. str(port), reposname))
  88. def syncfolders(self, srcrepos, destrepos):
  89. self._printData(self.logger.info, 'syncfolders', "%s\n%s" % (self.getnicename(srcrepos),
  90. self.getnicename(destrepos)))
  91. def syncingfolder(self, srcrepos, srcfolder, destrepos, destfolder):
  92. self._printData(self.logger.info, 'syncingfolder', "%s\n%s\n%s\n%s\n" %
  93. (self.getnicename(srcrepos), srcfolder.getname(),
  94. self.getnicename(destrepos), destfolder.getname()))
  95. def loadmessagelist(self, repos, folder):
  96. self._printData(self.logger.info, 'loadmessagelist', "%s\n%s" % (self.getnicename(repos),
  97. folder.getvisiblename()))
  98. def messagelistloaded(self, repos, folder, count):
  99. self._printData(self.logger.info, 'messagelistloaded', "%s\n%s\n%d" %
  100. (self.getnicename(repos), folder.getname(), count))
  101. def syncingmessages(self, sr, sf, dr, df):
  102. self._printData(self.logger.info, 'syncingmessages', "%s\n%s\n%s\n%s\n" %
  103. (self.getnicename(sr), sf.getname(), self.getnicename(dr),
  104. df.getname()))
  105. def ignorecopyingmessage(self, uid, srcfolder, destfolder):
  106. self._printData(self.logger.info, 'ignorecopyingmessage', "%d\n%s\n%s\n%s[%s]" %
  107. (uid, self.getnicename(srcfolder), srcfolder.getname(),
  108. self.getnicename(destfolder), destfolder))
  109. def copyingmessage(self, uid, num, num_to_copy, srcfolder, destfolder):
  110. self._printData(self.logger.info, 'copyingmessage', "%d\n%s\n%s\n%s[%s]" %
  111. (uid, self.getnicename(srcfolder), srcfolder.getname(),
  112. self.getnicename(destfolder), destfolder))
  113. def folderlist(self, ulist):
  114. return "\f".join(["%s\t%s" % (self.getnicename(x), x.getname()) for x in ulist])
  115. def uidlist(self, ulist):
  116. return "\f".join([str(u) for u in ulist])
  117. def deletingmessages(self, uidlist, destlist):
  118. ds = self.folderlist(destlist)
  119. self._printData(self.logger.info, 'deletingmessages', "%s\n%s" % (self.uidlist(uidlist), ds))
  120. def addingflags(self, uidlist, flags, dest):
  121. self._printData(self.logger.info, "addingflags", "%s\n%s\n%s" % (self.uidlist(uidlist),
  122. "\f".join(flags),
  123. dest))
  124. def deletingflags(self, uidlist, flags, dest):
  125. self._printData(self.logger.info, 'deletingflags', "%s\n%s\n%s" % (self.uidlist(uidlist),
  126. "\f".join(flags),
  127. dest))
  128. def threadException(self, thread):
  129. self._printData(self.logger.warning, 'threadException', "%s\n%s" %
  130. (thread.getName(), self.getThreadExceptionString(thread)))
  131. self.delThreadDebugLog(thread)
  132. self.terminate(100)
  133. def terminate(self, exitstatus=0, errortitle='', errormsg=''):
  134. self._printData(self.logger.info, 'terminate', "%d\n%s\n%s" % (exitstatus, errortitle, errormsg))
  135. sys.exit(exitstatus)
  136. def mainException(self):
  137. self._printData(self.logger.warning, 'mainException', self.getMainExceptionString())
  138. def threadExited(self, thread):
  139. self._printData(self.logger.info, 'threadExited', thread.getName())
  140. UIBase.threadExited(self, thread)
  141. def sleeping(self, sleepsecs, remainingsecs):
  142. self._printData(self.logger.info, 'sleeping', "%d\n%d" % (sleepsecs, remainingsecs))
  143. if sleepsecs > 0:
  144. time.sleep(sleepsecs)
  145. return 0
  146. def getpass(self, username, config, errmsg=None):
  147. if errmsg:
  148. self._printData(self.logger.warning,
  149. 'getpasserror', "%s\n%s" % (username, errmsg),
  150. False)
  151. self._log_con_handler.acquire() # lock the console output
  152. try:
  153. self._printData(self.logger.info, 'getpass', username)
  154. return sys.stdin.readline()[:-1]
  155. finally:
  156. self._log_con_handler.release()
  157. def init_banner(self):
  158. self._printData(self.logger.info, 'protocol', protocol)
  159. self._printData(self.logger.info, 'initbanner', offlineimap.banner)
  160. def callhook(self, msg):
  161. self._printData(self.logger.info, 'callhook', msg)