1 module hunt.database.driver.mysql.impl.codec.ChangeUserCommandCodec; 2 3 import hunt.database.driver.mysql.impl.codec.CapabilitiesFlag; 4 import hunt.database.driver.mysql.impl.codec.CommandCodec; 5 import hunt.database.driver.mysql.impl.codec.CommandType; 6 import hunt.database.driver.mysql.impl.codec.MySQLEncoder; 7 import hunt.database.driver.mysql.impl.codec.Packets; 8 9 import hunt.database.driver.mysql.impl.MySQLCollation; 10 import hunt.database.driver.mysql.impl.command.ChangeUserCommand; 11 import hunt.database.driver.mysql.impl.util.BufferUtils; 12 import hunt.database.driver.mysql.impl.util.Native41Authenticator; 13 import hunt.database.base.impl.command.CommandResponse; 14 15 import hunt.collection.Map; 16 import hunt.Exceptions; 17 import hunt.logging; 18 import hunt.net.buffer; 19 import hunt.Object; 20 import hunt.text.Charset; 21 22 import std.array; 23 24 /** 25 * 26 */ 27 class ChangeUserCommandCodec : CommandCodec!(Void, ChangeUserCommand) { 28 this(ChangeUserCommand cmd) { 29 super(cmd); 30 } 31 32 override 33 void encode(MySQLEncoder encoder) { 34 super.encode(encoder); 35 sendChangeUserCommand(); 36 } 37 38 override 39 void decodePayload(ByteBuf payload, int payloadLength, int sequenceId) { 40 Packets header = cast(Packets)payload.getUnsignedByte(payload.readerIndex()); 41 switch (header) { 42 case Packets.EOF_PACKET_HEADER: // 0xFE 43 string pluginName = BufferUtils.readNullTerminatedString(payload, StandardCharsets.UTF_8); 44 if (pluginName == "caching_sha2_password") { 45 // TODO support different auth methods later 46 completionHandler(failedResponse!(Void)(new 47 UnsupportedOperationException("unsupported authentication method: " ~ pluginName))); 48 return; 49 } 50 byte[] scramble = new byte[20]; 51 payload.readBytes(scramble); 52 byte[] scrambledPassword = Native41Authenticator.encode(cmd.password(), StandardCharsets.UTF_8, scramble); 53 sendAuthSwitchResponse(scrambledPassword); 54 break; 55 case Packets.OK_PACKET_HEADER: 56 completionHandler(succeededResponse(cast(Void)null)); 57 break; 58 case Packets.ERROR_PACKET_HEADER: 59 handleErrorPacketPayload(payload); 60 break; 61 62 default: 63 warningf("Can't handle %d", header); 64 break; 65 } 66 } 67 68 private void sendChangeUserCommand() { 69 ByteBuf packet = allocateBuffer(); 70 // encode packet header 71 int packetStartIdx = packet.writerIndex(); 72 packet.writeMediumLE(0); // will set payload length later by calculation 73 packet.writeByte(sequenceId); 74 75 // encode packet payload 76 packet.writeByte(CommandType.COM_CHANGE_USER); 77 BufferUtils.writeNullTerminatedString(packet, cmd.username(), StandardCharsets.UTF_8); 78 string password = cmd.password(); 79 if (password.empty()) { 80 packet.writeByte(0); 81 } else { 82 packet.writeByte(cast(int)password.length); 83 packet.writeCharSequence(password, StandardCharsets.UTF_8); 84 } 85 BufferUtils.writeNullTerminatedString(packet, cmd.database(), StandardCharsets.UTF_8); 86 MySQLCollation collation = cmd.collation(); 87 int collationId = collation.collationId(); 88 encoder.charset = collation.mappedCharsetName(); // Charset.forName(collation.mappedCharsetName()); 89 packet.writeShortLE(collationId); 90 91 if ((encoder.clientCapabilitiesFlag & CapabilitiesFlag.CLIENT_PLUGIN_AUTH) != 0) { 92 BufferUtils.writeNullTerminatedString(packet, "mysql_native_password", StandardCharsets.UTF_8); 93 } 94 Map!(string, string) clientConnectionAttributes = cmd.connectionAttributes(); 95 if (clientConnectionAttributes !is null && !clientConnectionAttributes.isEmpty()) { 96 encoder.clientCapabilitiesFlag |= CapabilitiesFlag.CLIENT_CONNECT_ATTRS; 97 } 98 if ((encoder.clientCapabilitiesFlag & CapabilitiesFlag.CLIENT_CONNECT_ATTRS) != 0) { 99 ByteBuf kv = allocateBuffer(); 100 foreach (string key, string value ; clientConnectionAttributes) { 101 BufferUtils.writeLengthEncodedString(kv, key, StandardCharsets.UTF_8); 102 BufferUtils.writeLengthEncodedString(kv, value, StandardCharsets.UTF_8); 103 } 104 BufferUtils.writeLengthEncodedInteger(packet, kv.readableBytes()); 105 packet.writeBytes(kv); 106 } 107 108 // set payload length 109 int lenOfPayload = packet.writerIndex() - packetStartIdx - 4; 110 packet.setMediumLE(packetStartIdx, lenOfPayload); 111 112 sendPacket(packet, lenOfPayload); 113 } 114 115 private void sendAuthSwitchResponse(byte[] responseData) { 116 int payloadLength = cast(int)responseData.length; 117 ByteBuf packet = allocateBuffer(payloadLength + 4); 118 // encode packet header 119 packet.writeMediumLE(payloadLength); 120 packet.writeByte(sequenceId); 121 122 // encode packet payload 123 packet.writeBytes(responseData); 124 125 sendNonSplitPacket(packet); 126 } 127 }