1 /* |
1 /* |
2 * Hedgewars, a free turn based strategy game |
2 * Hedgewars, a free turn based strategy game |
3 * Copyright (c) 2006-2008 Igor Ulyanov <iulyanov@gmail.com> |
3 * Copyright (c) 2006-2008 Igor Ulyanov <iulyanov@gmail.com> |
4 * Copyright (c) 2004-2013 Andrey Korotaev <unC0Rr@gmail.com> |
4 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com> |
5 * |
5 * |
6 * This program is free software; you can redistribute it and/or modify |
6 * This program is free software; you can redistribute it and/or modify |
7 * it under the terms of the GNU General Public License as published by |
7 * it under the terms of the GNU General Public License as published by |
8 * the Free Software Foundation; version 2 of the License |
8 * the Free Software Foundation; version 2 of the License |
9 * |
9 * |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 * GNU General Public License for more details. |
13 * GNU General Public License for more details. |
14 * |
14 * |
15 * You should have received a copy of the GNU General Public License |
15 * You should have received a copy of the GNU General Public License |
16 * along with this program; if not, write to the Free Software |
16 * along with this program; if not, write to the Free Software |
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 */ |
18 */ |
19 |
19 |
20 #include <QDebug> |
20 #include <QDebug> |
21 #include <QInputDialog> |
21 #include <QInputDialog> |
22 #include <QCryptographicHash> |
22 #include <QCryptographicHash> |
23 #include <QSortFilterProxyModel> |
23 #include <QSortFilterProxyModel> |
|
24 #include <QUuid> |
24 |
25 |
25 #include "hwconsts.h" |
26 #include "hwconsts.h" |
26 #include "newnetclient.h" |
27 #include "newnetclient.h" |
27 #include "proto.h" |
28 #include "proto.h" |
28 #include "game.h" |
29 #include "game.h" |
29 #include "roomslistmodel.h" |
30 #include "roomslistmodel.h" |
30 #include "playerslistmodel.h" |
31 #include "playerslistmodel.h" |
31 #include "servermessages.h" |
32 #include "servermessages.h" |
32 #include "HWApplication.h" |
33 #include "HWApplication.h" |
33 |
34 |
34 char delimeter='\n'; |
35 char delimiter='\n'; |
35 |
36 |
36 HWNewNet::HWNewNet() : |
37 HWNewNet::HWNewNet() : |
37 isChief(false), |
38 isChief(false), |
38 m_game_connected(false), |
39 m_game_connected(false), |
39 loginStep(0), |
|
40 netClientState(Disconnected) |
40 netClientState(Disconnected) |
41 { |
41 { |
|
42 m_private_game = false; |
|
43 m_nick_registered = false; |
|
44 |
42 m_roomsListModel = new RoomsListModel(this); |
45 m_roomsListModel = new RoomsListModel(this); |
43 |
46 |
44 m_playersModel = new PlayersListModel(this); |
47 m_playersModel = new PlayersListModel(this); |
45 |
48 |
46 m_lobbyPlayersModel = new QSortFilterProxyModel(this); |
49 m_lobbyPlayersModel = new QSortFilterProxyModel(this); |
121 } |
124 } |
122 |
125 |
123 myroom = room; |
126 myroom = room; |
124 |
127 |
125 if(password.isEmpty()) |
128 if(password.isEmpty()) |
126 RawSendNet(QString("JOIN_ROOM%1%2").arg(delimeter).arg(room)); |
129 RawSendNet(QString("JOIN_ROOM%1%2").arg(delimiter).arg(room)); |
127 else |
130 else |
128 RawSendNet(QString("JOIN_ROOM%1%2%1%3").arg(delimeter).arg(room).arg(password)); |
131 RawSendNet(QString("JOIN_ROOM%1%2%1%3").arg(delimiter).arg(room).arg(password)); |
129 |
132 |
130 isChief = false; |
133 isChief = false; |
131 } |
134 } |
132 |
135 |
133 void HWNewNet::AddTeam(const HWTeam & team) |
136 void HWNewNet::AddTeam(const HWTeam & team) |
134 { |
137 { |
135 QString cmd = QString("ADD_TEAM") + delimeter + |
138 QString cmd = QString("ADD_TEAM") + delimiter + |
136 team.name() + delimeter + |
139 team.name() + delimiter + |
137 QString::number(team.color()) + delimeter + |
140 QString::number(team.color()) + delimiter + |
138 team.grave() + delimeter + |
141 team.grave() + delimiter + |
139 team.fort() + delimeter + |
142 team.fort() + delimiter + |
140 team.voicepack() + delimeter + |
143 team.voicepack() + delimiter + |
141 team.flag() + delimeter + |
144 team.flag() + delimiter + |
142 QString::number(team.difficulty()); |
145 QString::number(team.difficulty()); |
143 |
146 |
144 for(int i = 0; i < HEDGEHOGS_PER_TEAM; ++i) |
147 for(int i = 0; i < HEDGEHOGS_PER_TEAM; ++i) |
145 { |
148 { |
146 cmd.append(delimeter); |
149 cmd.append(delimiter); |
147 cmd.append(team.hedgehog(i).Name); |
150 cmd.append(team.hedgehog(i).Name); |
148 cmd.append(delimeter); |
151 cmd.append(delimiter); |
149 cmd.append(team.hedgehog(i).Hat); |
152 cmd.append(team.hedgehog(i).Hat); |
150 } |
153 } |
151 RawSendNet(cmd); |
154 RawSendNet(cmd); |
152 } |
155 } |
153 |
156 |
154 void HWNewNet::RemoveTeam(const HWTeam & team) |
157 void HWNewNet::RemoveTeam(const HWTeam & team) |
155 { |
158 { |
156 RawSendNet(QString("REMOVE_TEAM") + delimeter + team.name()); |
159 RawSendNet(QString("REMOVE_TEAM") + delimiter + team.name()); |
157 } |
160 } |
158 |
161 |
159 void HWNewNet::NewNick(const QString & nick) |
162 void HWNewNet::NewNick(const QString & nick) |
160 { |
163 { |
161 RawSendNet(QString("NICK%1%2").arg(delimeter).arg(nick)); |
164 RawSendNet(QString("NICK%1%2").arg(delimiter).arg(nick)); |
162 } |
165 } |
163 |
166 |
164 void HWNewNet::ToggleReady() |
167 void HWNewNet::ToggleReady() |
165 { |
168 { |
166 RawSendNet(QString("TOGGLE_READY")); |
169 RawSendNet(QString("TOGGLE_READY")); |
284 { |
290 { |
285 if(lst.size() < 3 || lst[2].toInt() < cMinServerVersion) |
291 if(lst.size() < 3 || lst[2].toInt() < cMinServerVersion) |
286 { |
292 { |
287 // TODO: Warn user, disconnect |
293 // TODO: Warn user, disconnect |
288 qWarning() << "Server too old"; |
294 qWarning() << "Server too old"; |
289 RawSendNet(QString("QUIT%1%2").arg(delimeter).arg("Server too old")); |
295 RawSendNet(QString("QUIT%1%2").arg(delimiter).arg("Server too old")); |
290 Disconnect(); |
296 Disconnect(); |
291 emit disconnected(tr("The server is too old. Disconnecting now.")); |
297 emit disconnected(tr("The server is too old. Disconnecting now.")); |
292 return; |
298 return; |
293 } |
299 } |
294 |
300 |
295 RawSendNet(QString("NICK%1%2").arg(delimeter).arg(mynick)); |
301 RawSendNet(QString("NICK%1%2").arg(delimiter).arg(mynick)); |
296 RawSendNet(QString("PROTO%1%2").arg(delimeter).arg(*cProtoVer)); |
302 RawSendNet(QString("PROTO%1%2").arg(delimiter).arg(*cProtoVer)); |
297 netClientState = Connected; |
303 netClientState = Connected; |
298 m_game_connected = true; |
304 m_game_connected = true; |
299 emit adminAccess(false); |
305 emit adminAccess(false); |
300 return; |
306 return; |
301 } |
307 } |
302 |
308 |
|
309 if (lst[0] == "SERVER_AUTH") |
|
310 { |
|
311 if(lst.size() < 2) |
|
312 { |
|
313 qWarning("Net: Malformed SERVER_AUTH message"); |
|
314 return; |
|
315 } |
|
316 |
|
317 if(lst[1] != m_serverHash) |
|
318 { |
|
319 Error("Server authentication error"); |
|
320 Disconnect(); |
|
321 } else |
|
322 { |
|
323 // empty m_serverHash variable means no authentication was performed |
|
324 // or server passed authentication |
|
325 m_serverHash.clear(); |
|
326 } |
|
327 |
|
328 return; |
|
329 } |
|
330 |
303 if (lst[0] == "PING") |
331 if (lst[0] == "PING") |
304 { |
332 { |
305 if (lst.size() > 1) |
333 if (lst.size() > 1) |
306 RawSendNet(QString("PONG%1%2").arg(delimeter).arg(lst[1])); |
334 RawSendNet(QString("PONG%1%2").arg(delimiter).arg(lst[1])); |
307 else |
335 else |
308 RawSendNet(QString("PONG")); |
336 RawSendNet(QString("PONG")); |
309 return; |
337 return; |
310 } |
338 } |
311 |
339 |
448 m_playersModel->setFlag(nick, PlayersListModel::InRoom, setFlag); |
476 m_playersModel->setFlag(nick, PlayersListModel::InRoom, setFlag); |
449 break; |
477 break; |
450 // flag indicating if a player is contributor |
478 // flag indicating if a player is contributor |
451 case 'c': |
479 case 'c': |
452 foreach(const QString & nick, nicks) |
480 foreach(const QString & nick, nicks) |
453 m_playersModel->setFlag(nick, PlayersListModel::InRoom, setFlag); |
481 m_playersModel->setFlag(nick, PlayersListModel::Contributor, setFlag); |
454 break; |
482 break; |
455 // flag indicating if a player has engine running |
483 // flag indicating if a player has engine running |
456 case 'g': |
484 case 'g': |
457 if(inRoom) |
485 if(inRoom) |
458 foreach(const QString & nick, nicks) |
486 foreach(const QString & nick, nicks) |
543 |
579 |
544 QString roomName = tmp.takeFirst(); |
580 QString roomName = tmp.takeFirst(); |
545 m_roomsListModel->updateRoom(roomName, tmp); |
581 m_roomsListModel->updateRoom(roomName, tmp); |
546 |
582 |
547 // keep track of room name so correct name is displayed |
583 // keep track of room name so correct name is displayed |
548 if(myroom == roomName) |
584 if(myroom == roomName && myroom != tmp[1]) |
549 { |
585 { |
550 myroom = tmp[1]; |
586 myroom = tmp[1]; |
551 emit roomNameUpdated(myroom); |
587 emit roomNameUpdated(myroom); |
552 } |
588 } |
553 |
589 |
576 return; |
612 return; |
577 } |
613 } |
578 |
614 |
579 if (lst[0] == "ASKPASSWORD") |
615 if (lst[0] == "ASKPASSWORD") |
580 { |
616 { |
|
617 // server should send us salt of at least 16 characters |
|
618 |
|
619 if(lst.size() < 2 || lst[1].size() < 16) |
|
620 { |
|
621 qWarning("Net: Bad ASKPASSWORD message"); |
|
622 return; |
|
623 } |
|
624 |
581 emit NickRegistered(mynick); |
625 emit NickRegistered(mynick); |
582 m_nick_registered = true; |
626 m_nick_registered = true; |
|
627 |
|
628 // store server salt |
|
629 // when this variable is set, it is assumed that server asked us for a password |
|
630 m_serverSalt = lst[1]; |
|
631 m_clientSalt = QUuid::createUuid().toString(); |
|
632 |
|
633 maybeSendPassword(); |
|
634 |
583 return; |
635 return; |
584 } |
636 } |
585 |
637 |
586 if (lst[0] == "NOTICE") |
638 if (lst[0] == "NOTICE") |
587 { |
639 { |
818 |
871 |
819 void HWNewNet::onHedgehogsNumChanged(const HWTeam& team) |
872 void HWNewNet::onHedgehogsNumChanged(const HWTeam& team) |
820 { |
873 { |
821 if (isChief) |
874 if (isChief) |
822 RawSendNet(QString("HH_NUM%1%2%1%3") |
875 RawSendNet(QString("HH_NUM%1%2%1%3") |
823 .arg(delimeter) |
876 .arg(delimiter) |
824 .arg(team.name()) |
877 .arg(team.name()) |
825 .arg(team.numHedgehogs())); |
878 .arg(team.numHedgehogs())); |
826 } |
879 } |
827 |
880 |
828 void HWNewNet::onTeamColorChanged(const HWTeam& team) |
881 void HWNewNet::onTeamColorChanged(const HWTeam& team) |
829 { |
882 { |
830 if (isChief) |
883 if (isChief) |
831 RawSendNet(QString("TEAM_COLOR%1%2%1%3") |
884 RawSendNet(QString("TEAM_COLOR%1%2%1%3") |
832 .arg(delimeter) |
885 .arg(delimiter) |
833 .arg(team.name()) |
886 .arg(team.name()) |
834 .arg(team.color())); |
887 .arg(team.color())); |
835 } |
888 } |
836 |
889 |
837 void HWNewNet::onParamChanged(const QString & param, const QStringList & value) |
890 void HWNewNet::onParamChanged(const QString & param, const QStringList & value) |
838 { |
891 { |
839 if (isChief) |
892 if (isChief) |
840 RawSendNet( |
893 RawSendNet( |
841 QString("CFG%1%2%1%3") |
894 QString("CFG%1%2%1%3") |
842 .arg(delimeter) |
895 .arg(delimiter) |
843 .arg(param) |
896 .arg(param) |
844 .arg(value.join(QString(delimeter))) |
897 .arg(value.join(QString(delimiter))) |
845 ); |
898 ); |
846 } |
899 } |
847 |
900 |
848 void HWNewNet::chatLineToNetWithEcho(const QString& str) |
901 void HWNewNet::chatLineToNetWithEcho(const QString& str) |
849 { |
902 { |
856 |
909 |
857 void HWNewNet::chatLineToNet(const QString& str) |
910 void HWNewNet::chatLineToNet(const QString& str) |
858 { |
911 { |
859 if(str != "") |
912 if(str != "") |
860 { |
913 { |
861 RawSendNet(QString("CHAT") + delimeter + str); |
914 RawSendNet(QString("CHAT") + delimiter + str); |
862 QString action = HWProto::chatStringToAction(str); |
915 QString action = HWProto::chatStringToAction(str); |
863 if (action != NULL) |
916 if (action != NULL) |
864 emit(roomChatAction(mynick, action)); |
917 emit(roomChatAction(mynick, action)); |
865 else |
918 else |
866 emit(roomChatMessage(mynick, str)); |
919 emit(roomChatMessage(mynick, str)); |
869 |
922 |
870 void HWNewNet::chatLineToLobby(const QString& str) |
923 void HWNewNet::chatLineToLobby(const QString& str) |
871 { |
924 { |
872 if(str != "") |
925 if(str != "") |
873 { |
926 { |
874 RawSendNet(QString("CHAT") + delimeter + str); |
927 RawSendNet(QString("CHAT") + delimiter + str); |
875 QString action = HWProto::chatStringToAction(str); |
928 QString action = HWProto::chatStringToAction(str); |
876 if (action != NULL) |
929 if (action != NULL) |
877 emit(lobbyChatAction(mynick, action)); |
930 emit(lobbyChatAction(mynick, action)); |
878 else |
931 else |
879 emit(lobbyChatMessage(mynick, str)); |
932 emit(lobbyChatMessage(mynick, str)); |
880 } |
933 } |
881 } |
934 } |
882 |
935 |
883 void HWNewNet::SendTeamMessage(const QString& str) |
936 void HWNewNet::SendTeamMessage(const QString& str) |
884 { |
937 { |
885 RawSendNet(QString("TEAMCHAT") + delimeter + str); |
938 RawSendNet(QString("TEAMCHAT") + delimiter + str); |
886 } |
939 } |
887 |
940 |
888 void HWNewNet::askRoomsList() |
941 void HWNewNet::askRoomsList() |
889 { |
942 { |
890 if(netClientState != InLobby) |
943 if(netClientState != InLobby) |
923 void HWNewNet::gameFinished(bool correctly) |
976 void HWNewNet::gameFinished(bool correctly) |
924 { |
977 { |
925 if (netClientState == InGame) |
978 if (netClientState == InGame) |
926 { |
979 { |
927 netClientState = InRoom; |
980 netClientState = InRoom; |
928 RawSendNet(QString("ROUNDFINISHED%1%2").arg(delimeter).arg(correctly ? "1" : "0")); |
981 RawSendNet(QString("ROUNDFINISHED%1%2").arg(delimiter).arg(correctly ? "1" : "0")); |
929 } |
982 } |
930 } |
983 } |
931 |
984 |
932 void HWNewNet::banPlayer(const QString & nick) |
985 void HWNewNet::banPlayer(const QString & nick) |
933 { |
986 { |
934 RawSendNet(QString("BAN%1%2").arg(delimeter).arg(nick)); |
987 RawSendNet(QString("BAN%1%2").arg(delimiter).arg(nick)); |
935 } |
988 } |
936 |
989 |
937 void HWNewNet::banIP(const QString & ip, const QString & reason, int seconds) |
990 void HWNewNet::banIP(const QString & ip, const QString & reason, int seconds) |
938 { |
991 { |
939 RawSendNet(QString("BANIP%1%2%1%3%1%4").arg(delimeter).arg(ip).arg(reason).arg(seconds)); |
992 RawSendNet(QString("BANIP%1%2%1%3%1%4").arg(delimiter).arg(ip).arg(reason).arg(seconds)); |
940 } |
993 } |
941 |
994 |
942 void HWNewNet::banNick(const QString & nick, const QString & reason, int seconds) |
995 void HWNewNet::banNick(const QString & nick, const QString & reason, int seconds) |
943 { |
996 { |
944 RawSendNet(QString("BANNICK%1%2%1%3%1%4").arg(delimeter).arg(nick).arg(reason).arg(seconds)); |
997 RawSendNet(QString("BANNICK%1%2%1%3%1%4").arg(delimiter).arg(nick).arg(reason).arg(seconds)); |
945 } |
998 } |
946 |
999 |
947 void HWNewNet::getBanList() |
1000 void HWNewNet::getBanList() |
948 { |
1001 { |
949 RawSendNet(QByteArray("BANLIST")); |
1002 RawSendNet(QByteArray("BANLIST")); |
950 } |
1003 } |
951 |
1004 |
952 void HWNewNet::removeBan(const QString & b) |
1005 void HWNewNet::removeBan(const QString & b) |
953 { |
1006 { |
954 RawSendNet(QString("UNBAN%1%2").arg(delimeter).arg(b)); |
1007 RawSendNet(QString("UNBAN%1%2").arg(delimiter).arg(b)); |
955 } |
1008 } |
956 |
1009 |
957 void HWNewNet::kickPlayer(const QString & nick) |
1010 void HWNewNet::kickPlayer(const QString & nick) |
958 { |
1011 { |
959 RawSendNet(QString("KICK%1%2").arg(delimeter).arg(nick)); |
1012 RawSendNet(QString("KICK%1%2").arg(delimiter).arg(nick)); |
960 } |
1013 } |
961 |
1014 |
962 void HWNewNet::infoPlayer(const QString & nick) |
1015 void HWNewNet::infoPlayer(const QString & nick) |
963 { |
1016 { |
964 RawSendNet(QString("INFO%1%2").arg(delimeter).arg(nick)); |
1017 RawSendNet(QString("INFO%1%2").arg(delimiter).arg(nick)); |
965 } |
1018 } |
966 |
1019 |
967 void HWNewNet::followPlayer(const QString & nick) |
1020 void HWNewNet::followPlayer(const QString & nick) |
968 { |
1021 { |
969 if (!isInRoom()) |
1022 if (!isInRoom()) |
970 { |
1023 { |
971 RawSendNet(QString("FOLLOW%1%2").arg(delimeter).arg(nick)); |
1024 RawSendNet(QString("FOLLOW%1%2").arg(delimiter).arg(nick)); |
972 isChief = false; |
1025 isChief = false; |
973 } |
1026 } |
974 } |
1027 } |
975 |
1028 |
976 void HWNewNet::consoleCommand(const QString & cmd) |
1029 void HWNewNet::consoleCommand(const QString & cmd) |
977 { |
1030 { |
978 RawSendNet(QString("CMD%1%2").arg(delimeter).arg(cmd)); |
1031 RawSendNet(QString("CMD%1%2").arg(delimiter).arg(cmd)); |
979 } |
1032 } |
980 |
1033 |
981 bool HWNewNet::allPlayersReady() |
1034 bool HWNewNet::allPlayersReady() |
982 { |
1035 { |
983 int ready = 0; |
1036 int ready = 0; |
1030 return netClientState >= InRoom; |
1083 return netClientState >= InRoom; |
1031 } |
1084 } |
1032 |
1085 |
1033 void HWNewNet::setServerMessageNew(const QString & msg) |
1086 void HWNewNet::setServerMessageNew(const QString & msg) |
1034 { |
1087 { |
1035 RawSendNet(QString("SET_SERVER_VAR%1MOTD_NEW%1%2").arg(delimeter).arg(msg)); |
1088 RawSendNet(QString("SET_SERVER_VAR%1MOTD_NEW%1%2").arg(delimiter).arg(msg)); |
1036 } |
1089 } |
1037 |
1090 |
1038 void HWNewNet::setServerMessageOld(const QString & msg) |
1091 void HWNewNet::setServerMessageOld(const QString & msg) |
1039 { |
1092 { |
1040 RawSendNet(QString("SET_SERVER_VAR%1MOTD_OLD%1%2").arg(delimeter).arg(msg)); |
1093 RawSendNet(QString("SET_SERVER_VAR%1MOTD_OLD%1%2").arg(delimiter).arg(msg)); |
1041 } |
1094 } |
1042 |
1095 |
1043 void HWNewNet::setLatestProtocolVar(int proto) |
1096 void HWNewNet::setLatestProtocolVar(int proto) |
1044 { |
1097 { |
1045 RawSendNet(QString("SET_SERVER_VAR%1LATEST_PROTO%1%2").arg(delimeter).arg(proto)); |
1098 RawSendNet(QString("SET_SERVER_VAR%1LATEST_PROTO%1%2").arg(delimiter).arg(proto)); |
1046 } |
1099 } |
1047 |
1100 |
1048 void HWNewNet::askServerVars() |
1101 void HWNewNet::askServerVars() |
1049 { |
1102 { |
1050 RawSendNet(QString("GET_SERVER_VAR")); |
1103 RawSendNet(QString("GET_SERVER_VAR")); |
1081 void HWNewNet::roomPasswordEntered(const QString &password) |
1134 void HWNewNet::roomPasswordEntered(const QString &password) |
1082 { |
1135 { |
1083 if(!myroom.isEmpty()) |
1136 if(!myroom.isEmpty()) |
1084 JoinRoom(myroom, password); |
1137 JoinRoom(myroom, password); |
1085 } |
1138 } |
|
1139 |
|
1140 void HWNewNet::maybeSendPassword() |
|
1141 { |
|
1142 /* When we got password hash, and server asked us for a password, perform mutual authentication: |
|
1143 * at this point we have salt chosen by server |
|
1144 * client sends client salt and hash of secret (password hash) salted with client salt, server salt, |
|
1145 * and static salt (predefined string + protocol number) |
|
1146 * server should respond with hash of the same set in different order. |
|
1147 */ |
|
1148 |
|
1149 if(m_passwordHash.isEmpty() || m_serverSalt.isEmpty()) |
|
1150 return; |
|
1151 |
|
1152 QString hash = QCryptographicHash::hash( |
|
1153 m_clientSalt.toAscii() |
|
1154 .append(m_serverSalt.toAscii()) |
|
1155 .append(m_passwordHash) |
|
1156 .append(cProtoVer->toAscii()) |
|
1157 .append("!hedgewars") |
|
1158 , QCryptographicHash::Sha1).toHex(); |
|
1159 |
|
1160 m_serverHash = QCryptographicHash::hash( |
|
1161 m_serverSalt.toAscii() |
|
1162 .append(m_clientSalt.toAscii()) |
|
1163 .append(m_passwordHash) |
|
1164 .append(cProtoVer->toAscii()) |
|
1165 .append("!hedgewars") |
|
1166 , QCryptographicHash::Sha1).toHex(); |
|
1167 |
|
1168 RawSendNet(QString("PASSWORD%1%2%1%3").arg(delimiter).arg(hash).arg(m_clientSalt)); |
|
1169 } |