45 enum class LoadToxDataError
51 ENCRYPTED_NO_PASSWORD,
54 DECRYPT_UNENCRYPTED_FILE
57 enum class CreateToxDataError
74 std::unique_ptr<ToxEncrypt> loadToxData(
const QString& password,
const QString& filePath,
75 QByteArray& data, LoadToxDataError& error)
77 std::unique_ptr<ToxEncrypt> tmpKey;
80 QFile saveFile(filePath);
81 qDebug() <<
"Loading tox save " << filePath;
83 if (!saveFile.exists()) {
84 error = LoadToxDataError::FILE_NOT_FOUND;
88 if (!saveFile.open(QIODevice::ReadOnly)) {
89 error = LoadToxDataError::COULD_NOT_READ_FILE;
93 fileSize = saveFile.size();
95 error = LoadToxDataError::FILE_IS_EMPTY;
99 data = saveFile.readAll();
101 if (password.isEmpty()) {
102 error = LoadToxDataError::ENCRYPTED_NO_PASSWORD;
108 error = LoadToxDataError::COULD_NOT_DERIVE_KEY;
112 data = tmpKey->decrypt(data);
113 if (data.isEmpty()) {
114 error = LoadToxDataError::DECRYPTION_FAILED;
120 error = LoadToxDataError::OK;
134 std::unique_ptr<ToxEncrypt> createToxData(
const QString& name,
const QString& password,
135 const QString& filePath, CreateToxDataError& error)
137 std::unique_ptr<ToxEncrypt> newKey;
138 if (!password.isEmpty()) {
141 error = CreateToxDataError::COULD_NOT_DERIVE_KEY;
147 error = CreateToxDataError::PROFILE_LOCKED;
151 if (QFile::exists(filePath)) {
152 error = CreateToxDataError::ALREADY_EXISTS;
157 error = CreateToxDataError::LOCK_FAILED;
161 error = CreateToxDataError::OK;
165 bool logLoadToxDataError(
const LoadToxDataError& error,
const QString& path)
168 case LoadToxDataError::OK:
170 case LoadToxDataError::FILE_NOT_FOUND:
171 qWarning() <<
"The tox save file " << path <<
" was not found";
173 case LoadToxDataError::COULD_NOT_READ_FILE:
174 qCritical() <<
"The tox save file " << path <<
" couldn't be opened";
176 case LoadToxDataError::FILE_IS_EMPTY:
177 qWarning() <<
"The tox save file" << path <<
" is empty!";
179 case LoadToxDataError::ENCRYPTED_NO_PASSWORD:
180 qCritical() <<
"The tox save file is encrypted, but we don't have a password!";
182 case LoadToxDataError::COULD_NOT_DERIVE_KEY:
183 qCritical() <<
"Failed to derive key of the tox save file";
185 case LoadToxDataError::DECRYPTION_FAILED:
186 qCritical() <<
"Failed to decrypt the tox save file";
188 case LoadToxDataError::DECRYPT_UNENCRYPTED_FILE:
189 qWarning() <<
"We have a password, but the tox save file is not encrypted";
197 bool logCreateToxDataError(
const CreateToxDataError& error,
const QString& userName)
200 case CreateToxDataError::OK:
202 case CreateToxDataError::COULD_NOT_DERIVE_KEY:
203 qCritical() <<
"Failed to derive key for the tox save";
205 case CreateToxDataError::PROFILE_LOCKED:
206 qCritical() <<
"Tried to create profile " << userName
207 <<
", but another profile is already locked!";
209 case CreateToxDataError::ALREADY_EXISTS:
210 qCritical() <<
"Tried to create profile " << userName <<
", but it already exists!";
212 case CreateToxDataError::LOCK_FAILED:
213 qWarning() <<
"Failed to lock profile " << userName;
237 if (toxsave.isEmpty() && !isNewProfile) {
238 qCritical() <<
"Existing toxsave is empty";
242 if (!toxsave.isEmpty() && isNewProfile) {
243 qCritical() <<
"New profile has toxsave data";
264 qDebug() <<
"Failed to start Toxcore";
270 qDebug() <<
"Failed to start ToxAV";
280 core->setStatusMessage(tr(
"Toxing on qTox"));
291 Qt::ConnectionType::QueuedConnection);
298 , passkey{std::move(passkey)}
300 , encrypted{this->passkey !=
nullptr}
302 , settings{settings_}
314 const QCommandLineParser* parser)
317 qCritical() <<
"Tried to load profile " <<
name <<
", but another profile is already locked!";
322 qWarning() <<
"Failed to lock profile " <<
name;
326 LoadToxDataError error;
327 QByteArray toxsave = QByteArray();
330 std::unique_ptr<ToxEncrypt> tmpKey = loadToxData(password, path, toxsave, error);
331 if (logLoadToxDataError(error, path)) {
356 const QCommandLineParser* parser)
358 CreateToxDataError error;
361 std::unique_ptr<ToxEncrypt> tmpKey = createToxData(
name, password, path, error);
363 if (logCreateToxDataError(error,
name)) {
399 dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
400 dir.setNameFilters(QStringList(
"*." + extension));
401 QFileInfoList list = dir.entryInfoList();
402 out.reserve(list.size());
403 for (QFileInfo
file : list) {
404 out +=
file.completeBaseName();
418 for (
const QString& toxfile : toxfiles) {
419 if (!inifiles.contains(toxfile)) {
431 assert(c !=
nullptr);
451 const ToxPk& selfPk =
core->getSelfPublicKey();
453 if (data.isEmpty()) {
454 qDebug() <<
"Self avatar not found, will broadcast empty avatar to friends";
468 QByteArray data =
core->getToxSaveData();
477 const bool accept =
getAvatarHash(
core->getFriendPublicKey(friendId)) != avatarHash;
478 core->getCoreFile()->handleAvatarOffer(friendId, fileId, accept, filesize);
494 qDebug() <<
"Saving tox save to " << path;
495 QSaveFile saveFile(path);
496 if (!saveFile.open(QIODevice::WriteOnly)) {
497 qCritical() <<
"Tox save file " << path <<
" couldn't be opened";
503 if (data.isEmpty()) {
504 qCritical() <<
"Failed to encrypt, can't save!";
505 saveFile.cancelWriting();
510 saveFile.write(data);
513 if (saveFile.flush()) {
516 saveFile.cancelWriting();
517 qCritical() <<
"Failed to write, can't save!";
533 const QString ownerStr = owner.
toString();
538 QByteArray idData = ownerStr.toUtf8();
539 QByteArray pubkeyData =
core->getSelfPublicKey().getByteArray();
540 constexpr
int hashSize = TOX_PUBLIC_KEY_SIZE;
541 static_assert(hashSize >= crypto_generichash_BYTES_MIN && hashSize <= crypto_generichash_BYTES_MAX,
542 "Hash size not supported by libsodium");
543 static_assert(hashSize >= crypto_generichash_KEYBYTES_MIN
544 && hashSize <= crypto_generichash_KEYBYTES_MAX,
545 "Key size not supported by libsodium");
546 QByteArray hash(hashSize, 0);
547 crypto_generichash(
reinterpret_cast<uint8_t*
>(hash.data()), hashSize,
reinterpret_cast<uint8_t*
>(idData.data()), idData.size(),
548 reinterpret_cast<uint8_t*
>(pubkeyData.data()), pubkeyData.size());
572 if (avatarData.isEmpty()) {
575 pic.loadFromData(avatarData);
595 if (avatarEncrypted && !QFile::exists(path)) {
596 avatarEncrypted =
false;
601 if (!
file.open(QIODevice::ReadOnly)) {
605 QByteArray pic =
file.readAll();
606 if (avatarEncrypted && !pic.isEmpty()) {
609 qWarning() <<
"Failed to decrypt avatar at" << path;
621 qDebug() <<
"Can't load database of removed profile";
625 QByteArray salt =
core->getSelfPublicKey().getByteArray();
626 if (salt.size() != TOX_PASS_SALT_LENGTH) {
627 qWarning() <<
"Couldn't compute salt from public key" <<
name;
629 QObject::tr(
"qTox couldn't open your chat logs, they will be disabled."));
638 qWarning() <<
"Failed to open database for profile" <<
name;
640 QObject::tr(
"qTox couldn't open your chat logs, they will be disabled."));
651 QByteArray avatarData;
652 const ToxPk& selfPk =
core->getSelfPublicKey();
653 if (!pic.isEmpty()) {
654 pixmap.loadFromData(pic);
659 pixmap = QPixmap::fromImage(identicon);
662 pixmap.load(
":/img/contact_dark.svg");
682 QByteArray avatarData;
683 if (!pic.isEmpty()) {
684 pixmap.loadFromData(pic);
689 pixmap = QPixmap::fromImage(identicon);
692 pixmap.load(
":/img/contact_dark.svg");
710 const QString inviteStr = Core::tr(
"/me offers friendship, \"%1\"").arg(
message);
711 const ToxPk selfPk =
core->getSelfPublicKey();
712 const QDateTime datetime = QDateTime::currentDateTime();
713 const QString
name =
core->getUsername();
724 const bool needEncrypt =
encrypted && !avatar.isEmpty();
725 const QByteArray& pic = needEncrypt ?
passkey->encrypt(avatar) : avatar;
732 QSaveFile
file(path);
733 if (!
file.open(QIODevice::WriteOnly)) {
734 qWarning() <<
"Tox avatar " << path <<
" couldn't be saved";
750 QByteArray avatarHash(TOX_HASH_LENGTH, 0);
751 tox_hash(
reinterpret_cast<uint8_t*
>(avatarHash.data()),
reinterpret_cast<uint8_t*
>(pic.data()), pic.size());
797 if (owner ==
core->getSelfPublicKey()) {
807 return QFile::exists(path +
".tox");
827 uint8_t data[TOX_PASS_ENCRYPTION_EXTRA_LENGTH] = {0};
829 QFile saveFile(path);
830 if (!saveFile.open(QIODevice::ReadOnly)) {
831 qWarning() <<
"Couldn't open tox save " << path;
835 saveFile.read(
reinterpret_cast<char*
>(data), TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
838 return tox_is_data_encrypted(data);
850 qWarning() <<
"Profile " <<
name <<
" is already removed!";
855 qDebug() <<
"Removing profile" <<
name;
856 for (
int i = 0; i <
profiles.size(); ++i) {
865 QFile profileMain{path +
".tox"};
866 QFile profileConfig{path +
".ini"};
870 if (!profileMain.remove() && profileMain.exists()) {
871 ret.push_back(profileMain.fileName());
872 qWarning() <<
"Could not remove file " << profileMain.fileName();
874 if (!profileConfig.remove() && profileConfig.exists()) {
875 ret.push_back(profileConfig.fileName());
876 qWarning() <<
"Could not remove file " << profileConfig.fileName();
881 ret.push_back(dbPath);
882 qWarning() <<
"Could not remove file " << dbPath;
905 QFile::rename(path +
".tox", newPath +
".tox");
906 QFile::rename(path +
".ini", newPath +
".ini");
936 if (newPassword.isEmpty()) {
943 <<
"Failed to derive key from password, the profile won't use the new password";
945 "Failed to derive key from password, the profile won't use the new password.");
948 passkey = std::move(newpasskey);
955 bool dbSuccess =
false;
959 dbSuccess =
database->setPassword(newPassword);
964 error = tr(
"Couldn't change database password, it may be corrupted or use the old "
971 QVector<uint32_t> friendList =
core->getFriendList();
972 QVectorIterator<uint32_t> i(friendList);
973 while (i.hasNext()) {
974 const ToxPk friendPublicKey =
core->getFriendPublicKey(i.next());