Config.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import argparse
  2. import sys
  3. import os
  4. import locale
  5. import re
  6. import ConfigParser
  7. import logging
  8. import logging.handlers
  9. import stat
  10. class Config(object):
  11. def __init__(self, argv):
  12. self.version = "0.6.5"
  13. self.rev = 3870
  14. self.argv = argv
  15. self.action = None
  16. self.pending_changes = {}
  17. self.need_restart = False
  18. self.keys_api_change_allowed = set([
  19. "tor", "fileserver_port", "language", "tor_use_bridges", "trackers_proxy", "trackers",
  20. "trackers_file", "open_browser", "log_level", "fileserver_ip_type", "ip_external"
  21. ])
  22. self.keys_restart_need = set(["tor", "fileserver_port", "fileserver_ip_type"])
  23. self.start_dir = self.getStartDir()
  24. self.config_file = "zeronet.conf"
  25. self.trackers_file = False
  26. self.createParser()
  27. self.createArguments()
  28. def createParser(self):
  29. # Create parser
  30. self.parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
  31. self.parser.register('type', 'bool', self.strToBool)
  32. self.subparsers = self.parser.add_subparsers(title="Action to perform", dest="action")
  33. def __str__(self):
  34. return str(self.arguments).replace("Namespace", "Config") # Using argparse str output
  35. # Convert string to bool
  36. def strToBool(self, v):
  37. return v.lower() in ("yes", "true", "t", "1")
  38. def getStartDir(self):
  39. this_file = os.path.abspath(__file__).replace("\\", "/").rstrip("cd")
  40. if this_file.endswith("/Contents/Resources/core/src/Config.py"):
  41. # Running as ZeroNet.app
  42. if this_file.startswith("/Application") or this_file.startswith("/private") or this_file.startswith(os.path.expanduser("~/Library")):
  43. # Runnig from non-writeable directory, put data to Application Support
  44. start_dir = os.path.expanduser("~/Library/Application Support/ZeroNet").decode(sys.getfilesystemencoding())
  45. else:
  46. # Running from writeable directory put data next to .app
  47. start_dir = re.sub("/[^/]+/Contents/Resources/core/src/Config.py", "", this_file).decode(sys.getfilesystemencoding())
  48. elif this_file.endswith("/core/src/Config.py"):
  49. # Running as exe or source is at Application Support directory, put var files to outside of core dir
  50. start_dir = this_file.replace("/core/src/Config.py", "").decode(sys.getfilesystemencoding())
  51. elif this_file.endswith("usr/share/zeronet/src/Config.py"):
  52. # Running from non-writeable location, e.g., AppImage
  53. start_dir = os.path.expanduser("~/ZeroNet").decode(sys.getfilesystemencoding())
  54. else:
  55. start_dir = "."
  56. return start_dir
  57. # Create command line arguments
  58. def createArguments(self):
  59. trackers = [
  60. "zero://boot3rdez4rzn36x.onion:15441",
  61. "zero://zero.booth.moe#f36ca555bee6ba216b14d10f38c16f7769ff064e0e37d887603548cc2e64191d:443", # US/NY
  62. "udp://tracker.coppersurfer.tk:6969", # DE
  63. "udp://tracker.port443.xyz:6969", # UK
  64. "udp://104.238.198.186:8000", # US/LA
  65. "http://tracker2.itzmx.com:6961/announce", # US/LA
  66. "http://open.acgnxtracker.com:80/announce", # DE
  67. "http://open.trackerlist.xyz:80/announce", # Cloudflare
  68. "https://1.tracker.eu.org:443/announce", # Google App Engine
  69. "zero://2602:ffc5::c5b2:5360:26312" # US/ATL
  70. ]
  71. # Platform specific
  72. if sys.platform.startswith("win"):
  73. coffeescript = "type %s | tools\\coffee\\coffee.cmd"
  74. else:
  75. coffeescript = None
  76. try:
  77. language, enc = locale.getdefaultlocale()
  78. language = language.lower().replace("_", "-")
  79. if language not in ["pt-br", "zh-tw"]:
  80. language = language.split("-")[0]
  81. except Exception:
  82. language = "en"
  83. use_openssl = True
  84. if repr(1483108852.565) != "1483108852.565": # Fix for weird Android issue
  85. fix_float_decimals = True
  86. else:
  87. fix_float_decimals = False
  88. config_file = self.start_dir + "/zeronet.conf"
  89. data_dir = self.start_dir + "/data"
  90. log_dir = self.start_dir + "/log"
  91. ip_local = ["127.0.0.1", "::1"]
  92. # Main
  93. action = self.subparsers.add_parser("main", help='Start UiServer and FileServer (default)')
  94. # SiteCreate
  95. action = self.subparsers.add_parser("siteCreate", help='Create a new site')
  96. # SiteNeedFile
  97. action = self.subparsers.add_parser("siteNeedFile", help='Get a file from site')
  98. action.add_argument('address', help='Site address')
  99. action.add_argument('inner_path', help='File inner path')
  100. # SiteDownload
  101. action = self.subparsers.add_parser("siteDownload", help='Download a new site')
  102. action.add_argument('address', help='Site address')
  103. # SiteSign
  104. action = self.subparsers.add_parser("siteSign", help='Update and sign content.json: address [privatekey]')
  105. action.add_argument('address', help='Site to sign')
  106. action.add_argument('privatekey', help='Private key (default: ask on execute)', nargs='?')
  107. action.add_argument('--inner_path', help='File you want to sign (default: content.json)',
  108. default="content.json", metavar="inner_path")
  109. action.add_argument('--remove_missing_optional', help='Remove optional files that is not present in the directory', action='store_true')
  110. action.add_argument('--publish', help='Publish site after the signing', action='store_true')
  111. # SitePublish
  112. action = self.subparsers.add_parser("sitePublish", help='Publish site to other peers: address')
  113. action.add_argument('address', help='Site to publish')
  114. action.add_argument('peer_ip', help='Peer ip to publish (default: random peers ip from tracker)',
  115. default=None, nargs='?')
  116. action.add_argument('peer_port', help='Peer port to publish (default: random peer port from tracker)',
  117. default=15441, nargs='?')
  118. action.add_argument('--inner_path', help='Content.json you want to publish (default: content.json)',
  119. default="content.json", metavar="inner_path")
  120. # SiteVerify
  121. action = self.subparsers.add_parser("siteVerify", help='Verify site files using sha512: address')
  122. action.add_argument('address', help='Site to verify')
  123. # SiteCmd
  124. action = self.subparsers.add_parser("siteCmd", help='Execute a ZeroFrame API command on a site')
  125. action.add_argument('address', help='Site address')
  126. action.add_argument('cmd', help='API command name')
  127. action.add_argument('parameters', help='Parameters of the command', nargs='?')
  128. # dbRebuild
  129. action = self.subparsers.add_parser("dbRebuild", help='Rebuild site database cache')
  130. action.add_argument('address', help='Site to rebuild')
  131. # dbQuery
  132. action = self.subparsers.add_parser("dbQuery", help='Query site sql cache')
  133. action.add_argument('address', help='Site to query')
  134. action.add_argument('query', help='Sql query')
  135. # PeerPing
  136. action = self.subparsers.add_parser("peerPing", help='Send Ping command to peer')
  137. action.add_argument('peer_ip', help='Peer ip')
  138. action.add_argument('peer_port', help='Peer port', nargs='?')
  139. # PeerGetFile
  140. action = self.subparsers.add_parser("peerGetFile", help='Request and print a file content from peer')
  141. action.add_argument('peer_ip', help='Peer ip')
  142. action.add_argument('peer_port', help='Peer port')
  143. action.add_argument('site', help='Site address')
  144. action.add_argument('filename', help='File name to request')
  145. action.add_argument('--benchmark', help='Request file 10x then displays the total time', action='store_true')
  146. # PeerCmd
  147. action = self.subparsers.add_parser("peerCmd", help='Request and print a file content from peer')
  148. action.add_argument('peer_ip', help='Peer ip')
  149. action.add_argument('peer_port', help='Peer port')
  150. action.add_argument('cmd', help='Command to execute')
  151. action.add_argument('parameters', help='Parameters to command', nargs='?')
  152. # CryptSign
  153. action = self.subparsers.add_parser("cryptSign", help='Sign message using Bitcoin private key')
  154. action.add_argument('message', help='Message to sign')
  155. action.add_argument('privatekey', help='Private key')
  156. # Crypt Verify
  157. action = self.subparsers.add_parser("cryptVerify", help='Verify message using Bitcoin public address')
  158. action.add_argument('message', help='Message to verify')
  159. action.add_argument('sign', help='Signiture for message')
  160. action.add_argument('address', help='Signer\'s address')
  161. # Crypt GetPrivatekey
  162. action = self.subparsers.add_parser("cryptGetPrivatekey", help='Generate a privatekey from master seed')
  163. action.add_argument('master_seed', help='Source master seed')
  164. action.add_argument('site_address_index', help='Site address index', type=int)
  165. action = self.subparsers.add_parser("getConfig", help='Return json-encoded info')
  166. action = self.subparsers.add_parser("testConnection", help='Testing')
  167. action = self.subparsers.add_parser("testAnnounce", help='Testing')
  168. # Config parameters
  169. self.parser.add_argument('--verbose', help='More detailed logging', action='store_true')
  170. self.parser.add_argument('--debug', help='Debug mode', action='store_true')
  171. self.parser.add_argument('--silent', help='Disable logging to terminal output', action='store_true')
  172. self.parser.add_argument('--debug_socket', help='Debug socket connections', action='store_true')
  173. self.parser.add_argument('--batch', help="Batch mode (No interactive input for commands)", action='store_true')
  174. self.parser.add_argument('--config_file', help='Path of config file', default=config_file, metavar="path")
  175. self.parser.add_argument('--data_dir', help='Path of data directory', default=data_dir, metavar="path")
  176. self.parser.add_argument('--log_dir', help='Path of logging directory', default=log_dir, metavar="path")
  177. self.parser.add_argument('--log_level', help='Level of logging to file', default="DEBUG", choices=["DEBUG", "INFO", "ERROR"])
  178. self.parser.add_argument('--log_rotate', help='Log rotate interval', default="daily", choices=["hourly", "daily", "weekly", "off"])
  179. self.parser.add_argument('--log_rotate_backup_count', help='Log rotate backup count', default=5, type=int)
  180. self.parser.add_argument('--language', help='Web interface language', default=language, metavar='language')
  181. self.parser.add_argument('--ui_ip', help='Web interface bind address', default="127.0.0.1", metavar='ip')
  182. self.parser.add_argument('--ui_port', help='Web interface bind port', default=43110, type=int, metavar='port')
  183. self.parser.add_argument('--ui_restrict', help='Restrict web access', default=False, metavar='ip', nargs='*')
  184. self.parser.add_argument('--ui_host', help='Allow access using this hosts', metavar='host', nargs='*')
  185. self.parser.add_argument('--ui_trans_proxy', help='Allow access using a transparent proxy', action='store_true')
  186. self.parser.add_argument('--open_browser', help='Open homepage in web browser automatically',
  187. nargs='?', const="default_browser", metavar='browser_name')
  188. self.parser.add_argument('--homepage', help='Web interface Homepage', default='1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D',
  189. metavar='address')
  190. self.parser.add_argument('--updatesite', help='Source code update site', default='1UPDatEDxnvHDo7TXvq6AEBARfNkyfxsp',
  191. metavar='address')
  192. self.parser.add_argument('--size_limit', help='Default site size limit in MB', default=10, type=int, metavar='limit')
  193. self.parser.add_argument('--file_size_limit', help='Maximum per file size limit in MB', default=10, type=int, metavar='limit')
  194. self.parser.add_argument('--connected_limit', help='Max connected peer per site', default=8, type=int, metavar='connected_limit')
  195. self.parser.add_argument('--global_connected_limit', help='Max connections', default=512, type=int, metavar='global_connected_limit')
  196. self.parser.add_argument('--workers', help='Download workers per site', default=5, type=int, metavar='workers')
  197. self.parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip')
  198. self.parser.add_argument('--fileserver_port', help='FileServer bind port (0: randomize)', default=0, type=int, metavar='port')
  199. self.parser.add_argument('--fileserver_port_range', help='FileServer randomization range', default="10000-40000", metavar='port')
  200. self.parser.add_argument('--fileserver_ip_type', help='FileServer ip type', default="dual", choices=["ipv4", "ipv6", "dual"])
  201. self.parser.add_argument('--ip_local', help='My local ips', default=ip_local, type=int, metavar='ip', nargs='*')
  202. self.parser.add_argument('--ip_external', help='Set reported external ip (tested on start if None)', metavar='ip', nargs='*')
  203. self.parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true')
  204. self.parser.add_argument('--proxy', help='Socks proxy address', metavar='ip:port')
  205. self.parser.add_argument('--bind', help='Bind outgoing sockets to this address', metavar='ip')
  206. self.parser.add_argument('--trackers', help='Bootstraping torrent trackers', default=trackers, metavar='protocol://address', nargs='*')
  207. self.parser.add_argument('--trackers_file', help='Load torrent trackers dynamically from a file', default=False, metavar='path')
  208. self.parser.add_argument('--trackers_proxy', help='Force use proxy to connect to trackers (disable, tor, ip:port)', default="disable")
  209. self.parser.add_argument('--use_openssl', help='Use OpenSSL liblary for speedup',
  210. type='bool', choices=[True, False], default=use_openssl)
  211. self.parser.add_argument('--disable_db', help='Disable database updating', action='store_true')
  212. self.parser.add_argument('--disable_encryption', help='Disable connection encryption', action='store_true')
  213. self.parser.add_argument('--force_encryption', help="Enforce encryption to all peer connections", action='store_true')
  214. self.parser.add_argument('--disable_sslcompression', help='Disable SSL compression to save memory',
  215. type='bool', choices=[True, False], default=True)
  216. self.parser.add_argument('--keep_ssl_cert', help='Disable new SSL cert generation on startup', action='store_true')
  217. self.parser.add_argument('--max_files_opened', help='Change maximum opened files allowed by OS to this value on startup',
  218. default=2048, type=int, metavar='limit')
  219. self.parser.add_argument('--stack_size', help='Change thread stack size', default=None, type=int, metavar='thread_stack_size')
  220. self.parser.add_argument('--use_tempfiles', help='Use temporary files when downloading (experimental)',
  221. type='bool', choices=[True, False], default=False)
  222. self.parser.add_argument('--stream_downloads', help='Stream download directly to files (experimental)',
  223. type='bool', choices=[True, False], default=False)
  224. self.parser.add_argument("--msgpack_purepython", help='Use less memory, but a bit more CPU power',
  225. type='bool', choices=[True, False], default=False)
  226. self.parser.add_argument("--fix_float_decimals", help='Fix content.json modification date float precision on verification',
  227. type='bool', choices=[True, False], default=fix_float_decimals)
  228. self.parser.add_argument("--db_mode", choices=["speed", "security"], default="speed")
  229. self.parser.add_argument("--download_optional", choices=["manual", "auto"], default="manual")
  230. self.parser.add_argument('--coffeescript_compiler', help='Coffeescript compiler for developing', default=coffeescript,
  231. metavar='executable_path')
  232. self.parser.add_argument('--tor', help='enable: Use only for Tor peers, always: Use Tor for every connection', choices=["disable", "enable", "always"], default='enable')
  233. self.parser.add_argument('--tor_controller', help='Tor controller address', metavar='ip:port', default='127.0.0.1:9051')
  234. self.parser.add_argument('--tor_proxy', help='Tor proxy address', metavar='ip:port', default='127.0.0.1:9050')
  235. self.parser.add_argument('--tor_password', help='Tor controller password', metavar='password')
  236. self.parser.add_argument('--tor_use_bridges', help='Use obfuscated bridge relays to avoid Tor block', action='store_true')
  237. self.parser.add_argument('--tor_hs_limit', help='Maximum number of hidden services in Tor always mode', metavar='limit', type=int, default=10)
  238. self.parser.add_argument('--tor_hs_port', help='Hidden service port in Tor always mode', metavar='limit', type=int, default=15441)
  239. self.parser.add_argument('--version', action='version', version='ZeroNet %s r%s' % (self.version, self.rev))
  240. self.parser.add_argument('--end', help='Stop multi value argument parsing', action='store_true')
  241. return self.parser
  242. def loadTrackersFile(self):
  243. if not self.trackers_file:
  244. return None
  245. self.trackers = self.arguments.trackers[:]
  246. try:
  247. if self.trackers_file.startswith("/"): # Absolute
  248. trackers_file_path = self.trackers_file
  249. elif self.trackers_file.startswith("{data_dir}"): # Relative to data_dir
  250. trackers_file_path = self.trackers_file.replace("{data_dir}", self.data_dir)
  251. else: # Relative to zeronet.py
  252. trackers_file_path = self.start_dir + "/" + self.trackers_file
  253. for line in open(trackers_file_path):
  254. tracker = line.strip()
  255. if "://" in tracker and tracker not in self.trackers:
  256. self.trackers.append(tracker)
  257. except Exception as err:
  258. print "Error loading trackers file: %s" % err
  259. # Find arguments specified for current action
  260. def getActionArguments(self):
  261. back = {}
  262. arguments = self.parser._subparsers._group_actions[0].choices[self.action]._actions[1:] # First is --version
  263. for argument in arguments:
  264. back[argument.dest] = getattr(self, argument.dest)
  265. return back
  266. # Try to find action from argv
  267. def getAction(self, argv):
  268. actions = [action.choices.keys() for action in self.parser._actions if action.dest == "action"][0] # Valid actions
  269. found_action = False
  270. for action in actions: # See if any in argv
  271. if action in argv:
  272. found_action = action
  273. break
  274. return found_action
  275. # Move plugin parameters to end of argument list
  276. def moveUnknownToEnd(self, argv, default_action):
  277. valid_actions = sum([action.option_strings for action in self.parser._actions], [])
  278. valid_parameters = []
  279. plugin_parameters = []
  280. plugin = False
  281. for arg in argv:
  282. if arg.startswith("--"):
  283. if arg not in valid_actions:
  284. plugin = True
  285. else:
  286. plugin = False
  287. elif arg == default_action:
  288. plugin = False
  289. if plugin:
  290. plugin_parameters.append(arg)
  291. else:
  292. valid_parameters.append(arg)
  293. return valid_parameters + plugin_parameters
  294. # Parse arguments from config file and command line
  295. def parse(self, silent=False, parse_config=True):
  296. if silent: # Don't display messages or quit on unknown parameter
  297. original_print_message = self.parser._print_message
  298. original_exit = self.parser.exit
  299. def silencer(parser, function_name):
  300. parser.exited = True
  301. return None
  302. self.parser.exited = False
  303. self.parser._print_message = lambda *args, **kwargs: silencer(self.parser, "_print_message")
  304. self.parser.exit = lambda *args, **kwargs: silencer(self.parser, "exit")
  305. argv = self.argv[:] # Copy command line arguments
  306. self.parseCommandline(argv, silent) # Parse argv
  307. self.setAttributes()
  308. if parse_config:
  309. argv = self.parseConfig(argv) # Add arguments from config file
  310. self.parseCommandline(argv, silent) # Parse argv
  311. self.setAttributes()
  312. if not silent:
  313. if self.fileserver_ip != "*" and self.fileserver_ip not in self.ip_local:
  314. self.ip_local.append(self.fileserver_ip)
  315. if silent: # Restore original functions
  316. if self.parser.exited and self.action == "main": # Argument parsing halted, don't start ZeroNet with main action
  317. self.action = None
  318. self.parser._print_message = original_print_message
  319. self.parser.exit = original_exit
  320. self.loadTrackersFile()
  321. # Parse command line arguments
  322. def parseCommandline(self, argv, silent=False):
  323. # Find out if action is specificed on start
  324. action = self.getAction(argv)
  325. if not action:
  326. argv.append("--end")
  327. argv.append("main")
  328. action = "main"
  329. argv = self.moveUnknownToEnd(argv, action)
  330. if silent:
  331. res = self.parser.parse_known_args(argv[1:])
  332. if res:
  333. self.arguments = res[0]
  334. else:
  335. self.arguments = {}
  336. else:
  337. self.arguments = self.parser.parse_args(argv[1:])
  338. # Parse config file
  339. def parseConfig(self, argv):
  340. # Find config file path from parameters
  341. if "--config_file" in argv:
  342. self.config_file = argv[argv.index("--config_file") + 1]
  343. # Load config file
  344. if os.path.isfile(self.config_file):
  345. config = ConfigParser.ConfigParser(allow_no_value=True)
  346. config.read(self.config_file)
  347. for section in config.sections():
  348. for key, val in config.items(section):
  349. if val == "True":
  350. val = None
  351. if section != "global": # If not global prefix key with section
  352. key = section + "_" + key
  353. if key == "open_browser": # Prefer config file value over cli argument
  354. if "--%s" % key in argv:
  355. pos = argv.index("--open_browser")
  356. del argv[pos:pos + 2]
  357. argv_extend = ["--%s" % key]
  358. if val:
  359. for line in val.strip().split("\n"): # Allow multi-line values
  360. argv_extend.append(line)
  361. if "\n" in val:
  362. argv_extend.append("--end")
  363. argv = argv[:1] + argv_extend + argv[1:]
  364. return argv
  365. # Expose arguments as class attributes
  366. def setAttributes(self):
  367. # Set attributes from arguments
  368. if self.arguments:
  369. args = vars(self.arguments)
  370. for key, val in args.items():
  371. if type(val) is list:
  372. val = val[:]
  373. if key in ("data_dir", "log_dir"):
  374. val = val.replace("\\", "/")
  375. setattr(self, key, val)
  376. def loadPlugins(self):
  377. from Plugin import PluginManager
  378. @PluginManager.acceptPlugins
  379. class ConfigPlugin(object):
  380. def __init__(self, config):
  381. self.parser = config.parser
  382. self.createArguments()
  383. def createArguments(self):
  384. pass
  385. ConfigPlugin(self)
  386. def saveValue(self, key, value):
  387. if not os.path.isfile(self.config_file):
  388. content = ""
  389. else:
  390. content = open(self.config_file).read()
  391. lines = content.splitlines()
  392. global_line_i = None
  393. key_line_i = None
  394. i = 0
  395. for line in lines:
  396. if line.strip() == "[global]":
  397. global_line_i = i
  398. if line.startswith(key + " ="):
  399. key_line_i = i
  400. i += 1
  401. if key_line_i and len(lines) > key_line_i + 1:
  402. while True: # Delete previous multiline values
  403. is_value_line = lines[key_line_i + 1].startswith(" ") or lines[key_line_i + 1].startswith("\t")
  404. if not is_value_line:
  405. break
  406. del lines[key_line_i + 1]
  407. if value is None: # Delete line
  408. if key_line_i:
  409. del lines[key_line_i]
  410. else: # Add / update
  411. if type(value) is list:
  412. value_lines = [""] + [str(line).replace("\n", "").replace("\r", "") for line in value]
  413. else:
  414. value_lines = [str(value).replace("\n", "").replace("\r", "")]
  415. new_line = "%s = %s" % (key, "\n ".join(value_lines))
  416. if key_line_i: # Already in the config, change the line
  417. lines[key_line_i] = new_line
  418. elif global_line_i is None: # No global section yet, append to end of file
  419. lines.append("[global]")
  420. lines.append(new_line)
  421. else: # Has global section, append the line after it
  422. lines.insert(global_line_i + 1, new_line)
  423. open(self.config_file, "w").write("\n".join(lines))
  424. def getServerInfo(self):
  425. from Plugin import PluginManager
  426. info = {
  427. "platform": sys.platform,
  428. "fileserver_ip": self.fileserver_ip,
  429. "fileserver_port": self.fileserver_port,
  430. "ui_ip": self.ui_ip,
  431. "ui_port": self.ui_port,
  432. "version": self.version,
  433. "rev": self.rev,
  434. "language": self.language,
  435. "debug": self.debug,
  436. "plugins": PluginManager.plugin_manager.plugin_names,
  437. "log_dir": os.path.abspath(self.log_dir),
  438. "data_dir": os.path.abspath(self.data_dir),
  439. "src_dir": os.path.dirname(os.path.abspath(__file__))
  440. }
  441. try:
  442. info["ip_external"] = sys.modules["main"].file_server.port_opened
  443. info["tor_enabled"] = sys.modules["main"].file_server.tor_manager.enabled
  444. info["tor_status"] = sys.modules["main"].file_server.tor_manager.status
  445. except:
  446. pass
  447. return info
  448. def initConsoleLogger(self):
  449. if self.action == "main":
  450. format = '[%(asctime)s] %(name)s %(message)s'
  451. else:
  452. format = '%(name)s %(message)s'
  453. if self.silent:
  454. level = logging.ERROR
  455. elif self.debug:
  456. level = logging.DEBUG
  457. else:
  458. level = logging.INFO
  459. console_logger = logging.StreamHandler()
  460. console_logger.setFormatter(logging.Formatter(format, "%H:%M:%S"))
  461. console_logger.setLevel(level)
  462. logging.getLogger('').addHandler(console_logger)
  463. def initFileLogger(self):
  464. if self.action == "main":
  465. log_file_path = "%s/debug.log" % self.log_dir
  466. else:
  467. log_file_path = "%s/cmd.log" % self.log_dir
  468. if self.log_rotate == "off":
  469. file_logger = logging.FileHandler(log_file_path)
  470. else:
  471. when_names = {"weekly": "w", "daily": "d", "hourly": "h"}
  472. file_logger = logging.handlers.TimedRotatingFileHandler(
  473. log_file_path, when=when_names[self.log_rotate], interval=1, backupCount=self.log_rotate_backup_count
  474. )
  475. file_logger.doRollover() # Always start with empty log file
  476. file_logger.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)-8s %(name)s %(message)s'))
  477. file_logger.setLevel(logging.getLevelName(self.log_level))
  478. logging.getLogger('').setLevel(logging.getLevelName(self.log_level))
  479. logging.getLogger('').addHandler(file_logger)
  480. def initLogging(self):
  481. # Create necessary files and dirs
  482. if not os.path.isdir(self.log_dir):
  483. os.mkdir(self.log_dir)
  484. try:
  485. os.chmod(self.log_dir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
  486. except Exception as err:
  487. print "Can't change permission of %s: %s" % (self.log_dir, err)
  488. # Make warning hidden from console
  489. logging.WARNING = 15 # Don't display warnings if not in debug mode
  490. logging.addLevelName(15, "WARNING")
  491. logging.getLogger('').name = "-" # Remove root prefix
  492. self.initConsoleLogger()
  493. self.initFileLogger()
  494. config = Config(sys.argv)