configure method

bool configure({
  1. ArgParser? argp,
  2. OptionResultsHandler? optionsHandler,
})

Configure the link. If argp is provided for argument parsing, it is used. This includes:

  • processing command-line arguments
  • setting broker urls
  • loading dsalink.json files
  • loading or creating private keys

Implementation

bool configure({ArgParser? argp, OptionResultsHandler? optionsHandler}) {
  _configured = true;

  if (link != null) {
    link?.close();
    link = null;
  }

  argp ??= _argp ?? (_argp = ArgParser(allowTrailingOptions: !strictOptions));

  argp.addOption(
    'broker',
    abbr: 'b',
    help: 'Broker URL',
    defaultsTo: 'http://127.0.0.1:8080/conn',
  );
  argp.addOption('name', abbr: 'n', help: 'Link Name');
  argp.addOption('home', help: 'Home');
  argp.addOption('token', help: 'Token');
  argp.addOption('base-path', help: 'Base Path for dsalink');
  argp.addOption('watch-file', help: 'Watch File for dsalink', hide: true);
  argp.addOption('log-file', help: 'Log File for dsalink');

  var logLevelNames = Level.LEVELS.map(_logLevelToName).toList();
  logLevelNames.addAll(['auto', 'debug']);

  argp.addOption(
    'log',
    abbr: 'l',
    allowed: logLevelNames,
    help: 'Log Level',
    defaultsTo: 'AUTO',
  );
  argp.addFlag(
    'help',
    abbr: 'h',
    help: 'Displays this Help Message',
    negatable: false,
  );
  argp.addFlag(
    'discover',
    abbr: 'd',
    help: 'Automatically Discover a Broker',
    negatable: false,
  );
  argp.addFlag(
    'strictTls',
    help:
        'Enforces valid SSL/TLS certificates for secure connections to '
        'broker.',
  );

  var opts = _parsedArguments = argp.parse(args);

  if (opts['log'] == 'auto') {
    if (DEBUG_MODE) {
      UpdateLogLevel('all');
    } else {
      UpdateLogLevel(defaultLogLevel);
    }
  } else {
    UpdateLogLevel(opts['log']);
  }

  if (opts['base-path'] != null) {
    _basePath = opts['base-path'];

    if (_basePath.endsWith('/')) {
      _basePath = _basePath.substring(0, _basePath.length - 1);
    }
  }

  if (opts['watch-file'] != null) {
    _watchFile = opts['watch-file'];
  }

  _logFile = opts['log-file'];
  if (opts['strictTls']) {
    strictTls = true;
  }

  if (_logFile != null) {
    var file = File(_logFile!);
    if (!file.existsSync()) {
      file.createSync(recursive: true);
    }
    logger.clearListeners();
    var out = _logFileOut = file.openWrite(mode: FileMode.append);
    logger.onRecord.listen((record) {
      out.writeln(
        '[${DateTime.now()}][${record.level.name}] ${record.message}',
      );
      if (record.error != null) {
        out.writeln(record.error);
      }

      if (record.stackTrace != null) {
        out.writeln(record.stackTrace);
      }

      out.flush();
    });
  }

  if (_watchFile != null) {
    var file = File(_watchFile!);
    StreamSubscription? sub;
    sub = file.watch(events: FileSystemEvent.delete).listen((_) {
      close();
      sub?.cancel();

      if (_logFileOut != null) {
        try {
          _logFileOut?.close();
        } catch (e) {
          logger.finest('Error closing log file.', e);
        }
      }
    });
  }

  if (const bool.fromEnvironment(
    'dsalink.debugger.console',
    defaultValue: false,
  )) {
    readStdinLines().listen((String cmd) {
      if (cmd == 'list-stored-nodes') {
        if (provider is SimpleNodeProvider) {
          var prov = provider as SimpleNodeProvider;
          print(prov.nodes.keys.join('\n'));
        } else {
          print('Not a SimpleNodeProvider.');
        }
      } else if (cmd == 'list-stub-nodes') {
        if (provider is SimpleNodeProvider) {
          var prov = provider as SimpleNodeProvider;
          for (var node in prov.nodes.values) {
            var p = Path(node.path);
            if (prov.nodes[p.parentPath] == null) {
              print(node.path);
            } else if (!prov.nodes[p.parentPath]!.children.containsKey(
              p.name,
            )) {
              print(node.path);
            }
          }
        } else {
          print('Not a SimpleNodeProvider.');
        }
      }
    });
  }

  {
    dynamic runtimeConfig = Zone.current['dsalink.runtime.config'];

    if (runtimeConfig != null) {
      var closeHandler = () {
        close();

        if (_logFileOut != null) {
          try {
            _logFileOut?.close();
          } catch (e) {
            logger.finest('Error closing log file.', e);
          }
        }
      };

      runtimeConfig['closeHandler'] = closeHandler;
    }
  }

  var helpStr =
      'usage: $command [--broker URL] [--log LEVEL] [--name NAME] [--discover]';

  if (opts['help']) {
    print(helpStr);
    print(argp.usage);
    if (exitOnFailure) {
      exit(1);
    } else {
      return false;
    }
  }

  brokerUrl = opts['broker'];
  if (brokerUrl == null && !opts['discover']) {
    print(
      'No Broker URL Specified. One of [--broker, --discover] is required.',
    );
    print(helpStr);
    print(argp.usage);
    if (exitOnFailure) {
      exit(1);
    } else {
      return false;
    }
  }

  String? name = opts['name'];
  home = opts['home'];
  token = opts['token'];

  if (name != null) {
    if (name.endsWith('-')) {
      prefix = name;
    } else {
      prefix = '$name-';
    }
  }

  // load configs
  var dsalinkFile = File('$_basePath/dsalink.json');

  if (dsalinkFile.existsSync()) {
    dynamic e;
    try {
      var configStr = dsalinkFile.readAsStringSync();
      dsalinkJson = DsaJson.decode(configStr);
    } catch (err) {
      e = err;
    }

    if (dsalinkJson == null) {
      logger.severe('Invalid dsalink.json', e);
      if (exitOnFailure) {
        exit(1);
      } else {
        return false;
      }
    }
  } else {
    dsalinkJson = <String, dynamic>{};
  }

  if (brokerUrl != null) {
    if (!brokerUrl!.startsWith('http')) {
      brokerUrl = 'http://$brokerUrl';
    }
  }

  var keyFile =
      getConfig('key') == null
          ? File('$_basePath/.dsalink.key')
          : File.fromUri(Uri.parse(getConfig('key') as String));
  String? key;

  try {
    key = keyFile.readAsStringSync();
    privateKey = PrivateKey.loadFromString(key);
  } catch (err) {
    logger.warning('Invalid Private Key', err);
  }

  if (key == null || key.length != 131) {
    // 43 bytes d, 87 bytes Q, 1 space
    // generate the key
    if (DSRandom.instance.needsEntropy) {
      late String macs;
      if (Platform.isWindows) {
        macs = Process.runSync('getmac', []).stdout.toString();
      } else {
        try {
          macs = Process.runSync('arp', ['-an']).stdout.toString();
        } catch (e) {
          try {
            var envs = '';
            for (var i in Platform.environment.keys) {
              envs += '$i=${Platform.environment[i]}\n';
            }
            macs = envs;
          } catch (e) {
            logger.finest('Cant get macs:', e);
          }
        }
      }
      // randomize the PRNG with the system mac (as well as timestamp)
      DSRandom.instance.addEntropy(macs);
    }
    privateKey = PrivateKey.generateSync();
    key = privateKey.saveToString();
    if (savePrivateKey) {
      keyFile.writeAsStringSync(key);
    }
  }

  SimpleNode.initEncryption(privateKey.saveToString());

  if (optionsHandler != null) {
    optionsHandler(opts);
  }

  return true;
}