@@ -730,22 +730,27 @@ - (MYSQL *)_makeRawMySQLConnectionWithEncoding:(NSString *)encodingName isMaster
730730
731731- (void )_mysqlConnection : (MYSQL *)connection wantsPassword : (void (^)(const char *passwd))inBlock withPlugin : (const char *)pluginName
732732{
733- // If a password was supplied, use it; otherwise ask the delegate if appropriate.
734- //
735- // Note that password has no charset in mysql: If a user password is set to 'ü' on a latin1 connection
736- // and you later try to connect on an UTF-8 terminal (or vice versa) it will fail. The MySQL (5.5) manual wrongly states that
737- // MYSQL_SET_CHARSET_NAME has influence over that, but it does not and could not, since the password is hashed by the client
738- // before transmitting it to the server and the (5.5) client has no charset support, effectively treating password as
739- // a NUL-terminated byte array.
740- // There is one exception, though: The "mysql_clear_password" auth plugin sends the password in plaintext and the server side
741- // MAY choose to do a charset conversion as appropriate before handing it to whatever backend is used.
742- // Since we don't know which auth plugin server and client will agree upon, we'll do as the manual says...
743733 NSString *passwd = nil ;
744-
734+
735+ // If a password was supplied, use it; otherwise ask the delegate if appropriate.
745736 if (password) {
746737 passwd = password;
747- } else if ([delegate respondsToSelector: @selector (keychainPasswordForConnection: )]) {
748- passwd = [delegate keychainPasswordForConnection: self ]; // TODO pass pluginName to client
738+ }
739+ else if ([delegate respondsToSelector: @selector (keychainPasswordForConnection:authPlugin: )]) {
740+ // It's not clear what charset the plugin name is in:
741+ // In the 5.5 libmysqlclient:
742+ // * For the compiled-in plugins this will simply be the byte sequence as it was in the source code
743+ // * The server requests a plugin in the first packet it sends and gives its own charset (mysql->server_language)
744+ // * However client for the most part ignores the plugin name
745+ // * and it completely ignores the server_language
746+ // * When the client sends its reply (in send_client_reply_packet()) it will send the plugin name together with
747+ // the desired charset+collation (but doesn't apply any charset conversion logic to the values)
748+ // In the JDBC client it works like this:
749+ // * The plugin name in the first packet from the server will always be interpreted as "ASCII"
750+ // * The plugin name in the client response will be encoded in the client's initial charset
751+ // TODO We will just use latin1 for now, as it is the safest fallback
752+ NSString *plugin = [NSString stringWithCString: pluginName encoding: NSISOLatin1StringEncoding];
753+ passwd = [delegate keychainPasswordForConnection: self authPlugin: plugin];
749754 }
750755
751756 // shortcut for empty/nil password
@@ -754,7 +759,20 @@ - (void)_mysqlConnection:(MYSQL *)connection wantsPassword:(void (^)(const char
754759 return ;
755760 }
756761
757- NSStringEncoding connectEncodingNS = [SPMySQLConnection stringEncodingForMySQLCharset: connection->options.charset_name];
762+ // Note (libmysqlclient 5.5):
763+ // mysql_character_set_name() is only initialized after mysql has read the first packet from the server.
764+ // Before that it will always be latin1, regardless of what was set with mysql_options().
765+ // That does not mean, that client and server have agreed on a charset already, though!
766+ NSStringEncoding connectEncodingNS = [SPMySQLConnection stringEncodingForMySQLCharset: mysql_character_set_name (connection)];
767+
768+ // Note that password has no charset in mysql: If a user password is set to 'ü' on a latin1 connection
769+ // and you later try to connect on an UTF-8 terminal (or vice versa) it will fail. The MySQL (5.5) manual wrongly states that
770+ // MYSQL_SET_CHARSET_NAME has influence over that, but it does not and could not, since the password is hashed by the client
771+ // before transmitting it to the server and the (5.5) client has very limited charset support,
772+ // effectively treating password as a NUL-terminated byte array.
773+ // There is one exception, though: The "mysql_clear_password" auth plugin sends the password in plaintext and the server side
774+ // MAY choose to do a charset conversion as appropriate before handing it to whatever backend is used.
775+ // Since we don't know which auth plugin server and client will agree upon, we'll do as the manual says...
758776 NSInteger cLength = [passwd lengthOfBytesUsingEncoding: connectEncodingNS];
759777
760778 if (!cLength || cLength == NSIntegerMax) {
@@ -1218,7 +1236,7 @@ + (void)_removeThreadVariables:(NSNotification *)aNotification
12181236
12191237@end
12201238
1221- void PasswordCallback (MYSQL *mysql, const char *plugin, void (^with_password)(const char *passwd ))
1239+ void PasswordCallback (MYSQL *mysql, const char *plugin, void (^with_password)(const char *))
12221240{
12231241 assert (mysql && mysql->sp_context );
12241242 [(SPMySQLConnection *)mysql->sp_context _mysqlConnection: mysql wantsPassword: with_password withPlugin: plugin];
@@ -1234,7 +1252,7 @@ void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_password)(co
12341252errno_t LegacyMemsetS (void *s, rsize_t smax, int c, rsize_t n)
12351253{
12361254 volatile unsigned char * addr = (volatile unsigned char *)s;
1237- while (n--) *addr++ = c;
1255+ while (n--) *addr++ = ( unsigned char ) c;
12381256
12391257 return 0 ;
12401258}
0 commit comments