diff --git a/lib/src/x11_authority_file.dart b/lib/src/x11_authority_file.dart new file mode 100644 index 0000000..018f9e0 --- /dev/null +++ b/lib/src/x11_authority_file.dart @@ -0,0 +1,101 @@ +import "dart:convert"; +import "dart:io"; + +enum X11AuthorityAddressType { unknown, IPv4, IPv6, local, wild } + +class X11AuthorityAddress { + final X11AuthorityAddressType type; + final List address; + + X11AuthorityAddress(this.type, this.address); + + @override + String toString() => "X11AuthorityAddress($type, $address)"; +} + +class X11AuthorityFileRecord { + final X11AuthorityAddress address; + final String display; + final String authorizationName; + final List authorizationData; + + X11AuthorityFileRecord( + {required this.address, + required this.display, + required this.authorizationName, + required this.authorizationData}); + + @override + String toString() => + "X11AuthorityFileRecord($address, '$display', '$authorizationName', $authorizationData)"; +} + +class X11AuthorityFile { + final List records; + + X11AuthorityFile(this.records); +} + +class X11AuthorityFileLoader { + var _data = []; + var _offset = 0; + + Future load(String path) async { + var f = File(path); + _data = await f.readAsBytes(); + _offset = 0; + + var records = []; + while (_offset < _data.length) { + var family = _readUint16(); + var address = _readBytes(); + var display = _readString(); + var authorizationName = _readString(); + var authorizationData = _readBytes(); + + var addressType = { + 0: X11AuthorityAddressType.IPv4, + 6: X11AuthorityAddressType.IPv6, + 256: X11AuthorityAddressType.local, + 65535: X11AuthorityAddressType.wild + }[family] ?? + X11AuthorityAddressType.unknown; + + var record = X11AuthorityFileRecord( + address: X11AuthorityAddress(addressType, address), + display: display, + authorizationName: authorizationName, + authorizationData: authorizationData); + records.add(record); + } + + return X11AuthorityFile(records); + } + + int _readUint8() { + if (_offset >= _data.length) { + throw 'Invalid XAuthority file'; + } + var value = _data[_offset]; + _offset++; + return value; + } + + int _readUint16() { + return _readUint8() << 8 | _readUint8(); + } + + List _readBytes() { + var length = _readUint16(); + var value = []; + for (var i = 0; i < length; i++) { + value.add(_readUint8()); + } + return value; + } + + String _readString() { + var data = _readBytes(); + return utf8.decode(data); + } +} diff --git a/lib/src/x11_client.dart b/lib/src/x11_client.dart index a5c867c..255b56f 100644 --- a/lib/src/x11_client.dart +++ b/lib/src/x11_client.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'x11_authority_file.dart'; import 'x11_big_requests.dart'; import 'x11_composite.dart'; import 'x11_damage.dart'; @@ -289,11 +290,38 @@ class X11Client { throw "Invalid DISPLAY: '$display'"; } - await connectToHost(host, displayNumber); + var authorityPath = Platform.environment['XAUTHORITY']; + if (authorityPath == null) { + var home = Platform.environment['HOME']; + if (home == null) { + throw 'Unable to determine HOME'; + } + + authorityPath = '${home}/.Xauthority'; + } + var authorityFile = await X11AuthorityFileLoader().load(authorityPath); + var records = authorityFile.records.where((r) => + r.address.type == X11AuthorityAddressType.local && + r.authorizationName == 'MIT-MAGIC-COOKIE-1'); + var authorizationName = ''; + var authorizationData = []; + if (records.length > 0) { + var record = records.first; + authorizationName = 'MIT-MAGIC-COOKIE-1'; + authorizationData = record.authorizationData; + } + + await connectToHost(host, + displayNumber: displayNumber, + authorizationName: authorizationName, + authorizationData: authorizationData); } /// Connects to the X server on [host] using [displayNumber]. - Future connectToHost(String host, int displayNumber) async { + Future connectToHost(String host, + {int displayNumber = 0, + String authorizationName = '', + List authorizationData = const []}) async { if (!(host == '' || host == 'localhost')) { throw 'Connecting to host $host not supported'; } @@ -305,7 +333,9 @@ class X11Client { var buffer = X11WriteBuffer(); buffer.writeUint8(0x6c); // Little endian - var request = X11SetupRequest(); + var request = X11SetupRequest( + authorizationName: authorizationName, + authorizationData: authorizationData); request.encode(buffer); _socket?.add(buffer.data); diff --git a/lib/src/x11_requests.dart b/lib/src/x11_requests.dart index 53fb351..8ff0708 100644 --- a/lib/src/x11_requests.dart +++ b/lib/src/x11_requests.dart @@ -15,54 +15,52 @@ int pad(int length) { class X11SetupRequest { final X11Version protocolVersion; - final String authorizationProtocolName; - final List authorizationProtocolData; + final String authorizationName; + final List authorizationData; const X11SetupRequest( {this.protocolVersion = const X11Version(11, 0), - this.authorizationProtocolName = '', - this.authorizationProtocolData = const []}); + this.authorizationName = '', + this.authorizationData = const []}); factory X11SetupRequest.fromBuffer(X11ReadBuffer buffer) { buffer.skip(1); var protocolMajorVersion = buffer.readUint16(); var protocolMinorVersion = buffer.readUint16(); - var authorizationProtocolNameLength = buffer.readUint16(); - var authorizationProtocolDataLength = buffer.readUint16(); - var authorizationProtocolName = - buffer.readString8(authorizationProtocolNameLength); - buffer.skip(pad(authorizationProtocolNameLength)); - var authorizationProtocolData = []; - for (var i = 0; i < authorizationProtocolDataLength; i++) { - authorizationProtocolData.add(buffer.readUint8()); - } - buffer.skip(pad(authorizationProtocolDataLength)); + var authorizationNameLength = buffer.readUint16(); + var authorizationDataLength = buffer.readUint16(); + var authorizationName = buffer.readString8(authorizationNameLength); + buffer.skip(pad(authorizationNameLength)); + var authorizationData = []; + for (var i = 0; i < authorizationDataLength; i++) { + authorizationData.add(buffer.readUint8()); + } + buffer.skip(pad(authorizationDataLength)); buffer.skip(2); return X11SetupRequest( protocolVersion: X11Version(protocolMajorVersion, protocolMinorVersion), - authorizationProtocolName: authorizationProtocolName, - authorizationProtocolData: authorizationProtocolData); + authorizationName: authorizationName, + authorizationData: authorizationData); } void encode(X11WriteBuffer buffer) { buffer.skip(1); buffer.writeUint16(protocolVersion.major); buffer.writeUint16(protocolVersion.minor); - var authorizationProtocolNameLength = - buffer.getString8Length(authorizationProtocolName); - buffer.writeUint16(authorizationProtocolNameLength); - buffer.writeUint16(authorizationProtocolData.length); + var authorizationNameLength = buffer.getString8Length(authorizationName); + buffer.writeUint16(authorizationNameLength); + buffer.writeUint16(authorizationData.length); buffer.skip(2); - buffer.writeString8(authorizationProtocolName); - buffer.skip(pad(authorizationProtocolNameLength)); - buffer.writeListOfUint8(authorizationProtocolData); - buffer.skip(pad(authorizationProtocolData.length)); + buffer.writeString8(authorizationName); + buffer.skip(pad(authorizationNameLength)); + buffer.writeListOfUint8(authorizationData); + buffer.skip(pad(authorizationData.length)); } @override String toString() => - "X11SetupRequest(protocolVersion = $protocolVersion, authorizationProtocolName: '$authorizationProtocolName', authorizationProtocolData: $authorizationProtocolData)"; + "X11SetupRequest(protocolVersion = $protocolVersion, authorizationName: '$authorizationName', authorizationData: $authorizationData)"; } class X11SetupFailedReply {