mirror of
				https://github.com/Py-KMS-Organization/py-kms.git
				synced 2025-10-30 19:33:30 +00:00 
			
		
		
		
	py-kms Gui: matched all terminal options so far done; added modes onlyserver / only client
Introduced option for asynchronous messages Slimmed down parsing process with improved errors catching
This commit is contained in:
		| @@ -592,8 +592,8 @@ def main(): | ||||
|         parser = Etrigan_parser() | ||||
|         args = vars(parser.parse_args()) | ||||
|         # Check arguments. | ||||
|         Etrigan_check().checkfile(args['etriganpid'], 'pidfile', '.pid') | ||||
|         Etrigan_check().checkfile(args['etriganlog'], 'pidfile', '.log') | ||||
|         Etrigan_check().checkfile(args['etriganpid'], '--etrigan-pid', '.pid') | ||||
|         Etrigan_check().checkfile(args['etriganlog'], '--etrigan-log', '.log') | ||||
|  | ||||
|         # Setup daemon. | ||||
|         jasonblood_1 = Etrigan(pidfile = args['etriganpid'], logfile = args['etriganlog'], loglevel = args['etriganlev'], | ||||
|   | ||||
| @@ -140,21 +140,21 @@ class kmsBase: | ||||
|                 # https://docs.microsoft.com/en-us/windows/deployment/volume-activation/activate-windows-10-clients-vamt                 | ||||
|                 MinClients = kmsRequest['requiredClientCount']  | ||||
|                 RequiredClients = MinClients * 2 | ||||
|                 if self.srv_config["CurrentClientCount"] != None: | ||||
|                         if 0 < self.srv_config["CurrentClientCount"] < MinClients: | ||||
|                 if self.srv_config["clientcount"] != None: | ||||
|                         if 0 < self.srv_config["clientcount"] < MinClients: | ||||
|                                 # fixed to 6 (product server) or 26 (product desktop) | ||||
|                                 currentClientCount = MinClients + 1 | ||||
|                                 pretty_printer(log_obj = loggersrv.warning, | ||||
|                                                put_text = "{reverse}{yellow}{bold}Not enough clients ! Fixed with %s, but activated client \ | ||||
| could be detected as not genuine !{end}" %currentClientCount) | ||||
|                         elif MinClients <= self.srv_config["CurrentClientCount"] < RequiredClients: | ||||
|                                 currentClientCount = self.srv_config["CurrentClientCount"] | ||||
|                         elif MinClients <= self.srv_config["clientcount"] < RequiredClients: | ||||
|                                 currentClientCount = self.srv_config["clientcount"] | ||||
|                                 pretty_printer(log_obj = loggersrv.warning, | ||||
|                                                put_text = "{reverse}{yellow}{bold}With count = %s, activated client could be detected as not genuine !{end}" %currentClientCount) | ||||
|                         elif self.srv_config["CurrentClientCount"] >= RequiredClients: | ||||
|                         elif self.srv_config["clientcount"] >= RequiredClients: | ||||
|                                 # fixed to 10 (product server) or 50 (product desktop) | ||||
|                                 currentClientCount = RequiredClients | ||||
|                                 if self.srv_config["CurrentClientCount"] > RequiredClients: | ||||
|                                 if self.srv_config["clientcount"] > RequiredClients: | ||||
|                                         pretty_printer(log_obj = loggersrv.warning, | ||||
|                                                        put_text = "{reverse}{yellow}{bold}Too many clients ! Fixed with %s{end}" %currentClientCount) | ||||
|                 else: | ||||
| @@ -230,8 +230,8 @@ could be detected as not genuine !{end}" %currentClientCount) | ||||
|                 # rule: timeserver - 4h <= timeclient <= timeserver + 4h, check if is satisfied. | ||||
|                 response['responseTime'] = kmsRequest['requestTime']  | ||||
|                 response['currentClientCount'] = currentClientCount | ||||
|                 response['vLActivationInterval'] = self.srv_config["VLActivationInterval"] | ||||
|                 response['vLRenewalInterval'] = self.srv_config["VLRenewalInterval"] | ||||
|                 response['vLActivationInterval'] = self.srv_config["activation"] | ||||
|                 response['vLRenewalInterval'] = self.srv_config["renewal"] | ||||
|  | ||||
|                 if self.srv_config['sqlite'] and self.srv_config['dbSupport']: | ||||
|                         response = sql_update_epid(self.dbName, kmsRequest, response) | ||||
|   | ||||
| @@ -24,6 +24,7 @@ from pykms_RpcBase import rpcBase | ||||
| from pykms_DB2Dict import kmsDB2Dict | ||||
| from pykms_Misc import check_setup | ||||
| from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp | ||||
| from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals | ||||
| from pykms_Format import justify, byterize, enco, deco, pretty_printer | ||||
|  | ||||
| clt_version             = "py-kms_2020-02-02" | ||||
| @@ -56,8 +57,10 @@ clt_options = { | ||||
|                   'choi' : ["WindowsVista","Windows7","Windows8","Windows8.1","Windows10","Office2010","Office2013","Office2016","Office2019"]}, | ||||
|         'cmid' : {'help' : 'Use this flag to manually specify a CMID to use. If no CMID is specified, a random CMID will be generated.', | ||||
|                   'def' : None, 'des' : "cmid"}, | ||||
|         'name' : {'help' : 'Use this flag to manually specify an ASCII machineName to use. If no machineName is specified a random machineName \ | ||||
| will be generated.', 'def' : None, 'des' : "machineName"}, | ||||
|         'name' : {'help' : 'Use this flag to manually specify an ASCII machine name to use. If no machine name is specified a random one \ | ||||
| will be generated.', 'def' : None, 'des' : "machine"}, | ||||
|         'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Desactivated by default.', | ||||
|                       'def' : False, 'des' : "asyncmsg"}, | ||||
|         'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel", | ||||
|                     'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]}, | ||||
|         'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logclient.log\". Type \"STDOUT\" to view \ | ||||
| @@ -67,9 +70,6 @@ log info on stdout. Type \"FILESTDOUT\" to combine previous actions.', | ||||
|         } | ||||
|  | ||||
| def client_options(): | ||||
|         try: | ||||
|                 client_parser = KmsParser(description = clt_description, epilog = 'version: ' + clt_version, add_help = False, allow_abbrew = False) | ||||
|         except TypeError: | ||||
|         client_parser = KmsParser(description = clt_description, epilog = 'version: ' + clt_version, add_help = False) | ||||
|         client_parser.add_argument("ip", nargs = "?", action = "store", default = clt_options['ip']['def'], | ||||
|                                    help = clt_options['ip']['help'], type = str) | ||||
| @@ -81,6 +81,8 @@ def client_options(): | ||||
|                                    help = clt_options['cmid']['help'], type = str) | ||||
|         client_parser.add_argument("-n", "--name", dest = clt_options['name']['des'] , default = clt_options['name']['def'], | ||||
|                                    help = clt_options['name']['help'], type = str) | ||||
|         client_parser.add_argument("-y", "--async-msg", action = "store_true", dest = clt_options['asyncmsg']['des'], | ||||
|                                    default = clt_options['asyncmsg']['def'], help = clt_options['asyncmsg']['help']) | ||||
|         client_parser.add_argument("-V", "--loglevel", dest = clt_options['llevel']['des'], action = "store", | ||||
|                                    choices = clt_options['llevel']['choi'], default = clt_options['llevel']['def'], | ||||
|                                    help = clt_options['llevel']['help'], type = str) | ||||
| @@ -88,14 +90,25 @@ def client_options(): | ||||
|                                    default = clt_options['lfile']['def'], help = clt_options['lfile']['help'], type = str) | ||||
|         client_parser.add_argument("-S", "--logsize", dest = clt_options['lsize']['des'], action = "store", | ||||
|                                    default = clt_options['lsize']['def'], help = clt_options['lsize']['help'], type = float) | ||||
|  | ||||
|         client_parser.add_argument("-h", "--help", action = "help", help = "show this help message and exit") | ||||
|  | ||||
|         try: | ||||
|                 if "-h" in sys.argv[1:]: | ||||
|                 userarg = sys.argv[1:] | ||||
|  | ||||
|                 # Run help. | ||||
|                 if any(arg in ["-h", "--help"] for arg in userarg): | ||||
|                         KmsParserHelp().printer(parsers = [client_parser]) | ||||
|                 clt_config.update(vars(client_parser.parse_args())) | ||||
|  | ||||
|                 # Get stored arguments. | ||||
|                 pykmsclt_zeroarg, pykmsclt_onearg = kms_parser_get(client_parser) | ||||
|                 # Update pykms options for dict client config. | ||||
|                 kms_parser_check_optionals(userarg, pykmsclt_zeroarg, pykmsclt_onearg, msg = 'optional py-kms client', | ||||
|                                            exclude_opt_len = ['-F', '--logfile']) | ||||
|                 kms_parser_check_positionals(clt_config, client_parser.parse_args, msg = 'positional py-kms client') | ||||
|  | ||||
|         except KmsParserException as e: | ||||
|                 pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True) | ||||
|                 pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True, where = "clt") | ||||
|  | ||||
| def client_check(): | ||||
|         # Setup and some checks. | ||||
| @@ -107,18 +120,22 @@ def client_check(): | ||||
|                         uuid.UUID(clt_config['cmid']) | ||||
|                 except ValueError: | ||||
|                         pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", | ||||
|                                        put_text = "{reverse}{red}{bold}Bad CMID. Exiting...{end}") | ||||
|         # Check machineName. | ||||
|         if clt_config['machineName'] is not None: | ||||
|                                        put_text = "{reverse}{red}{bold}argument `-c/--cmid`: invalid with: '%s'. Exiting...{end}" %clt_config['cmid']) | ||||
|  | ||||
|         # Check machine name. | ||||
|         if clt_config['machine'] is not None: | ||||
|                 try: | ||||
|                         clt_config['machineName'].encode('utf-16le') | ||||
|                         clt_config['machine'].encode('utf-16le') | ||||
|  | ||||
|                         if len(clt_config['machine']) < 2: | ||||
|                                 pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", | ||||
|                                                put_text = "{reverse}{red}{bold}argument `-n/--name`: too short (required 2 - 63 chars). Exiting...{end}") | ||||
|                         elif len(clt_config['machine']) > 63: | ||||
|                                 pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", | ||||
|                                                put_text = "{reverse}{red}{bold}argument `-n/--name`: too long (required 2 - 63 chars). Exiting...{end}") | ||||
|                 except UnicodeEncodeError: | ||||
|                         pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", | ||||
|                                        put_text = "{reverse}{red}{bold}Bad machineName. Exiting...{end}") | ||||
|  | ||||
|                 if len(clt_config['machineName']) < 2 or len(clt_config['machineName']) > 63: | ||||
|                         pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", | ||||
|                                        put_text = "{reverse}{red}{bold}machineName must be between 2 and 63 characters in length. Exiting...{end}") | ||||
|                                        put_text = "{reverse}{red}{bold}argument `-n/--name`: invalid with: '%s'. Exiting...{end}" %clt_config['machine']) | ||||
|                          | ||||
|         clt_config['call_id'] = 1 | ||||
|  | ||||
| @@ -149,8 +166,14 @@ def client_update(): | ||||
|          | ||||
| def client_create(): | ||||
|         loggerclt.info("Connecting to %s on port %d..." % (clt_config['ip'], clt_config['port'])) | ||||
|         try: | ||||
|                 s = socket.create_connection((clt_config['ip'], clt_config['port'])) | ||||
|                 loggerclt.info("Connection successful !") | ||||
|         except (socket.gaierror, socket.error) as e: | ||||
|                 pretty_printer(log_obj = loggerclt.error, to_exit = True, where = "clt", | ||||
|                                put_text = "{reverse}{red}{bold}Connection failed '%s:%d': %s. Exiting...{end}" %(clt_config['ip'], | ||||
|                                                                                                                  clt_config['port'], | ||||
|                                                                                                                  str(e))) | ||||
|         binder = pykms_RpcBind.handler(None, clt_config) | ||||
|         RPC_Bind = enco(str(binder.generateRequest()), 'latin-1') | ||||
|  | ||||
| @@ -248,7 +271,7 @@ def createKmsRequestBase(): | ||||
|         requestDict['previousClientMachineId'] = '\0' * 16 # I'm pretty sure this is supposed to be a null UUID. | ||||
|         requestDict['requiredClientCount'] = clt_config['RequiredClientCount'] | ||||
|         requestDict['requestTime'] = dt_to_filetime(datetime.datetime.utcnow()) | ||||
|         requestDict['machineName'] = (clt_config['machineName'] if (clt_config['machineName'] is not None) else | ||||
|         requestDict['machineName'] = (clt_config['machine'] if (clt_config['machine'] is not None) else | ||||
|                                       ''.join(random.choice(string.ascii_letters + string.digits) for i in range(random.randint(2,63)))).encode('utf-16le') | ||||
|         requestDict['mnPad'] = '\0'.encode('utf-16le') * (63 - len(requestDict['machineName'].decode('utf-16le'))) | ||||
|          | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import re | ||||
| import sys | ||||
| import os | ||||
| from collections import OrderedDict | ||||
| import logging | ||||
|  | ||||
| try: | ||||
|     # Python 2.x imports | ||||
| @@ -16,6 +17,7 @@ except ImportError: | ||||
|     import queue as Queue | ||||
|  | ||||
| pyver = sys.version_info[:2] | ||||
|  | ||||
| #---------------------------------------------------------------------------------------------------------------------------------------------------------- | ||||
|  | ||||
| def enco(strg, typ = 'latin-1'): | ||||
| @@ -195,13 +197,15 @@ if pyver < (3, 3): | ||||
|             file = kwargs.get('file', sys.stdout) | ||||
|             file.flush() if file is not None else sys.stdout.flush() | ||||
|  | ||||
| # based on: https://ryanjoneil.github.io/posts/2014-02-14-capturing-stdout-in-a-python-child-process.html, | ||||
| # but not using threading/multiprocessing so: | ||||
| # 1) message visualization order preserved. | ||||
| # 2) newlines_count function output not wrong. | ||||
| # based on: https://ryanjoneil.github.io/posts/2014-02-14-capturing-stdout-in-a-python-child-process.html | ||||
| queue_print = Queue.Queue() | ||||
|  | ||||
| class ShellMessage(object): | ||||
|     view = True | ||||
|     count, remain, numlist = (0, 0, []) | ||||
|     viewsrv, viewclt = (True for _ in range(2)) | ||||
|     asyncmsgsrv, asyncmsgclt = (False for _ in range(2)) | ||||
|     indx, count, remain, numlist = (0, 0, 0, []) | ||||
|     loggersrv_pty = logging.getLogger('logsrvpty') | ||||
|     loggerclt_pty = logging.getLogger('logcltpty') | ||||
|  | ||||
|     class Collect(StringIO): | ||||
|         # Capture string sent to stdout. | ||||
| @@ -215,8 +219,9 @@ class ShellMessage(object): | ||||
|             self.put_text = put_text | ||||
|             self.where = where | ||||
|             self.plaintext = [] | ||||
|             self.path = os.path.dirname(os.path.abspath( __file__ )) + '/pykms_newlines.txt' | ||||
|             self.print_queue = Queue.Queue() | ||||
|             self.path_nl = os.path.dirname(os.path.abspath( __file__ )) + '/pykms_newlines.txt' | ||||
|             self.path_clean_nl = os.path.dirname(os.path.abspath( __file__ )) + '/pykms_clean_newlines.txt' | ||||
|             self.queue_get = Queue.Queue() | ||||
|  | ||||
|         def formatter(self, msgtofrmt): | ||||
|             if self.newlines: | ||||
| @@ -236,14 +241,14 @@ class ShellMessage(object): | ||||
|  | ||||
|         def newlines_file(self, mode, *args): | ||||
|             try: | ||||
|                 with open(self.path, mode) as file: | ||||
|                 with open(self.path_nl, mode) as file: | ||||
|                     if mode in ['w', 'a']: | ||||
|                         file.write(args[0]) | ||||
|                     elif mode == 'r': | ||||
|                         data = [int(i) for i in [line.rstrip('\n') for line in file.readlines()]] | ||||
|                         self.newlines, ShellMessage.remain = data[0], sum(data[1:]) | ||||
|             except: | ||||
|                 with open(self.path, 'w') as file: | ||||
|                 with open(self.path_nl, 'w') as file: | ||||
|                         pass | ||||
|  | ||||
|         def newlines_count(self, num): | ||||
| @@ -265,13 +270,67 @@ class ShellMessage(object): | ||||
|                     self.continuecount = True | ||||
|                 elif num in [-2 ,-4]: | ||||
|                     self.newlines_file('r') | ||||
|  | ||||
|             self.newlines_clean(num) | ||||
|  | ||||
|         def newlines_clean(self, num): | ||||
|             if num == 0: | ||||
|                 with open(self.path_clean_nl, 'w') as file: | ||||
|                     file.write('clean newlines') | ||||
|             try: | ||||
|                 with open(self.path_clean_nl, 'r') as file: | ||||
|                     some = file.read() | ||||
|                 if num == 21: | ||||
|                     ShellMessage.count, ShellMessage.remain, ShellMessage.numlist = (0, 0, []) | ||||
|                 os.remove(self.path) | ||||
|                     os.remove(self.path_nl) | ||||
|                     os.remove(self.path_clean_nl) | ||||
|             except: | ||||
|                 if num == 19: | ||||
|                     ShellMessage.count, ShellMessage.remain, ShellMessage.numlist = (0, 0, []) | ||||
|                     os.remove(self.path_nl) | ||||
|  | ||||
|         def run(self): | ||||
|             # view = False part. | ||||
|             if not ShellMessage.view: | ||||
|         def putter(self, aqueue, toput): | ||||
|             try: | ||||
|                 aqueue.put_nowait(toput) | ||||
|             except Queue.Full: | ||||
|                 pass | ||||
|  | ||||
|         def execute(self): | ||||
|             self.manage() | ||||
|             ShellMessage.indx += 1 | ||||
|  | ||||
|         def print_logging_setup(self, logger, async_flag, formatter = logging.Formatter('%(name)s %(message)s')): | ||||
|             from pykms_GuiBase import gui_redirector | ||||
|             stream = gui_redirector(StringIO()) | ||||
|             handler = logging.StreamHandler(stream) | ||||
|             handler.name = 'LogStream' | ||||
|             handler.setLevel(logging.INFO) | ||||
|             handler.setFormatter(formatter) | ||||
|  | ||||
|             if logger.handlers: | ||||
|                 logger.handlers = [] | ||||
|  | ||||
|             if async_flag: | ||||
|                 from pykms_Misc import MultiProcessingLogHandler | ||||
|                 logger.addHandler(MultiProcessingLogHandler('Thread-AsyncMsg{0}'.format(handler.name), handler = handler)) | ||||
|             else: | ||||
|                 logger.addHandler(handler) | ||||
|             logger.setLevel(logging.INFO) | ||||
|  | ||||
|         def print_logging(self, toprint): | ||||
|             if (self.nshell and ((0 in self.nshell) or (2 in self.nshell and not ShellMessage.viewclt))) or ShellMessage.indx == 0: | ||||
|                 from pykms_GuiBase import gui_redirector_setup, gui_redirector_clear | ||||
|                 gui_redirector_setup() | ||||
|                 gui_redirector_clear() | ||||
|                 self.print_logging_setup(ShellMessage.loggersrv_pty, ShellMessage.asyncmsgsrv) | ||||
|                 self.print_logging_setup(ShellMessage.loggerclt_pty, ShellMessage.asyncmsgclt) | ||||
|  | ||||
|             if self.where == 'srv': | ||||
|                 ShellMessage.loggersrv_pty.info(toprint) | ||||
|             elif self.where == 'clt': | ||||
|                 ShellMessage.loggerclt_pty.info(toprint) | ||||
|  | ||||
|         def notview(self): | ||||
|             if self.get_text: | ||||
|                 self.newlines = 0 | ||||
|                 if self.put_text is not None: | ||||
| @@ -280,25 +339,45 @@ class ShellMessage(object): | ||||
|                 else: | ||||
|                     for num in self.nshell: | ||||
|                         self.formatter(MsgMap[num]) | ||||
|                     return self.plaintext | ||||
|                 else: | ||||
|                 self.putter(self.queue_get, self.plaintext) | ||||
|  | ||||
|         def manage(self): | ||||
|             if not ShellMessage.viewsrv: | ||||
|                 # viewsrv = False, viewclt = True. | ||||
|                 if ShellMessage.viewclt: | ||||
|                     if self.where == 'srv': | ||||
|                         self.notview() | ||||
|                         return | ||||
|                 else: | ||||
|                     # viewsrv = False, viewclt = False. | ||||
|                     self.notview() | ||||
|                     return | ||||
|             else: | ||||
|                 # viewsrv = True, viewclt = False. | ||||
|                 if not ShellMessage.viewclt: | ||||
|                     if self.where == 'clt': | ||||
|                         self.notview() | ||||
|                         return | ||||
|                 else: | ||||
|                     # viewsrv = True, viewclt = True. | ||||
|                     pass | ||||
|  | ||||
|             # Do job. | ||||
|             self.produce() | ||||
|             toprint = self.consume(timeout = 0.1) | ||||
|             # Redirect output. | ||||
|             toprint = self.consume(queue_print, timeout = 0.1) | ||||
|  | ||||
|             if sys.stdout.isatty(): | ||||
|                 print(toprint) | ||||
|                 print(toprint, flush = True) | ||||
|             else: | ||||
|                 try: | ||||
|                     # Import after variables creation. | ||||
|                     from pykms_GuiBase import gui_redirect | ||||
|                     gui_redirect(toprint, self.where) | ||||
|                     self.print_logging(toprint) | ||||
|                 except: | ||||
|                     print(toprint) | ||||
|                     print(toprint, flush = True) | ||||
|  | ||||
|             # Get string/s printed. | ||||
|             if self.get_text: | ||||
|                 return self.plaintext | ||||
|                 self.putter(self.queue_get, self.plaintext) | ||||
|                 return | ||||
|  | ||||
|         def produce(self): | ||||
|             # Save everything that would otherwise go to stdout. | ||||
| @@ -327,15 +406,12 @@ class ShellMessage(object): | ||||
|             finally: | ||||
|                 # Restore stdout and send content. | ||||
|                 sys.stdout = sys.__stdout__ | ||||
|                 try: | ||||
|                     self.print_queue.put(outstream.getvalue()) | ||||
|                 except Queue.Full: | ||||
|                     pass | ||||
|                 self.putter(queue_print, outstream.getvalue()) | ||||
|  | ||||
|         def consume(self, timeout = None): | ||||
|         def consume(self, aqueue, timeout = None): | ||||
|             try: | ||||
|                 toprint = self.print_queue.get(block = timeout is not None, timeout = timeout) | ||||
|                 self.print_queue.task_done() | ||||
|                 toprint = aqueue.get(block = timeout is not None, timeout = timeout) | ||||
|                 aqueue.task_done() | ||||
|                 return toprint | ||||
|             except Queue.Empty: | ||||
|                 return None | ||||
| @@ -381,11 +457,13 @@ def pretty_printer(**kwargs): | ||||
|                 options['get_text'] = False | ||||
|  | ||||
|         # Process messages. | ||||
|         plain_messages = ShellMessage.Process(options['num_text'], | ||||
|         shmsg = ShellMessage.Process(options['num_text'], | ||||
|                                      get_text = options['get_text'], | ||||
|                                      put_text = options['put_text'], | ||||
|                                               where = options['where']).run() | ||||
|                                      where = options['where']) | ||||
|  | ||||
|         shmsg.execute() | ||||
|         plain_messages = shmsg.consume(shmsg.queue_get, timeout = None) | ||||
|         if options['log_obj']: | ||||
|                 for plain_message in plain_messages: | ||||
|                         options['log_obj'](plain_message) | ||||
|   | ||||
| @@ -49,24 +49,35 @@ def get_ip_address(): | ||||
|                 ip = 'Unknown' | ||||
|         return ip | ||||
|  | ||||
| def gui_redirect(str_to_print, where): | ||||
| def gui_redirector(stream, redirect_to = TextRedirect.Pretty, redirect_conditio = True, stderr_side = "srv"): | ||||
|         global txsrv, txclt, txcol | ||||
|         if redirect_conditio: | ||||
|                 if stream == 'stdout': | ||||
|                         sys.stdout = redirect_to(txsrv, txclt, txcol) | ||||
|                 elif stream == 'stderr': | ||||
|                         sys.stderr = redirect_to(txsrv, txclt, txcol, stderr_side) | ||||
|                 else: | ||||
|                         stream = redirect_to(txsrv, txclt, txcol) | ||||
|                         return stream | ||||
|  | ||||
| def gui_redirector_setup(): | ||||
|         TextRedirect.Pretty.tag_num = 0 | ||||
|         TextRedirect.Pretty.newlinecut = [-1, -2, -4, -5] | ||||
|  | ||||
| def gui_redirector_clear(): | ||||
|         global txsrv, oysrv | ||||
|         try: | ||||
|                 TextRedirect.StdoutRedirect(txsrv, txclt, txcol, str_to_print, where) | ||||
|                 if oysrv: | ||||
|                         txsrv.configure(state = 'normal') | ||||
|                         txsrv.delete('1.0', 'end') | ||||
|                         txsrv.configure(state = 'disabled') | ||||
|         except: | ||||
|                 print(str_to_print) | ||||
|                 # self.onlysrv not defined (menu not used) | ||||
|                 pass | ||||
|  | ||||
| ##----------------------------------------------------------------------------------------------------------------------------------------------------------- | ||||
|  | ||||
| class KmsGui(tk.Tk): | ||||
|         def browse(self, entrywidget, options): | ||||
|                 path = filedialog.askdirectory() | ||||
|                 if os.path.isdir(path): | ||||
|                         entrywidget.delete('0', 'end') | ||||
|                         entrywidget.insert('end', path + os.sep + os.path.basename(options['lfile']['def'])) | ||||
|                          | ||||
|                          | ||||
|         def __init__(self, *args, **kwargs): | ||||
|                 tk.Tk.__init__(self, *args, **kwargs) | ||||
|                 self.wraplength = 200 | ||||
| @@ -97,18 +108,75 @@ class KmsGui(tk.Tk): | ||||
|  | ||||
|                 self.gui_create() | ||||
|  | ||||
|         def browse(self, entrywidget, options): | ||||
|                 path = filedialog.askdirectory() | ||||
|                 if os.path.isdir(path): | ||||
|                         entrywidget.delete('0', 'end') | ||||
|                         entrywidget.insert('end', path + os.sep + os.path.basename(options['lfile']['def'])) | ||||
|  | ||||
|         def invert(self, widgets = []): | ||||
|                 for widget in widgets: | ||||
|                         if widget['state'] == 'normal': | ||||
|                                 widget.configure(state = 'disabled') | ||||
|                         elif widget['state'] == 'disabled': | ||||
|                                 widget.configure(state = 'normal') | ||||
|  | ||||
|         def gui_menu(self): | ||||
|                 self.onlysrv, self.onlyclt = (False for _ in range(2)) | ||||
|                 menubar = tk.Menu(self) | ||||
|                 prefmenu = tk.Menu(menubar, tearoff = 0, font = ("Noto Sans Regular", 10), borderwidth = 3, relief = 'ridge') | ||||
|                 menubar.add_cascade(label = 'Preferences', menu = prefmenu) | ||||
|                 prefmenu.add_command(label = 'Enable server-side mode', command = lambda: self.pref_onlysrv(prefmenu)) | ||||
|                 prefmenu.add_command(label = 'Enable client-side mode', command = lambda: self.pref_onlyclt(prefmenu)) | ||||
|                 self.config(menu = menubar) | ||||
|                  | ||||
|         def pref_onlysrv(self, menu): | ||||
|                 global oysrv | ||||
|  | ||||
|                 if self.onlyclt or serverthread.is_running_server: | ||||
|                         return | ||||
|                 self.onlysrv = not self.onlysrv | ||||
|                 if self.onlysrv: | ||||
|                         menu.entryconfigure(0, label = 'Disable server-side mode') | ||||
|                         self.clt_on_show(force_remove = True) | ||||
|                 else: | ||||
|                         menu.entryconfigure(0, label = 'Enable server-side mode') | ||||
|                 self.invert(widgets = [self.shbtnclt]) | ||||
|                 oysrv = self.onlysrv | ||||
|  | ||||
|         def pref_onlyclt(self, menu): | ||||
|                 if self.onlysrv or serverthread.is_running_server: | ||||
|                         return | ||||
|                 self.onlyclt = not self.onlyclt | ||||
|                 if self.onlyclt: | ||||
|                         menu.entryconfigure(1, label = 'Disable client-side mode') | ||||
|                         if self.shbtnclt['text'] == 'SHOW\nCLIENT': | ||||
|                                 self.clt_on_show(force_view = True) | ||||
|                         self.optsrvwin.grid_remove() | ||||
|                         self.msgsrvwin.grid_remove() | ||||
|                         gui_redirector('stderr', redirect_to = TextRedirect.Stderr, stderr_side = "clt") | ||||
|                 else: | ||||
|                         menu.entryconfigure(1, label = 'Enable client-side mode') | ||||
|                         self.optsrvwin.grid() | ||||
|                         self.msgsrvwin.grid() | ||||
|                         gui_redirector('stderr', redirect_to = TextRedirect.Stderr) | ||||
|  | ||||
|                 self.invert(widgets = [self.runbtnsrv, self.shbtnclt, self.runbtnclt]) | ||||
|  | ||||
|         def gui_create(self): | ||||
|                 ## Create server gui | ||||
|                 self.gui_srv() | ||||
|                 ## Create client gui + other operations. | ||||
|                 self.gui_complete() | ||||
|                 ## Create menu. | ||||
|                 self.gui_menu() | ||||
|                 ## Create globals for printing process (redirect stdout). | ||||
|                 global txsrv, txclt, txcol | ||||
|                 txsrv = self.textboxsrv.get() | ||||
|                 txclt = self.textboxclt.get() | ||||
|                 txcol = self.customcolors | ||||
|                 ## Redirect stderr. | ||||
|                 sys.stderr = TextRedirect.StderrRedirect(txsrv, txclt, txcol) | ||||
|                 gui_redirector('stderr', redirect_to = TextRedirect.Stderr) | ||||
|  | ||||
|         def gui_pages_show(self, pagename, side): | ||||
|                 # https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter | ||||
| @@ -162,7 +230,7 @@ class KmsGui(tk.Tk): | ||||
|                         btnani = tk.Button(aniwin) | ||||
|                         btnani.grid(row = 0, column = col[2], padx = 2, pady = 2, sticky = stick) | ||||
|                         self.pagewidgets[side]["BtnAni"][position] = btnani | ||||
|                 # customize buttons. | ||||
|                 ## Customize buttons. | ||||
|                 custom_pages(self, side) | ||||
|  | ||||
|         def gui_pages_create(self, parent, side, create = {}): | ||||
| @@ -209,11 +277,11 @@ class KmsGui(tk.Tk): | ||||
|  | ||||
|                 self.pagewidgets = {} | ||||
|  | ||||
|                 ## subpages of optsrvwin. | ||||
|                 ## Subpages of "optsrvwin". | ||||
|                 self.gui_pages_create(parent = self.optsrvwin, side = "Srv", create = {"PageStart": None, | ||||
|                                                                                        "PageEnd": None}) | ||||
|  | ||||
|                 ## continue to grid. | ||||
|                 ## Continue to grid. | ||||
|                 self.msgsrvwin.grid(row = 1, column = 2, padx = 1, pady = 1, sticky = 'nsew') | ||||
|                 self.msgsrvwin.grid_propagate(False) | ||||
|                 self.msgsrvwin.grid_columnconfigure(0, weight = 1) | ||||
| @@ -285,7 +353,7 @@ class KmsGui(tk.Tk): | ||||
|                 self.activ.insert('end', str(srv_options['activation']['def'])) | ||||
|                 ToolTip(self.activ, text = srv_options['activation']['help'], wraplength = self.wraplength) | ||||
|                 # Renewal Interval. | ||||
|                 renewlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Activation Interval: ', font = self.optfont) | ||||
|                 renewlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Renewal Interval: ', font = self.optfont) | ||||
|                 self.renew = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, validate = "key", | ||||
|                                       validatecommand = self.validation_int) | ||||
|                 self.renew.insert('end', str(srv_options['renewal']['def'])) | ||||
| @@ -298,28 +366,34 @@ class KmsGui(tk.Tk): | ||||
|                 ToolTip(self.srvfile, text = srv_options['lfile']['help'], wraplength = self.wraplength) | ||||
|                 srvfilebtnwin = tk.Button(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Browse', | ||||
|                                        command = lambda: self.browse(self.srvfile, srv_options)) | ||||
|  | ||||
|                 # Loglevel. | ||||
|                 srvlevellbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.optfont) | ||||
|                 self.srvlevel = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = tuple(srv_options['llevel']['choi']), | ||||
|                                              width = 10, height = 10, font = self.optfontredux, state = "readonly") | ||||
|                 self.srvlevel.set(srv_options['llevel']['def']) | ||||
|                 ToolTip(self.srvlevel, text = srv_options['llevel']['help'], wraplength = self.wraplength) | ||||
|  | ||||
|                 self.chksrvfile = ListboxOfRadiobuttons(self.pagewidgets["Srv"]["PageWin"]["PageStart"], | ||||
|                                                         ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], | ||||
|                                                         self.optfontredux, | ||||
|                                                         changed = [(self.srvfile, srv_options['lfile']['def']), | ||||
|                                                                    (srvfilebtnwin, ''), | ||||
|                                                                    (self.srvlevel, srv_options['llevel']['def'])], | ||||
|                                                         width = 10, height = 1, borderwidth = 2, relief = 'ridge') | ||||
|  | ||||
|                 # Logsize. | ||||
|                 srvsizelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.optfont) | ||||
|                 self.srvsize = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.optfont, validate = "key", | ||||
|                                         validatecommand = self.validation_float) | ||||
|                 self.srvsize.insert('end', srv_options['lsize']['def']) | ||||
|                 ToolTip(self.srvsize, text = srv_options['lsize']['help'], wraplength = self.wraplength) | ||||
|                 # Asynchronous messages. | ||||
|                 self.chkvalsrvasy = tk.BooleanVar() | ||||
|                 self.chkvalsrvasy.set(srv_options['asyncmsg']['def']) | ||||
|                 chksrvasy = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Async\nMsg', | ||||
|                                            font = self.optfontredux, var = self.chkvalsrvasy, relief = 'groove') | ||||
|                 ToolTip(chksrvasy, text = srv_options['asyncmsg']['help'], wraplength = self.wraplength) | ||||
|  | ||||
|                 # Listbox radiobuttons server. | ||||
|                 self.chksrvfile = ListboxOfRadiobuttons(self.pagewidgets["Srv"]["PageWin"]["PageStart"], | ||||
|                                                         ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], | ||||
|                                                         self.optfontredux, | ||||
|                                                         changed = [(self.srvfile, srv_options['lfile']['def']), | ||||
|                                                                    (srvfilebtnwin, ''), | ||||
|                                                                    (self.srvsize, srv_options['lsize']['def']), | ||||
|                                                                    (self.srvlevel, srv_options['llevel']['def'])], | ||||
|                                                         width = 10, height = 1, borderwidth = 2, relief = 'ridge') | ||||
|  | ||||
|                 ## Layout widgets (optsrvwin:Srv:PageWin:PageStart) | ||||
|                 ver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew') | ||||
| @@ -344,6 +418,7 @@ class KmsGui(tk.Tk): | ||||
|                 self.srvfile.grid(row = 10, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 srvfilebtnwin.grid(row = 10, column = 2, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 self.chksrvfile.grid(row = 11, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 chksrvasy.grid(row = 11, column = 2, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 srvlevellbl.grid(row = 12, column = 0, padx = 5, pady = 5, sticky = 'e') | ||||
|                 self.srvlevel.grid(row = 12, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 srvsizelbl.grid(row = 13, column = 0, padx = 5, pady = 5, sticky = 'e') | ||||
| @@ -352,23 +427,25 @@ class KmsGui(tk.Tk): | ||||
|                 ## Create widgets (optsrvwin:Srv:PageWin:PageEnd)------------------------------------------------------------------------------------------- | ||||
|                 # Timeout connection. | ||||
|                 timeout0lbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Timeout connection: ', font = self.optfont) | ||||
|                 self.timeout0 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 10, font = self.optfont) | ||||
|                 self.timeout0 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.optfont) | ||||
|                 self.timeout0.insert('end', str(srv_options['time0']['def'])) | ||||
|                 ToolTip(self.timeout0, text = srv_options['time0']['help'], wraplength = self.wraplength) | ||||
|                 # Sqlite database. | ||||
|                 self.chkvalsql = tk.BooleanVar() | ||||
|                 self.chkvalsql.set(srv_options['sql']['def']) | ||||
|                 chksql = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Create Sqlite\nDatabase', | ||||
|                                         font = self.optfont, var = self.chkvalsql) | ||||
|                                         font = self.optfontredux, var = self.chkvalsql, relief = 'groove') | ||||
|                 ToolTip(chksql, text = srv_options['sql']['help'], wraplength = self.wraplength) | ||||
|  | ||||
|                 ## Layout widgets (optsrvwin:Srv:PageWin:PageEnd) | ||||
|                 tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 0, height = 0).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') | ||||
|                 # a label for vertical aligning with PageStart | ||||
|                 tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 0, | ||||
|                          height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') | ||||
|                 timeout0lbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e') | ||||
|                 self.timeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 chksql.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 self.timeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'w') | ||||
|                 chksql.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'w') | ||||
|  | ||||
|                 # Store Srv widgets. | ||||
|                 # Store server-side widgets. | ||||
|                 self.storewidgets_srv = self.gui_store(side = "Srv", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton']) | ||||
|                 self.storewidgets_srv.append(self.chksrvfile) | ||||
|  | ||||
| @@ -377,28 +454,52 @@ class KmsGui(tk.Tk): | ||||
|                                                    relief = 'ridge', font = self.msgfont) | ||||
|                 self.textboxsrv.put() | ||||
|  | ||||
|         def always_centered(self, geo, centered, refs): | ||||
|                 x = (self.winfo_screenwidth() // 2) - (self.winfo_width() // 2) | ||||
|                 y = (self.winfo_screenheight() // 2) - (self.winfo_height() // 2) | ||||
|                 w, h, dx, dy = geo.split('+')[0].split('x') + geo.split('+')[1:] | ||||
|  | ||||
|                 if w == refs[1]: | ||||
|                         if centered: | ||||
|                                 self.geometry('+%d+%d' %(x, y)) | ||||
|                                 centered = False | ||||
|                 elif w == refs[0]: | ||||
|                         if not centered: | ||||
|                                 self.geometry('+%d+%d' %(x, y)) | ||||
|                                 centered = True | ||||
|  | ||||
|                 if dx != str(x) or dy != str(y): | ||||
|                         self.geometry('+%d+%d' %(x, 0)) | ||||
|  | ||||
|                 self.after(200, self.always_centered, self.geometry(), centered, refs) | ||||
|  | ||||
|         def gui_complete(self): | ||||
|                 ## Create client widgets (optcltwin, msgcltwin, btncltwin) | ||||
|                 self.update_idletasks()   # update Gui to get btnsrvwin values --> btncltwin. | ||||
|                 minw, minh = self.winfo_width(), self.winfo_height() | ||||
|                 self.iconify()   | ||||
|                 self.gui_clt() | ||||
|                 minw, minh = self.winfo_width(), self.winfo_height() | ||||
|                 # Main window custom background. | ||||
|                 maxw, minh = self.winfo_width(), self.winfo_height() | ||||
|                 ## Main window custom background. | ||||
|                 self.update_idletasks()   # update Gui for custom background | ||||
|                 self.iconify() | ||||
|                 custom_background(self) | ||||
|                 # Main window other modifications. | ||||
|                 ## Main window other modifications. | ||||
|                 self.eval('tk::PlaceWindow %s center' %self.winfo_pathname(self.winfo_id())) | ||||
|                 self.wm_attributes("-topmost", True) | ||||
|                 self.protocol("WM_DELETE_WINDOW", lambda:0)  | ||||
|                 self.minsize(minw, minh) | ||||
|                 self.resizable(True, False) | ||||
|                 self.protocol("WM_DELETE_WINDOW", lambda: 0) | ||||
|                 ## Disable maximize button. | ||||
|                 self.resizable(False, False) | ||||
|                 ## Centered window. | ||||
|                 self.always_centered(self.geometry(), False, [minw, maxw]) | ||||
|  | ||||
|         def get_position(self, genericwidget): | ||||
|                 x, y = (genericwidget.winfo_x(), genericwidget.winfo_y()) | ||||
|                 w, h = (genericwidget.winfo_width(), genericwidget.winfo_height()) | ||||
|         def get_position(self, widget): | ||||
|                 x, y = (widget.winfo_x(), widget.winfo_y()) | ||||
|                 w, h = (widget.winfo_width(), widget.winfo_height()) | ||||
|                 return x, y, w, h | ||||
|                  | ||||
|         def gui_clt(self): | ||||
|                 self.count_clear = 0 | ||||
|                 self.optcltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') | ||||
|                 self.msgcltwin = tk.Frame(self.masterwin, background = self.customcolors['black'], relief = 'ridge', width = 300, height = 200) | ||||
|                 self.btncltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') | ||||
| @@ -411,29 +512,25 @@ class KmsGui(tk.Tk): | ||||
|                 self.optcltwin.grid_rowconfigure(0, weight = 1) | ||||
|                 self.optcltwin.grid_columnconfigure(1, weight = 1) | ||||
|  | ||||
|                 # subpages of optcltwin. | ||||
|                 ## Subpages of "optcltwin". | ||||
|                 self.gui_pages_create(parent = self.optcltwin, side = "Clt", create = {"PageStart": None, | ||||
|                                                                                        "PageEnd": None}) | ||||
|  | ||||
|                 # continue to grid. | ||||
|                 ## Continue to grid. | ||||
|                 self.msgcltwin.grid(row = 1, column = 4, padx = 1, pady = 1, sticky = 'nsew') | ||||
|                 self.msgcltwin.grid_propagate(False) | ||||
|                 self.msgcltwin.grid_columnconfigure(0, weight = 1) | ||||
|                 self.msgcltwin.grid_rowconfigure(0, weight = 1) | ||||
|  | ||||
|                 # Create widgets (btncltwin) ---------------------------------------------------------------------------------------------------------------- | ||||
|                 ## Create widgets (btncltwin) ---------------------------------------------------------------------------------------------------------------- | ||||
|                 self.runbtnclt = tk.Button(self.btncltwin, text = 'START\nCLIENT', background = self.customcolors['blue'], | ||||
|                                            foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont, | ||||
|                                            state = 'disabled', command = self.clt_on_start) | ||||
|  | ||||
| ##                self.othbutt = tk.Button(self.btncltwin, text = 'Botton\n2', background = self.customcolors['green'], | ||||
| ##                                         foreground = self.customcolors['white'], relief = 'flat', font = self.btnwinfont) | ||||
|                  | ||||
|                 # Layout widgets (btncltwin) | ||||
|                 ## Layout widgets (btncltwin) | ||||
|                 self.runbtnclt.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'ew') | ||||
| ##                self.othbutt.grid(row = 1, column = 0, padx = 2, pady = 2, sticky = 'ew') | ||||
|                  | ||||
|                 # Create widgets (optcltwin:Clt:PageWin:PageStart) ------------------------------------------------------------------------------------------ | ||||
|                 ## Create widgets (optcltwin:Clt:PageWin:PageStart) ------------------------------------------------------------------------------------------ | ||||
|                 # Version. | ||||
|                 cltver = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'You are running client version: ' + clt_version, | ||||
|                                   foreground = self.customcolors['red'], font = self.othfont) | ||||
| @@ -479,21 +576,30 @@ class KmsGui(tk.Tk): | ||||
|                 self.cltlevel.set(clt_options['llevel']['def']) | ||||
|                 ToolTip(self.cltlevel, text = clt_options['llevel']['help'], wraplength = self.wraplength) | ||||
|  | ||||
|                 self.chkcltfile = ListboxOfRadiobuttons(self.pagewidgets["Clt"]["PageWin"]["PageStart"], | ||||
|                                                         ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], | ||||
|                                                         self.optfontredux, | ||||
|                                                         changed = [(self.cltfile, clt_options['lfile']['def']), | ||||
|                                                                    (cltfilebtnwin, ''), | ||||
|                                                                    (self.cltlevel, clt_options['llevel']['def'])], | ||||
|                                                         width = 10, height = 1, borderwidth = 2, relief = 'ridge') | ||||
|                 # Logsize. | ||||
|                 cltsizelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.optfont) | ||||
|                 self.cltsize = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.optfont, validate = "key", | ||||
|                                         validatecommand = self.validation_float) | ||||
|                 self.cltsize.insert('end', clt_options['lsize']['def']) | ||||
|                 ToolTip(self.cltsize, text = clt_options['lsize']['help'], wraplength = self.wraplength) | ||||
|                 # Asynchronous messages. | ||||
|                 self.chkvalcltasy = tk.BooleanVar() | ||||
|                 self.chkvalcltasy.set(clt_options['asyncmsg']['def']) | ||||
|                 chkcltasy = tk.Checkbutton(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Async\nMsg', | ||||
|                                            font = self.optfontredux, var = self.chkvalcltasy, relief = 'groove') | ||||
|                 ToolTip(chkcltasy, text = clt_options['asyncmsg']['help'], wraplength = self.wraplength) | ||||
|  | ||||
|                 # Layout widgets (optcltwin:Clt:PageWin:PageStart) | ||||
|                 # Listbox radiobuttons client. | ||||
|                 self.chkcltfile = ListboxOfRadiobuttons(self.pagewidgets["Clt"]["PageWin"]["PageStart"], | ||||
|                                                         ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], | ||||
|                                                         self.optfontredux, | ||||
|                                                         changed = [(self.cltfile, clt_options['lfile']['def']), | ||||
|                                                                    (cltfilebtnwin, ''), | ||||
|                                                                    (self.cltsize, clt_options['lsize']['def']), | ||||
|                                                                    (self.cltlevel, clt_options['llevel']['def'])], | ||||
|                                                         width = 10, height = 1, borderwidth = 2, relief = 'ridge') | ||||
|                 | ||||
|                 ## Layout widgets (optcltwin:Clt:PageWin:PageStart) | ||||
|                 cltver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 cltipaddlbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e') | ||||
|                 self.cltipadd.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
| @@ -509,21 +615,29 @@ class KmsGui(tk.Tk): | ||||
|                 self.cltfile.grid(row = 6, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 cltfilebtnwin.grid(row = 6, column = 2, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 self.chkcltfile.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 chkcltasy.grid(row = 7, column = 2, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 cltlevellbl.grid(row = 8, column = 0, padx = 5, pady = 5, sticky = 'e') | ||||
|                 self.cltlevel.grid(row = 8, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|                 cltsizelbl.grid(row = 9, column = 0, padx = 5, pady = 5, sticky = 'e') | ||||
|                 self.cltsize.grid(row = 9, column = 1, padx = 5, pady = 5, sticky = 'ew') | ||||
|  | ||||
|                 # ugly fix when client-side mode is activated. | ||||
|                 templbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], | ||||
|                                    bg = self.customcolors['lavender']).grid(row = 10, column = 0, | ||||
|                                                                             padx = 35, pady = 54, sticky = 'e') | ||||
|  | ||||
|                 ## Create widgets (optcltwin:Clt:PageWin:PageEnd) ------------------------------------------------------------------------------------------- | ||||
|  | ||||
|                 ## Layout widgets (optcltwin:Clt:PageWin:PageEnd) | ||||
|                 tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 0, height = 0).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') | ||||
|                 # a label for vertical aligning with PageStart | ||||
|                 tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 0, | ||||
|                          height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') | ||||
|  | ||||
|                 # Store Clt widgets. | ||||
|                 self.storewidgets_clt = self.gui_store(side = "Clt", typewidgets = ['Button', 'Entry', 'TCombobox']) | ||||
|                 ## Store client-side widgets. | ||||
|                 self.storewidgets_clt = self.gui_store(side = "Clt", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton']) | ||||
|                 self.storewidgets_clt.append(self.chkcltfile) | ||||
|                  | ||||
|                 # Create widgets and layout (msgcltwin) ----------------------------------------------------------------------------------------------------- | ||||
|                 ## Create widgets and layout (msgcltwin) ----------------------------------------------------------------------------------------------------- | ||||
|                 self.textboxclt = TextDoubleScroll(self.msgcltwin, background = self.customcolors['black'], wrap = 'none', state = 'disabled', | ||||
|                                                    relief = 'ridge', font = self.msgfont) | ||||
|                 self.textboxclt.put() | ||||
| @@ -540,22 +654,22 @@ class KmsGui(tk.Tk): | ||||
|                                 # is a STRING. | ||||
|                                 return value | ||||
|  | ||||
|         def prep_logfile(self, filepath): | ||||
|         def prep_logfile(self, filepath, status): | ||||
|                 # FILE       (pretty on,  log view off, logfile yes) | ||||
|                 # FILEOFF    (pretty on,  log view off, no logfile) | ||||
|                 # STDOUT     (pretty off, log view on,  no logfile) | ||||
|                 # STDOUTOFF  (pretty off, log view off, logfile yes) | ||||
|                 # FILESTDOUT (pretty off, log view on,  logfile yes) | ||||
|                 st = self.chksrvfile.state() | ||||
|                 if st == 'FILE': | ||||
|  | ||||
|                 if status == 'FILE': | ||||
|                         return filepath | ||||
|                 elif st in ['FILESTDOUT', 'STDOUTOFF']: | ||||
|                         return [st, filepath] | ||||
|                 elif st in ['STDOUT', 'FILEOFF']: | ||||
|                         return st | ||||
|                 elif status in ['FILESTDOUT', 'STDOUTOFF']: | ||||
|                         return [status, filepath] | ||||
|                 elif status in ['STDOUT', 'FILEOFF']: | ||||
|                         return status | ||||
|  | ||||
|         def validate_int(self, value): | ||||
|                 return value == '' or value.isdigit() | ||||
|                 return value == "" or value.isdigit() | ||||
|  | ||||
|         def validate_float(self, value): | ||||
|                 if value == "": | ||||
| @@ -566,13 +680,13 @@ class KmsGui(tk.Tk): | ||||
|                 except ValueError: | ||||
|                         return False | ||||
|  | ||||
|         def clt_on_show(self, force = False): | ||||
|                 if self.optcltwin.winfo_ismapped() or force: | ||||
|         def clt_on_show(self, force_remove = False, force_view = False): | ||||
|                 if self.optcltwin.winfo_ismapped() or force_remove: | ||||
|                         self.shbtnclt['text'] = 'SHOW\nCLIENT' | ||||
|                         self.optcltwin.grid_remove() | ||||
|                         self.msgcltwin.grid_remove() | ||||
|                         self.btncltwin.place_forget() | ||||
|                 else: | ||||
|                 elif not self.optcltwin.winfo_ismapped() or force_view: | ||||
|                         self.shbtnclt['text'] = 'HIDE\nCLIENT' | ||||
|                         self.optcltwin.grid() | ||||
|                         self.msgcltwin.grid() | ||||
| @@ -580,12 +694,12 @@ class KmsGui(tk.Tk): | ||||
|  | ||||
|         def srv_on_start(self): | ||||
|                 if self.runbtnsrv['text'] == 'START\nSERVER': | ||||
|                         self.on_clear([txsrv, txclt]) | ||||
|                         self.srv_actions_start() | ||||
|                         # wait for switch. | ||||
|                         while not serverthread.is_running_server: | ||||
|                                 pass | ||||
|  | ||||
|                         self.on_clear([txsrv, txclt]) | ||||
|                         self.srv_toggle_all(on_start = True) | ||||
|                         # run thread for interrupting server when an error happens. | ||||
|                         self.srv_eject_thread = threading.Thread(target = self.srv_eject, name = "Thread-SrvEjt") | ||||
| @@ -609,12 +723,18 @@ class KmsGui(tk.Tk): | ||||
|                 srv_config[srv_options['count']['des']] = self.prep_option(self.count.get()) | ||||
|                 srv_config[srv_options['activation']['des']] = self.prep_option(self.activ.get()) | ||||
|                 srv_config[srv_options['renewal']['des']] = self.prep_option(self.renew.get()) | ||||
|                 srv_config[srv_options['lfile']['des']] = self.prep_logfile(self.srvfile.get()) | ||||
|                 srv_config[srv_options['lfile']['des']] = self.prep_logfile(self.srvfile.get(), self.chksrvfile.state()) | ||||
|                 srv_config[srv_options['asyncmsg']['des']] = self.chkvalsrvasy.get() | ||||
|                 srv_config[srv_options['llevel']['des']] = self.srvlevel.get() | ||||
|                 srv_config[srv_options['sql']['des']] = self.chkvalsql.get() | ||||
|                 srv_config[srv_options['lsize']['des']] = self.prep_option(self.srvsize.get()) | ||||
|                 srv_config[srv_options['time0']['des']] = self.prep_option(self.timeout0.get()) | ||||
|  | ||||
|                 srv_config[srv_options['time0']['des']] = self.prep_option(self.timeout0.get()) | ||||
|                 srv_config[srv_options['sql']['des']] = self.chkvalsql.get() | ||||
|  | ||||
|                 ## Redirect stdout. | ||||
|                 gui_redirector('stdout', redirect_to = TextRedirect.Log, | ||||
|                                redirect_conditio = (srv_config[srv_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT'])) | ||||
|                 gui_redirector_setup() | ||||
|                 serverqueue.put('start') | ||||
|  | ||||
|         def srv_actions_stop(self): | ||||
| @@ -641,6 +761,8 @@ class KmsGui(tk.Tk): | ||||
|                                          foreground = self.customcolors['white']) | ||||
|                         for widget in self.storewidgets_srv: | ||||
|                                 widget.configure(state = 'normal') | ||||
|                                 if isinstance(widget, ListboxOfRadiobuttons): | ||||
|                                         widget.change() | ||||
|                         self.runbtnclt.configure(state = 'disabled') | ||||
|  | ||||
|         def srv_toggle_state(self): | ||||
| @@ -652,13 +774,18 @@ class KmsGui(tk.Tk): | ||||
|                 self.statesrv.configure(text = txt, foreground = color) | ||||
|  | ||||
|         def clt_on_start(self): | ||||
|                 if self.onlyclt: | ||||
|                         self.on_clear([txclt]) | ||||
|                 else: | ||||
|                         rng, add_newline = self.on_clear_setup() | ||||
|                         self.on_clear([txsrv, txclt], clear_range = [rng, None], newline_list = [add_newline, False]) | ||||
|  | ||||
|                 self.clt_actions_start() | ||||
|                 # run thread for disabling interrupt server and client, when client running. | ||||
|                 self.clt_eject_thread = threading.Thread(target = self.clt_eject, name = "Thread-CltEjt") | ||||
|                 self.clt_eject_thread.setDaemon(True) | ||||
|                 self.clt_eject_thread.start() | ||||
|  | ||||
|                 self.on_clear([txsrv, txclt]) | ||||
|                 for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt]: | ||||
|                         widget.configure(state = 'disabled') | ||||
|  | ||||
| @@ -668,10 +795,16 @@ class KmsGui(tk.Tk): | ||||
|                 clt_config[clt_options['mode']['des']] = self.cltmode.get() | ||||
|                 clt_config[clt_options['cmid']['des']] = self.cltcmid.get() | ||||
|                 clt_config[clt_options['name']['des']] = self.cltname.get() | ||||
|                 clt_config[clt_options['lfile']['des']] = self.prep_logfile(self.cltfile.get(), self.chkcltfile.state()) | ||||
|                 clt_config[clt_options['asyncmsg']['des']] = self.chkvalcltasy.get() | ||||
|                 clt_config[clt_options['llevel']['des']] = self.cltlevel.get() | ||||
|                 clt_config[clt_options['lfile']['des']] = self.prep_logfile(self.cltfile.get()) | ||||
|                 clt_config[clt_options['lsize']['des']] = self.prep_option(self.cltsize.get()) | ||||
|  | ||||
|                 ## Redirect stdout. | ||||
|                 gui_redirector('stdout', redirect_to = TextRedirect.Log, | ||||
|                                redirect_conditio = (clt_config[clt_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT'])) | ||||
|                 gui_redirector_setup() | ||||
|  | ||||
|                 # run client (in a thread). | ||||
|                 self.clientthread = client_thread(name = "Thread-Clt") | ||||
|                 self.clientthread.setDaemon(True) | ||||
| @@ -681,8 +814,18 @@ class KmsGui(tk.Tk): | ||||
|         def clt_eject(self): | ||||
|                 while self.clientthread.is_alive(): | ||||
|                         sleep(0.1) | ||||
|                 for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt]: | ||||
|  | ||||
|                 widgets = self.storewidgets_clt + [self.runbtnclt] | ||||
|                 if not self.onlyclt: | ||||
|                         widgets += [self.runbtnsrv] | ||||
|  | ||||
|                 for widget in widgets: | ||||
|                         if isinstance(widget, ttk.Combobox): | ||||
|                                 widget.configure(state = 'readonly') | ||||
|                         else: | ||||
|                                 widget.configure(state = 'normal') | ||||
|                                 if isinstance(widget, ListboxOfRadiobuttons): | ||||
|                                         widget.change() | ||||
|  | ||||
|         def on_exit(self): | ||||
|                 if serverthread.is_running_server: | ||||
| @@ -693,8 +836,38 @@ class KmsGui(tk.Tk): | ||||
|                 server_terminate(serverthread, exit_thread = True) | ||||
|                 self.destroy() | ||||
|  | ||||
|         def on_clear(self, widgetlist): | ||||
|                 for widget in widgetlist: | ||||
|         def on_clear_setup(self): | ||||
|                 if any(opt in ['STDOUT', 'FILESTDOUT'] for opt in srv_config[srv_options['lfile']['des']]): | ||||
|                         if self.count_clear == 0: | ||||
|                                 self.ini = txsrv.index('end') | ||||
|                                 add_newline = False | ||||
|                         else: | ||||
|                                 if self.count_clear == 1: | ||||
|                                         self.ini = '%s.0' %(int(self.ini[0]) - 1) | ||||
|                                 else: | ||||
|                                         self.ini = '%s.0' %(int(self.ini[0])) | ||||
|                                 add_newline = True | ||||
|                         rng = [self.ini, 'end'] | ||||
|                         self.count_clear += 1 | ||||
|                 else: | ||||
|                         rng, add_newline = None, False | ||||
|                         self.count_clear = 0 | ||||
|  | ||||
|                 return rng, add_newline | ||||
|  | ||||
|         def on_clear(self, widget_list, clear_range = None, newline_list = []): | ||||
|                 if newline_list == []: | ||||
|                         newline_list = len(widget_list) * [False] | ||||
|  | ||||
|                 for num, couple in enumerate(zip(widget_list, newline_list)): | ||||
|                         widget, add_n = couple | ||||
|                         try: | ||||
|                                 ini, fin = clear_range[num] | ||||
|                         except TypeError: | ||||
|                                 ini, fin = '1.0', 'end' | ||||
|  | ||||
|                         widget.configure(state = 'normal') | ||||
|                         widget.delete('1.0', 'end') | ||||
|                         widget.delete(ini, fin) | ||||
|                         if add_n: | ||||
|                                 widget.insert('end', '\n') | ||||
|                         widget.configure(state = 'disabled') | ||||
|   | ||||
| @@ -120,37 +120,21 @@ class ToolTip(object): | ||||
|                 self.tw = None | ||||
|  | ||||
| ##----------------------------------------------------------------------------------------------------------------------------------------------------------- | ||||
| # https://stackoverflow.com/questions/2914603/segmentation-fault-while-redirecting-sys-stdout-to-tkinter-text-widget | ||||
| # https://stackoverflow.com/questions/7217715/threadsafe-printing-across-multiple-processes-python-2-x | ||||
| # https://stackoverflow.com/questions/3029816/how-do-i-get-a-thread-safe-print-in-python-2-6 | ||||
| # https://stackoverflow.com/questions/20303291/issue-with-redirecting-stdout-to-tkinter-text-widget-with-threads | ||||
|  | ||||
| class TextRedirect(object): | ||||
|         class StdoutRedirect(object): | ||||
|                 tag_num = 0 | ||||
|  | ||||
|         class Pretty(object): | ||||
|                 grpmsg = unformat_message([MsgMap[1], MsgMap[7], MsgMap[12], MsgMap[20]]) | ||||
|                 arrows = [ item[0] for item in grpmsg  ] | ||||
|                 clt_msg_nonewline = [ item[1] for item in grpmsg ] | ||||
|                 arrows = list(set(arrows)) | ||||
|                 lenarrow = len(arrows[0]) | ||||
|                 srv_msg_nonewline = [ item[0] for item in unformat_message([MsgMap[2], MsgMap[5], MsgMap[13], MsgMap[18]]) ] | ||||
|                 terminator = unformat_message([MsgMap[21]])[0][0] | ||||
|                 msg_align = [ msg[0].replace('\t', '').replace('\n', '') for msg in unformat_message([MsgMap[-2], MsgMap[-4]])] | ||||
|                 newlinecut = [-1, -2, -4, -5] | ||||
|                 msg_align = [ msg[0].replace('\t', '').replace('\n', '') for msg in unformat_message([MsgMap[-2], MsgMap[-4]]) ] | ||||
|  | ||||
|                 def __init__(self, srv_text_space, clt_text_space, customcolors, str_to_print, where): | ||||
|                 def __init__(self, srv_text_space, clt_text_space, customcolors): | ||||
|                         self.srv_text_space = srv_text_space | ||||
|                         self.clt_text_space = clt_text_space | ||||
|                         self.customcolors = customcolors | ||||
|                         self.str_to_print = str_to_print | ||||
|                         self.where = where | ||||
|                         self.textbox_do() | ||||
|  | ||||
|                 def textbox_finish(self, message): | ||||
|                         if message == self.terminator: | ||||
|                                 TextRedirect.StdoutRedirect.tag_num = 0 | ||||
|                                 TextRedirect.StdoutRedirect.newlinecut = [-1, -2, -4, -5] | ||||
|  | ||||
|                 def textbox_write(self, tag, message, color, extras): | ||||
|                         widget = self.textbox_choose(message) | ||||
| @@ -161,14 +145,15 @@ class TextRedirect(object): | ||||
|                         self.textbox_color(tag, widget, color, self.customcolors['black'], extras) | ||||
|                         widget.after(100, widget.see('end')) | ||||
|                         widget.configure(state = 'disabled') | ||||
|                         self.textbox_finish(message) | ||||
|  | ||||
|                 def textbox_choose(self, message): | ||||
|                         if self.where == "srv": | ||||
|                         if any(item.startswith('logsrv') for item in [message, self.str_to_print]): | ||||
|                                 self.srv_text_space.focus_set() | ||||
|                                 self.where = "srv" | ||||
|                                 return self.srv_text_space | ||||
|                         elif self.where == "clt": | ||||
|                         elif any(item.startswith('logclt') for item in [message, self.str_to_print]): | ||||
|                                 self.clt_text_space.focus_set() | ||||
|                                 self.where = "clt" | ||||
|                                 return self.clt_text_space | ||||
|  | ||||
|                 def textbox_color(self, tag, widget, forecolor = 'white', backcolor = 'black', extras = []): | ||||
| @@ -211,8 +196,8 @@ class TextRedirect(object): | ||||
|                                 # horizontal align. | ||||
|                                 if msg_unformat in self.msg_align: | ||||
|                                         msg_strip = message.lstrip('\n') | ||||
|                                         message = '\n' * (len(message) - len(msg_strip) + TextRedirect.StdoutRedirect.newlinecut[0]) + msg_strip | ||||
|                                         TextRedirect.StdoutRedirect.newlinecut.pop(0) | ||||
|                                         message = '\n' * (len(message) - len(msg_strip) + TextRedirect.Pretty.newlinecut[0]) + msg_strip | ||||
|                                         TextRedirect.Pretty.newlinecut.pop(0) | ||||
|  | ||||
|                         count = Counter(message) | ||||
|                         countab = (count['\t'] if count['\t'] != 0 else 1) | ||||
| @@ -220,25 +205,49 @@ class TextRedirect(object): | ||||
|                         return message | ||||
|  | ||||
|                 def textbox_do(self): | ||||
|                         msgs, TextRedirect.StdoutRedirect.tag_num = unshell_message(self.str_to_print, TextRedirect.StdoutRedirect.tag_num) | ||||
|                         msgs, TextRedirect.Pretty.tag_num = unshell_message(self.str_to_print, TextRedirect.Pretty.tag_num) | ||||
|                         for tag in msgs: | ||||
|                                 self.textbox_write(tag, msgs[tag]['text'], self.customcolors[msgs[tag]['color']], msgs[tag]['extra']) | ||||
|  | ||||
|         class StderrRedirect(StdoutRedirect):                 | ||||
|                 def __init__(self, srv_text_space, clt_text_space, customcolors): | ||||
|                 def flush(self): | ||||
|                         pass | ||||
|  | ||||
|                 def write(self, string): | ||||
|                         if string != '\n': | ||||
|                                 self.str_to_print = string | ||||
|                                 self.textbox_do() | ||||
|  | ||||
|         class Stderr(Pretty): | ||||
|                 def __init__(self, srv_text_space, clt_text_space, customcolors, side): | ||||
|                         self.srv_text_space = srv_text_space | ||||
|                         self.clt_text_space = clt_text_space | ||||
|                         self.customcolors = customcolors | ||||
|                         self.side = side | ||||
|                         self.tag_err = 'STDERR' | ||||
|                         self.xfont = tkFont.Font(font = self.srv_text_space['font']) | ||||
|  | ||||
|                 def textbox_choose(self, message): | ||||
|                         if self.side == "srv": | ||||
|                                 return self.srv_text_space | ||||
|                         elif self.side == "clt": | ||||
|                                 return self.clt_text_space | ||||
|                                                  | ||||
|                 def write(self, string): | ||||
|                         self.textbox_color(self.tag_err, self.srv_text_space, self.customcolors['red'], self.customcolors['black']) | ||||
|                         widget = self.textbox_choose(string) | ||||
|                         self.textbox_color(self.tag_err, widget, self.customcolors['red'], self.customcolors['black']) | ||||
|                         self.srv_text_space.configure(state = 'normal') | ||||
|                         self.srv_text_space.insert('end', string, self.tag_err) | ||||
|                         self.srv_text_space.see('end') | ||||
|                         self.srv_text_space.configure(state = 'disabled') | ||||
|  | ||||
|         class Log(Pretty): | ||||
|                 def textbox_format(self, message): | ||||
|                         if message.startswith('logsrv'): | ||||
|                                 message = message.replace('logsrv ', '') | ||||
|                         if message.startswith('logclt'): | ||||
|                                 message = message.replace('logclt ', '') | ||||
|                         return message + '\n' | ||||
|                  | ||||
| ##----------------------------------------------------------------------------------------------------------------------------------------------------------- | ||||
| class TextDoubleScroll(tk.Frame):  | ||||
|         def __init__(self, master, **kwargs): | ||||
| @@ -328,7 +337,7 @@ def custom_background(window): | ||||
|                         widget.configure(background = window.customcolors['lavender']) | ||||
|  | ||||
|         # Hide client. | ||||
|         window.clt_on_show(force = True) | ||||
|         window.clt_on_show(force_remove = True) | ||||
|         # Show Gui. | ||||
|         window.deiconify() | ||||
|  | ||||
| @@ -488,7 +497,12 @@ class ListboxOfRadiobuttons(tk.Frame): | ||||
|                         if st in ['STDOUT', 'FILEOFF']: | ||||
|                                 if wclass == 'Entry': | ||||
|                                         widget.delete(0, 'end') | ||||
|                                         widget.configure(state = "disabled") | ||||
|                                 elif wclass == 'TCombobox': | ||||
|                                         if st == 'STDOUT': | ||||
|                                                 widget.set(default) | ||||
|                                                 widget.configure(state = "readonly") | ||||
|                                         elif st == 'FILEOFF': | ||||
|                                                 widget.set('') | ||||
|                                                 widget.configure(state = "disabled") | ||||
|                         elif st in ['FILE', 'FILESTDOUT', 'STDOUTOFF']: | ||||
|   | ||||
| @@ -86,28 +86,104 @@ class LevelFormatter(logging.Formatter): | ||||
|                 formatter = self.formatters.get(record.levelno, self.default_fmt) | ||||
|                 return formatter.format(record) | ||||
|  | ||||
| # based on https://github.com/jruere/multiprocessing-logging (license LGPL-3.0) | ||||
| from multiprocessing import Queue as MPQueue | ||||
| try: | ||||
|     # Python 2.x imports | ||||
|     import Queue as Queue | ||||
| except ImportError: | ||||
|     # Python 3.x imports | ||||
|     import queue as Queue | ||||
| import threading | ||||
|  | ||||
| class MultiProcessingLogHandler(logging.Handler): | ||||
|         def __init__(self, name, handler = None): | ||||
|                 super(MultiProcessingLogHandler, self).__init__() | ||||
|                 self.queue = MPQueue(-1) | ||||
|                 if handler is None: | ||||
|                         handler = logging.StreamHandler() | ||||
|                 self.handler = handler | ||||
|                 self.name = handler.name | ||||
|  | ||||
|                 self.setLevel(self.handler.level) | ||||
|                 self.setFormatter(self.handler.formatter) | ||||
|                 self.filters = self.handler.filters | ||||
|  | ||||
|                 self.is_closed = False | ||||
|                 self.receive_thread = threading.Thread(target = self.receive, name = name) | ||||
|                 self.receive_thread.daemon = True | ||||
|                 self.receive_thread.start() | ||||
|  | ||||
|         def setFormatter(self, fmt): | ||||
|                 super(MultiProcessingLogHandler, self).setFormatter(fmt) | ||||
|                 self.handler.setFormatter(fmt) | ||||
|  | ||||
|         def emit(self, record): | ||||
|                 try: | ||||
|                         if record.args: | ||||
|                                 record.msg = record.msg %record.args | ||||
|                                 record.args = None | ||||
|                         if record.exc_info: | ||||
|                                 dummy = self.format(record) | ||||
|                                 record.exc_info = None | ||||
|                         self.queue.put_nowait(record) | ||||
|                 except (KeyboardInterrupt, SystemExit): | ||||
|                         raise | ||||
|                 except: | ||||
|                         self.handleError(record) | ||||
|  | ||||
|         def receive(self): | ||||
|                 while not (self.is_closed and self.queue.empty()): | ||||
|                         try: | ||||
|                                 record = self.queue.get(timeout = 0.2) | ||||
|                                 self.handler.emit(record) | ||||
|                         except (KeyboardInterrupt, SystemExit): | ||||
|                                 raise | ||||
|                         except EOFError: | ||||
|                                 break | ||||
|                         except Queue.Empty: | ||||
|                                 pass | ||||
|                         except: | ||||
|                                 logging.exception('Error in log handler.') | ||||
|                 self.queue.close() | ||||
|                 self.queue.join_thread() | ||||
|  | ||||
|         def close(self): | ||||
|                 if not self.is_closed: | ||||
|                         self.is_closed = True | ||||
|                         self.receive_thread.join(5.0) | ||||
|                         self.handler.close() | ||||
|                         super(MultiProcessingLogHandler, self).close() | ||||
|  | ||||
|  | ||||
| def logger_create(log_obj, config, mode = 'a'): | ||||
|         # Create new level. | ||||
|         add_logging_level('MINI', logging.CRITICAL + 10) | ||||
|         log_handlers = [] | ||||
|  | ||||
|         # Configure visualization. | ||||
|         log_handlers = [] | ||||
|         if any(opt in ['STDOUT', 'FILESTDOUT', 'STDOUTOFF'] for opt in config['logfile']): | ||||
|                 if 'STDOUTOFF' not in config['logfile']: | ||||
|                         # STDOUT. | ||||
|                         log_handlers.append(logging.StreamHandler(sys.stdout)) | ||||
|                 if any(opt in ['STDOUT', 'FILESTDOUT'] for opt in config['logfile']): | ||||
|                         # STDOUT or FILESTDOUT. | ||||
|                         hand_stdout = logging.StreamHandler(sys.stdout) | ||||
|                         hand_stdout.name = 'LogStdout' | ||||
|                         log_handlers.append(hand_stdout) | ||||
|                 if any(opt in ['STDOUTOFF', 'FILESTDOUT'] for opt in config['logfile']): | ||||
|                         # FILESTDOUT or STDOUTOFF. | ||||
|                         log_handlers.append(RotatingFileHandler(filename = config['logfile'][1], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512), | ||||
|                                                                 backupCount = 1, encoding = None, delay = 0)) | ||||
|                         # STDOUTOFF or FILESTDOUT. | ||||
|                         hand_rotate = RotatingFileHandler(filename = config['logfile'][1], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512), | ||||
|                                                           backupCount = 1, encoding = None, delay = 0) | ||||
|                         hand_rotate.name = 'LogRotate' | ||||
|                         log_handlers.append(hand_rotate) | ||||
|         elif 'FILEOFF' in config['logfile']: | ||||
|                 config['loglevel'] = 'ERROR' # for py-kms GUI: set a recognized level never used. | ||||
|                 log_handlers.append(logging.FileHandler(os.devnull)) | ||||
|                 hand_null = logging.FileHandler(os.devnull) | ||||
|                 hand_null.name = 'LogNull' | ||||
|                 log_handlers.append(hand_null) | ||||
|         else: | ||||
|                 # FILE. | ||||
|                 log_handlers.append(RotatingFileHandler(filename = config['logfile'][0], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512), | ||||
|                                                         backupCount = 1, encoding = None, delay = 0)) | ||||
|                 hand_rotate = RotatingFileHandler(filename = config['logfile'][0], mode = mode, maxBytes = int(config['logsize'] * 1024 * 512), | ||||
|                                                   backupCount = 1, encoding = None, delay = 0) | ||||
|                 hand_rotate.name = 'LogRotate' | ||||
|                 log_handlers.append(hand_rotate) | ||||
|  | ||||
|         # Configure formattation. | ||||
|         try: | ||||
| @@ -115,27 +191,38 @@ def logger_create(log_obj, config, mode = 'a'): | ||||
|         except AttributeError: | ||||
|                 levelnames = logging._levelNames | ||||
|         levelnum = [k for k in levelnames if k != 0] | ||||
|         frmt0 = '%(asctime)s %(levelname)-8s %(message)s' | ||||
|         frmt1 = '[%(asctime)s] [%(levelname)-8s]   %(host)s   %(status)s   %(product)s   %(message)s' | ||||
|  | ||||
|         frmt_gen = '%(asctime)s %(levelname)-8s %(message)s' | ||||
|         frmt_std = '%(name)s %(asctime)s %(levelname)-8s %(message)s' | ||||
|         frmt_min = '[%(asctime)s] [%(levelname)-8s]   %(host)s   %(status)s   %(product)s   %(message)s' | ||||
|  | ||||
|         def apply_formatter(levelnum, formats, handler, color = False): | ||||
|                 levelformdict = {} | ||||
|                 for num in levelnum: | ||||
|                         if num != logging.CRITICAL + 10: | ||||
|                         levelformdict[num] = frmt0 | ||||
|                                 levelformdict[num] = formats[0] | ||||
|                         else: | ||||
|                         levelformdict[num] = frmt1 | ||||
|                                 levelformdict[num] = formats[1] | ||||
|                 handler.setFormatter(LevelFormatter(levelformdict, color = color)) | ||||
|                 return handler | ||||
|  | ||||
|         # Clear old handlers. | ||||
|         if log_obj.handlers: | ||||
|                 log_obj.handlers = [] | ||||
|  | ||||
|         # Set level and format. | ||||
|         levelformdictcopy = levelformdict.copy() | ||||
|         for log_handler in log_handlers: | ||||
|                 log_handler.setLevel(config['loglevel']) | ||||
|                 if log_handler.__class__.__name__ == 'StreamHandler': | ||||
|                         log_handler.setFormatter(LevelFormatter(levelformdict, color = True)) | ||||
|                 elif log_handler.__class__.__name__ == 'RotatingFileHandler': | ||||
|                         log_handler.setFormatter(LevelFormatter(levelformdictcopy, color = False)) | ||||
|  | ||||
|                 if log_handler.name in ['LogStdout']: | ||||
|                         log_handler = apply_formatter(levelnum, (frmt_std, frmt_min), log_handler, color = True) | ||||
|                 elif log_handler.name in ['LogRotate']: | ||||
|                         log_handler = apply_formatter(levelnum, (frmt_gen, frmt_min), log_handler) | ||||
|                 # Attach. | ||||
|                 if config['asyncmsg']: | ||||
|                         log_obj.addHandler(MultiProcessingLogHandler('Thread-AsyncMsg{0}'.format(log_handler.name), handler = log_handler)) | ||||
|                 else: | ||||
|                         log_obj.addHandler(log_handler) | ||||
|  | ||||
|         log_obj.setLevel(config['loglevel']) | ||||
|         [ log_obj.addHandler(log_handler) for log_handler in log_handlers ] | ||||
|  | ||||
| #------------------------------------------------------------------------------------------------------------------------------------------------------------ | ||||
|  | ||||
| @@ -144,14 +231,16 @@ def check_logfile(optionlog, defaultlog, where): | ||||
|                 optionlog = [optionlog] | ||||
|  | ||||
|         lenopt = len(optionlog) | ||||
|         msg_dir  = "{reverse}{red}{bold}argument logfile: invalid directory: '%s'. Exiting...{end}" | ||||
|         msg_long = "{reverse}{red}{bold}argument logfile: too much arguments. Exiting...{end}" | ||||
|         msg_log = "{reverse}{red}{bold}argument logfile: not a log file, invalid extension: '%s'. Exiting...{end}" | ||||
|         msg_dir  = "{reverse}{red}{bold}argument `-F/--logfile`: invalid directory: '%s'. Exiting...{end}" | ||||
|         msg_long = "{reverse}{red}{bold}argument `-F/--logfile`: too much arguments. Exiting...{end}" | ||||
|         msg_log = "{reverse}{red}{bold}argument `-F/--logfile`: not a log file, invalid extension: '%s'. Exiting...{end}" | ||||
|  | ||||
|         def checkdir(path): | ||||
|                 filename = os.path.basename(path) | ||||
|                 pathname = os.path.dirname(path) | ||||
|                 if not os.path.isdir(pathname): | ||||
|                         if path.count('/') == 0: | ||||
|                                 pathname = filename | ||||
|                         pretty_printer(put_text = msg_dir %pathname, where = where, to_exit = True) | ||||
|                 elif not filename.lower().endswith('.log'): | ||||
|                         pretty_printer(put_text = msg_log %filename, where = where, to_exit = True) | ||||
| @@ -159,7 +248,6 @@ def check_logfile(optionlog, defaultlog, where): | ||||
|         if lenopt > 2: | ||||
|                 pretty_printer(put_text = msg_long, where = where, to_exit = True) | ||||
|  | ||||
|  | ||||
|         if (any(opt in ['FILESTDOUT', 'STDOUTOFF'] for opt in optionlog)): | ||||
|                 if lenopt == 1: | ||||
|                         # add default logfile. | ||||
| @@ -215,8 +303,7 @@ def check_lcid(lcid, log_obj): | ||||
|                                 fixlcid = next(k for k, v in locale.windows_locale.items() if v == locale.getdefaultlocale()[0]) | ||||
|                         except StopIteration: | ||||
|                                 fixlcid = 1033 | ||||
|                 pretty_printer(log_obj = log_obj, | ||||
|                                put_text = "{reverse}{yellow}{bold}LCID %s auto-fixed with LCID %s{end}" %(lcid, fixlcid)) | ||||
|                 pretty_printer(log_obj = log_obj, put_text = "{reverse}{yellow}{bold}LCID '%s' auto-fixed with LCID '%s'{end}" %(lcid, fixlcid)) | ||||
|                 return fixlcid | ||||
|         return lcid | ||||
|  | ||||
| @@ -262,28 +349,123 @@ class KmsParserHelp(object): | ||||
|                 print(parser_base.epilog + '\n') | ||||
|                 parser_base.exit() | ||||
|  | ||||
| def kms_parser_get(parser): | ||||
|         zeroarg, onearg = ([] for _ in range(2)) | ||||
|         act = vars(parser)['_actions'] | ||||
|         for i in range(len(act)): | ||||
|                 if act[i].option_strings not in ([], ['-h', '--help']): | ||||
|                         if isinstance(act[i], argparse._StoreAction): | ||||
|                                 onearg.append(act[i].option_strings) | ||||
|                         else: | ||||
|                                 zeroarg.append(act[i].option_strings) | ||||
|         return zeroarg, onearg | ||||
|  | ||||
| def kms_parser_check_optionals(userarg, zeroarg, onearg, msg = 'optional py-kms server', exclude_opt_len = []): | ||||
|         """ | ||||
|         For optionals arguments: | ||||
|         Don't allow duplicates, | ||||
|         Don't allow abbreviations, | ||||
|         Don't allow joining and not existing arguments, | ||||
|         Checks length values passed to arguments. | ||||
|         """ | ||||
|         zeroarg = [item for sublist in zeroarg for item in sublist] | ||||
|         onearg = [item for sublist in onearg for item in sublist] | ||||
|         allarg = zeroarg + onearg | ||||
|  | ||||
|         def is_abbrev(allarg, arg_to_check): | ||||
|                 for opt in allarg: | ||||
|                         if len(opt) > 2 and opt[2] == arg_to_check[2]: | ||||
|                                 for indx in range(-1, -len(opt), -1): | ||||
|                                         if opt[:indx] == arg_to_check: | ||||
|                                                 raise KmsParserException("%s argument `%s` abbreviation not allowed for `%s`" %(msg, arg_to_check, opt)) | ||||
|                 return False | ||||
|  | ||||
|         # Check abbreviations, joining, not existing. | ||||
|         for arg in userarg: | ||||
|                 if arg not in allarg: | ||||
|                         if arg.startswith('-'): | ||||
|                                 if arg == '--' or arg[:2] != '--' or not is_abbrev(allarg, arg): | ||||
|                                         raise KmsParserException("unrecognized %s arguments: `%s`" %(msg, arg)) | ||||
|  | ||||
|         # Check duplicates. | ||||
|         founds = [i for i in userarg if i in allarg] | ||||
|         dup = [item for item in set(founds) if founds.count(item) > 1] | ||||
|         if dup != []: | ||||
|                 raise KmsParserException("%s argument `%s` appears several times" %(msg, ', '.join(dup))) | ||||
|  | ||||
|         # Check length. | ||||
|         elem = None | ||||
|         for found in founds: | ||||
|                 if found not in exclude_opt_len: | ||||
|                         pos = userarg.index(found) | ||||
|                         try: | ||||
|                                 if found in zeroarg: | ||||
|                                         elem = userarg[pos + 1] | ||||
|                                         num = "zero arguments," | ||||
|                                 elif found in onearg: | ||||
|                                         elem = userarg[pos + 2] | ||||
|                                         num = "one argument," | ||||
|                         except IndexError: | ||||
|                                 pass | ||||
|  | ||||
|                         if elem and elem not in allarg: | ||||
|                                 raise KmsParserException("%s argument `" %msg + found + "`:" + " expected " + num + " unrecognized: '%s'" %elem) | ||||
|  | ||||
| def kms_parser_check_positionals(config, parse_method, arguments = None, msg = 'positional py-kms server'): | ||||
|         try: | ||||
|                 if arguments: | ||||
|                         config.update(vars(parse_method(arguments))) | ||||
|                 else: | ||||
|                         config.update(vars(parse_method())) | ||||
|         except KmsParserException as e: | ||||
|                 e = str(e) | ||||
|                 if e.startswith('argument'): | ||||
|                         raise | ||||
|                 else: | ||||
|                         raise KmsParserException("unrecognized %s arguments: '%s'" %(msg, e.split(': ')[1])) | ||||
|  | ||||
| #------------------------------------------------------------------------------------------------------------------------------------------------------------ | ||||
| def proper_none(dictionary): | ||||
|         for key in dictionary.keys(): | ||||
|                 dictionary[key] = None if dictionary[key] == 'None' else dictionary[key] | ||||
|  | ||||
| def check_setup(config, options, logger, where): | ||||
|         # 'None'--> None. | ||||
|         proper_none(config) | ||||
|  | ||||
|         # Check logfile. | ||||
|         config['logfile'] = check_logfile(config['logfile'], options['lfile']['def'], where = where) | ||||
|  | ||||
|         # Setup hidden or not messages. | ||||
|         # Check logsize (py-kms Gui). | ||||
|         if config['logsize'] == "": | ||||
|                 if any(opt in ['STDOUT', 'FILEOFF'] for opt in config['logfile']): | ||||
|                         # set a recognized size never used. | ||||
|                         config['logsize'] = 0 | ||||
|                 else: | ||||
|                         pretty_printer(put_text = "{reverse}{red}{bold}argument `-S/--logsize`: invalid with: '%s'. Exiting...{end}" %config['logsize'], | ||||
|                                        where = where, to_exit = True) | ||||
|  | ||||
|         # Check loglevel (py-kms Gui). | ||||
|         if config['loglevel'] == "": | ||||
|                 # set a recognized level never used. | ||||
|                 config['loglevel'] = 'ERROR' | ||||
|  | ||||
|         # Setup hidden / asynchronous messages. | ||||
|         hidden = ['STDOUT', 'FILESTDOUT', 'STDOUTOFF'] | ||||
|         ShellMessage.view = (False if any(opt in hidden for opt in config['logfile']) else True) | ||||
|         view_flag = (False if any(opt in hidden for opt in config['logfile']) else True) | ||||
|         if where == 'srv': | ||||
|                 ShellMessage.viewsrv = view_flag | ||||
|                 ShellMessage.asyncmsgsrv = config['asyncmsg'] | ||||
|         elif where == 'clt': | ||||
|                 ShellMessage.viewclt = view_flag | ||||
|                 ShellMessage.asyncmsgclt = config['asyncmsg'] | ||||
|  | ||||
|         # Create log. | ||||
|         logger_create(logger, config, mode = 'a') | ||||
|  | ||||
|         # 'None'--> None. | ||||
|         proper_none(config) | ||||
|  | ||||
|         # Check port. | ||||
|         if not 1 <= config['port'] <= 65535: | ||||
|                 pretty_printer(log_obj = logger.error, to_exit = True, | ||||
|         if (config['port'] == "") or (not 1 <= config['port'] <= 65535): | ||||
|                 pretty_printer(log_obj = logger.error, where = where, to_exit = True, | ||||
|                                put_text = "{reverse}{red}{bold}Port number '%s' is invalid. Enter between 1 - 65535. Exiting...{end}" %config['port']) | ||||
|  | ||||
| #------------------------------------------------------------------------------------------------------------------------------------------------------------ | ||||
|   | ||||
| @@ -29,6 +29,7 @@ from pykms_RpcBase import rpcBase | ||||
| from pykms_Dcerpc import MSRPCHeader | ||||
| from pykms_Misc import check_setup, check_lcid | ||||
| from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp | ||||
| from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals | ||||
| from pykms_Format import enco, deco, pretty_printer | ||||
| from Etrigan import Etrigan, Etrigan_parser, Etrigan_check, Etrigan_job | ||||
|  | ||||
| @@ -148,12 +149,15 @@ class server_thread(threading.Thread): | ||||
|                                                 # Create and run server. | ||||
|                                                 self.server = server_create() | ||||
|                                                 self.server.pykms_serve() | ||||
|                                 except SystemExit as e: | ||||
|                                 except (SystemExit, Exception) as e: | ||||
|                                         self.eject = True | ||||
|                                         if not self.with_gui: | ||||
|                                                 raise | ||||
|                                         else: | ||||
|                                                 if isinstance(e, SystemExit): | ||||
|                                                         continue | ||||
|                                                 else: | ||||
|                                                         raise | ||||
|  | ||||
| ##--------------------------------------------------------------------------------------------------------------------------------------------------------- | ||||
|  | ||||
| @@ -168,17 +172,19 @@ srv_options = { | ||||
|         'lcid' : {'help' : 'Use this option to manually specify an LCID for use with randomly generated ePIDs. Default is \"1033\" (en-us)', | ||||
|                   'def' : 1033, 'des' : "lcid"}, | ||||
|         'count' : {'help' : 'Use this option to specify the current client count. A number >=25 is required to enable activation of client OSes; \ | ||||
| for server OSes and Office >=5', 'def' : None, 'des' : "CurrentClientCount"}, | ||||
| for server OSes and Office >=5', 'def' : None, 'des' : "clientcount"}, | ||||
|         'activation' : {'help' : 'Use this option to specify the activation interval (in minutes). Default is \"120\" minutes (2 hours).', | ||||
|                         'def' : 120, 'des': "VLActivationInterval"}, | ||||
|                         'def' : 120, 'des': "activation"}, | ||||
|         'renewal' : {'help' : 'Use this option to specify the renewal interval (in minutes). Default is \"10080\" minutes (7 days).', | ||||
|                      'def' : 1440 * 7, 'des' : "VLRenewalInterval"}, | ||||
|                      'def' : 1440 * 7, 'des' : "renewal"}, | ||||
|         'sql' : {'help' : 'Use this option to store request information from unique clients in an SQLite database. Desactivated by default.', | ||||
|                  'def' : False, 'des' : "sqlite"}, | ||||
|         'hwid' : {'help' : 'Use this option to specify a HWID. The HWID must be an 16-character string of hex characters. \ | ||||
| The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID.', 'def' : "364F463A8863D35F", 'des' : "hwid"}, | ||||
|         'time0' : {'help' : 'Maximum inactivity time (in seconds) after which the connection with the client is closed. If \"None\" (default) serve forever.', | ||||
|                    'def' : None, 'des' : "timeout_idle"}, | ||||
|                    'def' : None, 'des' : "timeoutidle"}, | ||||
|         'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Desactivated by default.', | ||||
|                       'def' : False, 'des' : "asyncmsg"}, | ||||
|         'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel", | ||||
|                     'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]}, | ||||
|         'lfile' : {'help' : 'Use this option to set an output log file. The default is \"pykms_logserver.log\". \ | ||||
| @@ -189,11 +195,7 @@ Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to | ||||
|         } | ||||
|  | ||||
| def server_options(): | ||||
|         try: | ||||
|                 server_parser = KmsParser(description = srv_description, epilog = 'version: ' + srv_version, add_help = False, allow_abbrev = False) | ||||
|         except TypeError: | ||||
|         server_parser = KmsParser(description = srv_description, epilog = 'version: ' + srv_version, add_help = False) | ||||
|  | ||||
|         server_parser.add_argument("ip", nargs = "?", action = "store", default = srv_options['ip']['def'], help = srv_options['ip']['help'], type = str) | ||||
|         server_parser.add_argument("port", nargs = "?", action = "store", default = srv_options['port']['def'], help = srv_options['port']['help'], type = int) | ||||
|         server_parser.add_argument("-e", "--epid", action = "store", dest = srv_options['epid']['des'], default = srv_options['epid']['def'], | ||||
| @@ -204,85 +206,114 @@ def server_options(): | ||||
|                                    help = srv_options['count']['help'], type = str) | ||||
|         server_parser.add_argument("-a", "--activation-interval", action = "store", dest = srv_options['activation']['des'], | ||||
|                                    default = srv_options['activation']['def'], help = srv_options['activation']['help'], type = int) | ||||
|         server_parser.add_argument("-r", "--renewal-interval", action = "store", dest = srv_options['renewal']['des'], default = srv_options['renewal']['def'], | ||||
|                                    help = srv_options['renewal']['help'], type = int) | ||||
|         server_parser.add_argument("-s", "--sqlite", action = "store_const", dest = srv_options['sql']['des'], const = True, default = srv_options['sql']['def'], | ||||
|                                    help = srv_options['sql']['help']) | ||||
|         server_parser.add_argument("-r", "--renewal-interval", action = "store", dest = srv_options['renewal']['des'], | ||||
|                                    default = srv_options['renewal']['def'], help = srv_options['renewal']['help'], type = int) | ||||
|         server_parser.add_argument("-s", "--sqlite", action = "store_true", dest = srv_options['sql']['des'], | ||||
|                                    default = srv_options['sql']['def'], help = srv_options['sql']['help']) | ||||
|         server_parser.add_argument("-w", "--hwid", action = "store", dest = srv_options['hwid']['des'], default = srv_options['hwid']['def'], | ||||
|                                    help = srv_options['hwid']['help'], type = str) | ||||
|         server_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = srv_options['time0']['des'], default = srv_options['time0']['def'], | ||||
|                                    help = srv_options['time0']['help'], type = str) | ||||
|         server_parser.add_argument("-y", "--async-msg", action = "store_true", dest = srv_options['asyncmsg']['des'], | ||||
|                                    default = srv_options['asyncmsg']['def'], help = srv_options['asyncmsg']['help']) | ||||
|         server_parser.add_argument("-V", "--loglevel", action = "store", dest = srv_options['llevel']['des'], choices = srv_options['llevel']['choi'], | ||||
|                                    default = srv_options['llevel']['def'], help = srv_options['llevel']['help'], type = str) | ||||
|         server_parser.add_argument("-F", "--logfile", nargs = "+", action = "store", dest = srv_options['lfile']['des'], default = srv_options['lfile']['def'], | ||||
|                                    help = srv_options['lfile']['help'], type = str) | ||||
|         server_parser.add_argument("-F", "--logfile", nargs = "+", action = "store", dest = srv_options['lfile']['des'], | ||||
|                                    default = srv_options['lfile']['def'], help = srv_options['lfile']['help'], type = str) | ||||
|         server_parser.add_argument("-S", "--logsize", action = "store", dest = srv_options['lsize']['des'], default = srv_options['lsize']['def'], | ||||
|                                    help = srv_options['lsize']['help'], type = float) | ||||
|  | ||||
|         server_parser.add_argument("-h", "--help", action = "help", help = "show this help message and exit") | ||||
|  | ||||
|         try: | ||||
|                 daemon_parser = KmsParser(description = "daemon options inherited from Etrigan", add_help = False, allow_abbrev = False) | ||||
|         except TypeError: | ||||
|         daemon_parser = KmsParser(description = "daemon options inherited from Etrigan", add_help = False) | ||||
|  | ||||
|         daemon_subparser = daemon_parser.add_subparsers(dest = "mode") | ||||
|         try: | ||||
|                 etrigan_parser = daemon_subparser.add_parser("etrigan", add_help = False, allow_abbrev = False) | ||||
|         except TypeError: | ||||
|  | ||||
|         etrigan_parser = daemon_subparser.add_parser("etrigan", add_help = False) | ||||
|         etrigan_parser.add_argument("-g", "--gui", action = "store_const", dest = 'gui', const = True, default = False, | ||||
|                                     help = "Enable py-kms GUI usage.") | ||||
|         etrigan_parser = Etrigan_parser(parser = etrigan_parser) | ||||
|  | ||||
|         try: | ||||
|                 if "-h" in sys.argv[1:]: | ||||
|                 userarg = sys.argv[1:] | ||||
|  | ||||
|                 # Run help. | ||||
|                 if any(arg in ["-h", "--help"] for arg in userarg): | ||||
|                         KmsParserHelp().printer(parsers = [server_parser, daemon_parser, etrigan_parser]) | ||||
|  | ||||
|                 # Set defaults for config. | ||||
|                 # case: python3 pykms_Server.py | ||||
|                 srv_config.update(vars(server_parser.parse_args([]))) | ||||
|                 # Eventually set daemon values for config. | ||||
|                 if 'etrigan' in sys.argv[1:]: | ||||
|                         if 'etrigan' == sys.argv[1]: | ||||
|                                 # case: python3 pykms_Server.py etrigan start --daemon_optionals | ||||
|                                 srv_config.update(vars(daemon_parser.parse_args(sys.argv[1:]))) | ||||
|                         elif 'etrigan' == sys.argv[2]: | ||||
|                                 # case: python3 pykms_Server.py 1.2.3.4 etrigan start --daemon_optionals | ||||
|                                 srv_config['ip'] = sys.argv[1] | ||||
|                                 srv_config.update(vars(daemon_parser.parse_args(sys.argv[2:]))) | ||||
|                         else: | ||||
|                                 # case: python3 pykms_Server.py 1.2.3.4 1234 --main_optionals etrigan start --daemon_optionals | ||||
|                                 knw_args, knw_extras = server_parser.parse_known_args() | ||||
|                                 # fix for logfile option (at the end) that catchs etrigan parser options. | ||||
|                                 if 'etrigan' in knw_args.logfile: | ||||
|                                         indx = knw_args.logfile.index('etrigan') | ||||
|                                         for num, elem in enumerate(knw_args.logfile[indx:]): | ||||
|                                                 knw_extras.insert(num, elem) | ||||
|                                         knw_args.logfile = knw_args.logfile[:indx] | ||||
|                 # Get stored arguments. | ||||
|                 pykmssrv_zeroarg, pykmssrv_onearg = kms_parser_get(server_parser) | ||||
|                 etrigan_zeroarg, etrigan_onearg = kms_parser_get(etrigan_parser) | ||||
|                 pykmssrv_zeroarg += ['etrigan'] # add subparser | ||||
|  | ||||
|                                 # continue parsing. | ||||
|                                 if len(knw_extras) > 0 and knw_extras[0] in ['etrigan']: | ||||
|                                         daemon_parser.parse_args(knw_extras, namespace = knw_args) | ||||
|                                 srv_config.update(vars(knw_args)) | ||||
|                 else: | ||||
|                         # Update dict config. | ||||
|                         # case: python3 pykms_Server.py 1.2.3.4 1234 --main_optionals | ||||
|                         knw_args, knw_extras = server_parser.parse_known_args() | ||||
|                         if knw_extras != []: | ||||
|                                 raise KmsParserException("unrecognized arguments: %s" %' '.join(knw_extras)) | ||||
|                         else: | ||||
|                                 srv_config.update(vars(knw_args)) | ||||
|                 # Set defaults for config. | ||||
|                 # example case: | ||||
|                 #               python3 pykms_Server.py | ||||
|                 srv_config.update(vars(server_parser.parse_args([]))) | ||||
|  | ||||
|                 try: | ||||
|                         # Eventually set daemon options for dict server config. | ||||
|                         pos = sys.argv[1:].index('etrigan') | ||||
|                         # example cases: | ||||
|                         #               python3 pykms_Server.py etrigan start | ||||
|                         #               python3 pykms_Server.py etrigan start --daemon_optionals | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 etrigan start | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 etrigan start --daemon_optionals | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 1234 etrigan start | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 1234 etrigan start --daemon_optionals | ||||
|                         #               python3 pykms_Server.py --pykms_optionals etrigan start | ||||
|                         #               python3 pykms_Server.py --pykms_optionals etrigan start --daemon_optionals | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 --pykms_optionals etrigan start | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 --pykms_optionals etrigan start --daemon_optionals | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 1234 --pykms_optionals etrigan start | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 1234 --pykms_optionals etrigan start --daemon_optionals | ||||
|  | ||||
|                         kms_parser_check_optionals(userarg[0:pos], pykmssrv_zeroarg, pykmssrv_onearg, exclude_opt_len = ['-F', '--logfile']) | ||||
|                         kms_parser_check_positionals(srv_config, server_parser.parse_args, arguments = userarg[0:pos]) | ||||
|                         kms_parser_check_optionals(userarg[pos:], etrigan_zeroarg, etrigan_onearg, msg = 'optional etrigan') | ||||
|                         kms_parser_check_positionals(srv_config, daemon_parser.parse_args, arguments = userarg[pos:], msg = 'positional etrigan') | ||||
|  | ||||
|                 except ValueError: | ||||
|                         # Update pykms options for dict server config. | ||||
|                         # example cases: | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 --pykms_optionals | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 1234 | ||||
|                         #               python3 pykms_Server.py 1.2.3.4 1234 --pykms_optionals | ||||
|                         #               python3 pykms_Server.py --pykms_optionals | ||||
|  | ||||
|                         kms_parser_check_optionals(userarg, pykmssrv_zeroarg, pykmssrv_onearg, exclude_opt_len = ['-F', '--logfile']) | ||||
|                         kms_parser_check_positionals(srv_config, server_parser.parse_args) | ||||
|  | ||||
|         except KmsParserException as e: | ||||
|                 pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True) | ||||
|  | ||||
|  | ||||
| class Etrigan_Check(Etrigan_check): | ||||
|         def emit_opt_err(self, msg): | ||||
|                 pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %msg, to_exit = True) | ||||
|  | ||||
| class Etrigan(Etrigan): | ||||
|         def emit_message(self, message, to_exit = False): | ||||
|                 if not self.mute: | ||||
|                         pretty_printer(put_text = "{reverse}{green}{bold}%s{end}" %message) | ||||
|                 if to_exit: | ||||
|                         sys.exit(0) | ||||
|  | ||||
|         def emit_error(self, message, to_exit = True): | ||||
|                 if not self.mute: | ||||
|                         pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %message, to_exit = True) | ||||
|  | ||||
| def server_daemon(): | ||||
|         if 'etrigan' in srv_config.values(): | ||||
|                 path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pykms_config.pickle') | ||||
|  | ||||
|                 if srv_config['operation'] in ['stop', 'restart', 'status'] and len(sys.argv[1:]) > 2: | ||||
|                         pretty_printer(put_text = "{reverse}{red}{bold}too much arguments. Exiting...{end}", to_exit = True) | ||||
|                         pretty_printer(put_text = "{reverse}{red}{bold}too much arguments with etrigan '%s'. Exiting...{end}" %srv_config['operation'], | ||||
|                                        to_exit = True) | ||||
|  | ||||
|                 # Check file arguments. | ||||
|                 Etrigan_Check().checkfile(srv_config['etriganpid'], '--etrigan-pid', '.pid') | ||||
|                 Etrigan_Check().checkfile(srv_config['etriganlog'], '--etrigan-log', '.log') | ||||
|  | ||||
|                 if srv_config['gui']: | ||||
|                         pass | ||||
| @@ -357,21 +388,30 @@ def server_check(): | ||||
|         else: | ||||
|                 srv_config['dbSupport'] = True | ||||
|  | ||||
|         # Check client count, timeout. | ||||
|         list_dest = ['CurrentClientCount', 'timeout_idle'] | ||||
|         list_opt = ['--client-count', '--timeout-idle'] | ||||
|  | ||||
|         # Check other specific server options. | ||||
|         list_dest = ['clientcount', 'timeoutidle'] | ||||
|         list_opt = ['-c/--client-count', '-t0/--timeout-idle'] | ||||
|  | ||||
|         if serverthread.with_gui: | ||||
|                 list_dest += ['activation', 'renewal'] | ||||
|                 list_opt += ['-a/--activation-interval', '-r/--renewal-interval'] | ||||
|  | ||||
|         for dest, opt in zip(list_dest, list_opt): | ||||
|                 if srv_config[dest] is not None: | ||||
|                         if not srv_config[dest].isdigit(): | ||||
|                 value = srv_config[dest] | ||||
|                 if (value is not None) and (not isinstance(value, int)): | ||||
|                         pretty_printer(log_obj = loggersrv.error, to_exit = True, | ||||
|                                                put_text = "{reverse}{red}{bold}Option %s is invalid with '%s'. Exiting...{end}" | ||||
|                                                %(opt, srv_config[dest])) | ||||
|                         else: | ||||
|                                 srv_config[dest] = int(srv_config[dest]) | ||||
|                                        put_text = "{reverse}{red}{bold}argument `%s`: invalid with: '%s'. Exiting...{end}" %(opt, value)) | ||||
|  | ||||
| def server_create(): | ||||
|         try: | ||||
|                 server = KeyServer((srv_config['ip'], srv_config['port']), kmsServerHandler) | ||||
|         server.timeout = srv_config['timeout_idle'] | ||||
|         except (socket.gaierror, socket.error) as e: | ||||
|                 pretty_printer(log_obj = loggersrv.error, to_exit = True, | ||||
|                                put_text = "{reverse}{red}{bold}Connection failed '%s:%d': %s. Exiting...{end}" %(srv_config['ip'], | ||||
|                                                                                                                 srv_config['port'], | ||||
|                                                                                                                 str(e))) | ||||
|         server.timeout = srv_config['timeoutidle'] | ||||
|         loggersrv.info("TCP server listening at %s on port %d." % (srv_config['ip'], srv_config['port'])) | ||||
|         loggersrv.info("HWID: %s" % deco(binascii.b2a_hex(srv_config['hwid']), 'utf-8').upper()) | ||||
|         return server | ||||
| @@ -426,20 +466,8 @@ def server_main_terminal(): | ||||
| def server_with_gui(): | ||||
|         import pykms_GuiBase | ||||
|  | ||||
|         width = 950 | ||||
|         height = 660 | ||||
|  | ||||
|         root = pykms_GuiBase.KmsGui() | ||||
|         root.title(pykms_GuiBase.gui_description + ' (' + pykms_GuiBase.gui_version + ')') | ||||
|         # Main window initial position. | ||||
|         ## https://stackoverflow.com/questions/14910858/how-to-specify-where-a-tkinter-window-opens | ||||
|         ws = root.winfo_screenwidth() | ||||
|         hs = root.winfo_screenheight() | ||||
|         x = (ws / 2) - (width / 2) | ||||
|         y = (hs / 2) - (height / 2) | ||||
|         root.geometry('+%d+%d' %(x, y)) | ||||
|         # disable maximize button. | ||||
|         root.resizable(0, 0) | ||||
|         root.mainloop() | ||||
|  | ||||
| def server_main_no_terminal(): | ||||
| @@ -449,7 +477,7 @@ def server_main_no_terminal(): | ||||
|  | ||||
| class kmsServerHandler(socketserver.BaseRequestHandler): | ||||
|         def setup(self): | ||||
|                 loggersrv.info("Connection accepted: %s:%d" % (self.client_address[0], self.client_address[1])) | ||||
|                 loggersrv.info("Connection accepted: %s:%d" %(self.client_address[0], self.client_address[1])) | ||||
|  | ||||
|         def handle(self): | ||||
|                 while True: | ||||
| @@ -499,7 +527,7 @@ class kmsServerHandler(socketserver.BaseRequestHandler): | ||||
|  | ||||
|         def finish(self): | ||||
|                 self.request.close() | ||||
|                 loggersrv.info("Connection closed: %s:%d" % (self.client_address[0], self.client_address[1])) | ||||
|                 loggersrv.info("Connection closed: %s:%d" %(self.client_address[0], self.client_address[1])) | ||||
|  | ||||
|  | ||||
| serverqueue = Queue.Queue(maxsize = 0) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user