Changeset View
Changeset View
Standalone View
Standalone View
src/buffer/katetextbuffer.cpp
Show All 30 Lines | |||||
31 | #include "katepartdebug.h" | 31 | #include "katepartdebug.h" | ||
32 | 32 | | |||
33 | #ifndef Q_OS_WIN | 33 | #ifndef Q_OS_WIN | ||
34 | #include <unistd.h> | 34 | #include <unistd.h> | ||
35 | #endif | 35 | #endif | ||
36 | 36 | | |||
37 | #include <QSaveFile> | 37 | #include <QSaveFile> | ||
38 | #include <QTemporaryFile> | 38 | #include <QTemporaryFile> | ||
39 | #include <QFileDevice> | | |||
40 | #include <QFileInfo> | 39 | #include <QFileInfo> | ||
40 | #include <QCryptographicHash> | ||||
41 | #include <QBuffer> | ||||
41 | 42 | | |||
42 | #if 0 | 43 | #if 0 | ||
43 | #define BUFFER_DEBUG qCDebug(LOG_KTE) | 44 | #define BUFFER_DEBUG qCDebug(LOG_KTE) | ||
44 | #else | 45 | #else | ||
45 | #define BUFFER_DEBUG if (0) qCDebug(LOG_KTE) | 46 | #define BUFFER_DEBUG if (0) qCDebug(LOG_KTE) | ||
46 | #endif | 47 | #endif | ||
47 | 48 | | |||
48 | namespace Kate | 49 | namespace Kate | ||
▲ Show 20 Lines • Show All 725 Lines • ▼ Show 20 Line(s) | 765 | { | |||
774 | uint groupId = -2; | 775 | uint groupId = -2; | ||
775 | if (!newFile) { | 776 | if (!newFile) { | ||
776 | QFileInfo fileInfo(filename); | 777 | QFileInfo fileInfo(filename); | ||
777 | ownerId = fileInfo.ownerId(); | 778 | ownerId = fileInfo.ownerId(); | ||
778 | groupId = fileInfo.groupId(); | 779 | groupId = fileInfo.groupId(); | ||
779 | } | 780 | } | ||
780 | 781 | | |||
781 | /** | 782 | /** | ||
782 | * use QSaveFile for save write + rename | 783 | * use QSaveFile for file saving | ||
783 | */ | 784 | */ | ||
784 | QScopedPointer<QFileDevice> saveFile(new QSaveFile(filename)); | 785 | QScopedPointer<QIODevice> saveFile(new QSaveFile(filename)); | ||
785 | static_cast<QSaveFile *>(saveFile.data())->setDirectWriteFallback(true); | 786 | static_cast<QSaveFile *>(saveFile.data())->setDirectWriteFallback(true); | ||
786 | 787 | | |||
787 | bool usingTemporaryFile = false; | 788 | bool usingTemporaryBuffer = false; | ||
788 | QScopedPointer<QVariantMap> kAuthActionArgs; | | |||
789 | QScopedPointer<KAuth::Action> kAuthSaveAction; | | |||
790 | 789 | | |||
791 | // open QSaveFile for write | 790 | // open QSaveFile for write | ||
792 | if (m_alwaysUseKAuthForSave || !saveFile->open(QIODevice::WriteOnly)) { | 791 | if (m_alwaysUseKAuthForSave || !saveFile->open(QIODevice::WriteOnly)) { | ||
793 | 792 | | |||
794 | // if that fails we need more privileges to save this file | 793 | // if that fails we need more privileges to save this file | ||
795 | // -> we write to temporary file and then move it to target location | 794 | // -> we write to a temporary file and then send its path to KAuth action for privileged save | ||
796 | 795 | | |||
797 | usingTemporaryFile = true; | 796 | usingTemporaryBuffer = true; | ||
798 | 797 | | |||
799 | QString targetFilename(filename); | 798 | // we are now saving to a temporary buffer | ||
799 | saveFile.reset(new QBuffer()); | ||||
800 | 800 | | |||
801 | kAuthActionArgs.reset(new QVariantMap()); | 801 | // open buffer for write and read (read is used for checksum computing and writing to temporary file) | ||
802 | kAuthActionArgs->insert(QLatin1String("actionMode"), SecureTextBuffer::ActionMode::Prepare); | 802 | if (!saveFile->open(QIODevice::ReadWrite)) { | ||
803 | kAuthActionArgs->insert(QLatin1String("targetFile"), targetFilename); | | |||
804 | kAuthActionArgs->insert(QLatin1String("ownerId"), getuid()); | | |||
805 | | ||||
806 | // call save with elevated privileges | | |||
807 | if (KTextEditor::EditorPrivate::unitTestMode()) { | | |||
808 | | ||||
809 | // unit testing purposes only | | |||
810 | ActionReply reply = SecureTextBuffer::savefile(*kAuthActionArgs); | | |||
811 | if (!reply.succeeded()) { | | |||
812 | return false; | | |||
813 | } | | |||
814 | targetFilename = reply.data()[QLatin1String("temporaryFile")].toString(); | | |||
815 | | ||||
816 | } else { | | |||
817 | | ||||
818 | // call action | | |||
819 | kAuthSaveAction.reset(new KAuth::Action(QLatin1String("org.kde.ktexteditor.katetextbuffer.savefile"))); | | |||
820 | kAuthSaveAction->setHelperId(QLatin1String("org.kde.ktexteditor.katetextbuffer")); | | |||
821 | kAuthSaveAction->setArguments(*kAuthActionArgs); | | |||
822 | KAuth::ExecuteJob *job = kAuthSaveAction->execute(); | | |||
823 | if (!job->exec()) { | | |||
824 | return false; | 803 | return false; | ||
825 | } | 804 | } | ||
826 | | ||||
827 | // get temporary file path from the reply | | |||
828 | targetFilename = job->data()[QLatin1String("temporaryFile")].toString(); | | |||
829 | | ||||
830 | } | | |||
831 | | ||||
832 | if (targetFilename.isEmpty()) { | | |||
833 | return false; | | |||
834 | } | | |||
835 | | ||||
836 | // we are now saving to a prepared temporary file | | |||
837 | saveFile.reset(new QFile(targetFilename)); | | |||
838 | | ||||
839 | // open QTemporaryFile for write | | |||
840 | if (!saveFile->open(QIODevice::WriteOnly)) { | | |||
841 | return false; | | |||
842 | } | | |||
843 | | ||||
844 | if (!newFile) { | | |||
845 | // set original file's permissions to temporary file (QSaveFile does this automatically) | | |||
846 | saveFile->setPermissions(QFile(filename).permissions()); | | |||
847 | } | | |||
848 | } | 805 | } | ||
849 | 806 | | |||
850 | /** | 807 | /** | ||
851 | * construct correct filter device and try to open | 808 | * construct correct filter device and try to open | ||
852 | */ | 809 | */ | ||
853 | KCompressionDevice::CompressionType type = KFilterDev::compressionTypeForMimeType(m_mimeTypeForFilterDev); | 810 | KCompressionDevice::CompressionType type = KFilterDev::compressionTypeForMimeType(m_mimeTypeForFilterDev); | ||
854 | KCompressionDevice file(saveFile.data(), false, type); | 811 | KCompressionDevice file(saveFile.data(), false, type); | ||
855 | 812 | | |||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Line(s) | |||||
901 | 858 | | |||
902 | // flush stream | 859 | // flush stream | ||
903 | stream.flush(); | 860 | stream.flush(); | ||
904 | 861 | | |||
905 | // close and delete file | 862 | // close and delete file | ||
906 | file.close(); | 863 | file.close(); | ||
907 | 864 | | |||
908 | // flush file | 865 | // flush file | ||
909 | if (!saveFile->flush()) { | 866 | if (!usingTemporaryBuffer) { | ||
910 | return false; | 867 | static_cast<QSaveFile *>(saveFile.data())->flush(); | ||
911 | } | | |||
912 | | ||||
913 | if (usingTemporaryFile) { | | |||
914 | // ensure that the file is written to disk | | |||
915 | // just for temporary file (QSaveFile does this automatically in commit()) | | |||
916 | SecureTextBuffer::syncToDisk(saveFile->handle()); | | |||
917 | } | 868 | } | ||
918 | 869 | | |||
919 | // did save work? | 870 | // did save work? | ||
920 | // only finalize if stream status == OK | 871 | // only finalize if stream status == OK | ||
921 | bool ok = (stream.status() == QTextStream::Ok); | 872 | bool ok = (stream.status() == QTextStream::Ok); | ||
922 | 873 | | |||
923 | // commit changes | 874 | // commit changes | ||
924 | if (ok) { | 875 | if (ok) { | ||
925 | 876 | | |||
926 | if (usingTemporaryFile) { | 877 | if (usingTemporaryBuffer) { | ||
927 | 878 | | |||
928 | // temporary file was used to save the file | 879 | // temporary buffer was used to save the file | ||
929 | // -> moving this file to original location with KAuth action | 880 | // -> computing checksum | ||
881 | // -> saving to temporary file | ||||
882 | // -> copying the temporary file to the original file location with KAuth action | ||||
930 | 883 | | |||
931 | kAuthActionArgs->insert(QLatin1String("actionMode"), SecureTextBuffer::ActionMode::Move); | 884 | QTemporaryFile tempFile; | ||
932 | kAuthActionArgs->insert(QLatin1String("sourceFile"), saveFile->fileName()); | 885 | if (!tempFile.open()) { | ||
933 | kAuthActionArgs->insert(QLatin1String("targetFile"), filename); | 886 | return false; | ||
934 | kAuthActionArgs->insert(QLatin1String("ownerId"), ownerId); | 887 | } | ||
935 | kAuthActionArgs->insert(QLatin1String("groupId"), groupId); | 888 | QCryptographicHash cryptographicHash(SecureTextBuffer::checksumAlgorithm); | ||
889 | | ||||
890 | // go to QBuffer start | ||||
891 | saveFile->seek(0); | ||||
892 | | ||||
893 | // read contents of QBuffer and add them to checksum utility as well as to QTemporaryFile | ||||
894 | char buffer[bufferLength]; | ||||
895 | qint64 read = -1; | ||||
896 | while ((read = saveFile->read(buffer, bufferLength)) > 0) { | ||||
897 | cryptographicHash.addData(buffer, read); | ||||
898 | if (tempFile.write(buffer, read) == -1) { | ||||
899 | return false; | ||||
900 | } | ||||
901 | } | ||||
902 | if (!tempFile.flush()) { | ||||
903 | return false; | ||||
904 | } | ||||
905 | | ||||
906 | // compute checksum | ||||
907 | QByteArray checksum = cryptographicHash.result(); | ||||
908 | | ||||
909 | // prepare data for KAuth action | ||||
910 | QVariantMap kAuthActionArgs; | ||||
911 | kAuthActionArgs.insert(QLatin1String("sourceFile"), tempFile.fileName()); | ||||
912 | kAuthActionArgs.insert(QLatin1String("targetFile"), filename); | ||||
913 | kAuthActionArgs.insert(QLatin1String("checksum"), checksum); | ||||
914 | kAuthActionArgs.insert(QLatin1String("ownerId"), ownerId); | ||||
915 | kAuthActionArgs.insert(QLatin1String("groupId"), groupId); | ||||
936 | 916 | | |||
937 | // call save with elevated privileges | 917 | // call save with elevated privileges | ||
938 | if (KTextEditor::EditorPrivate::unitTestMode()) { | 918 | if (KTextEditor::EditorPrivate::unitTestMode()) { | ||
939 | 919 | | |||
940 | // unit testing purposes only | 920 | // unit testing purposes only | ||
941 | ok = SecureTextBuffer::savefile(*kAuthActionArgs).succeeded(); | 921 | ok = SecureTextBuffer::savefile(kAuthActionArgs).succeeded(); | ||
942 | 922 | | |||
943 | } else { | 923 | } else { | ||
944 | 924 | | |||
945 | kAuthSaveAction->setArguments(*kAuthActionArgs); | 925 | KAuth::Action kAuthSaveAction(QLatin1String("org.kde.ktexteditor.katetextbuffer.savefile")); | ||
946 | KAuth::ExecuteJob *job = kAuthSaveAction->execute(); | 926 | kAuthSaveAction.setHelperId(QLatin1String("org.kde.ktexteditor.katetextbuffer")); | ||
927 | kAuthSaveAction.setArguments(kAuthActionArgs); | ||||
928 | KAuth::ExecuteJob *job = kAuthSaveAction.execute(); | ||||
947 | ok = job->exec(); | 929 | ok = job->exec(); | ||
948 | 930 | | |||
949 | } | 931 | } | ||
950 | 932 | | |||
951 | } else { | 933 | } else { | ||
952 | 934 | | |||
953 | // standard save without elevated privileges | 935 | // standard save without elevated privileges | ||
954 | 936 | | |||
955 | ok = static_cast<QSaveFile *>(saveFile.data())->commit(); | 937 | QSaveFile *saveFileLocal = static_cast<QSaveFile *>(saveFile.data()); | ||
956 | 938 | | |||
957 | if (ok && !newFile) { | 939 | if (!newFile) { | ||
958 | // ensure correct owner | 940 | // ensure correct owner | ||
959 | SecureTextBuffer::setOwner(filename, ownerId, groupId); | 941 | SecureTextBuffer::setOwner(saveFileLocal->handle(), ownerId, groupId); | ||
960 | } | 942 | } | ||
961 | 943 | | |||
944 | ok = saveFileLocal->commit(); | ||||
945 | | ||||
962 | } | 946 | } | ||
963 | } | 947 | } | ||
964 | 948 | | |||
965 | // remember this revision as last saved if we had success! | 949 | // remember this revision as last saved if we had success! | ||
966 | if (ok) { | 950 | if (ok) { | ||
967 | m_history.setLastSavedRevision(); | 951 | m_history.setLastSavedRevision(); | ||
968 | } | 952 | } | ||
969 | 953 | | |||
▲ Show 20 Lines • Show All 92 Lines • Show Last 20 Lines |