Changeset View
Changeset View
Standalone View
Standalone View
src/buffer/katetextbuffer.cpp
Show All 28 Lines | |||||
29 | #include "katedocument.h" | 29 | #include "katedocument.h" | ||
30 | #include "kateview.h" | 30 | #include "kateview.h" | ||
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 <QFile> | ||
38 | #include <QTemporaryFile> | 38 | #include <QTemporaryFile> | ||
39 | #include <QFileInfo> | 39 | #include <QFileInfo> | ||
40 | #include <QCryptographicHash> | 40 | #include <QCryptographicHash> | ||
41 | #include <QBuffer> | 41 | #include <QBuffer> | ||
42 | 42 | | |||
43 | #if 0 | 43 | #if 0 | ||
44 | #define BUFFER_DEBUG qCDebug(LOG_KTE) | 44 | #define BUFFER_DEBUG qCDebug(LOG_KTE) | ||
45 | #else | 45 | #else | ||
▲ Show 20 Lines • Show All 715 Lines • ▼ Show 20 Line(s) | 751 | { | |||
761 | } | 761 | } | ||
762 | } | 762 | } | ||
763 | 763 | | |||
764 | bool TextBuffer::save(const QString &filename) | 764 | bool TextBuffer::save(const QString &filename) | ||
765 | { | 765 | { | ||
766 | // codec must be set! | 766 | // codec must be set! | ||
767 | Q_ASSERT(m_textCodec); | 767 | Q_ASSERT(m_textCodec); | ||
768 | 768 | | |||
769 | const bool newFile = !QFile::exists(filename); | 769 | /** | ||
770 | * use QFile for file saving in the normal case | ||||
771 | */ | ||||
772 | QScopedPointer<QIODevice> saveFile(new QFile(filename)); | ||||
770 | 773 | | |||
771 | /** | 774 | /** | ||
772 | * Memorize owner and group. Due to design of QSaveFile we will have to re-set them after save is complete. | 775 | * if we want to use kauth anyways or we can open the file write only => use buffer and try to use authhelper | ||
773 | */ | 776 | */ | ||
774 | uint ownerId = -2; | 777 | uint ownerId = -2; | ||
775 | uint groupId = -2; | 778 | uint groupId = -2; | ||
776 | if (!newFile) { | 779 | bool usingTemporaryBuffer = false; | ||
777 | QFileInfo fileInfo(filename); | 780 | if (m_alwaysUseKAuthForSave || !saveFile->open(QIODevice::WriteOnly)) { | ||
781 | /** | ||||
782 | * Memorize owner and group. | ||||
783 | */ | ||||
784 | const QFileInfo fileInfo(filename); | ||||
785 | if (fileInfo.exists()) { | ||||
778 | ownerId = fileInfo.ownerId(); | 786 | ownerId = fileInfo.ownerId(); | ||
779 | groupId = fileInfo.groupId(); | 787 | groupId = fileInfo.groupId(); | ||
780 | } | 788 | } | ||
781 | 789 | | |||
782 | /** | | |||
783 | * use QSaveFile for file saving | | |||
784 | */ | | |||
785 | QScopedPointer<QIODevice> saveFile(new QSaveFile(filename)); | | |||
786 | static_cast<QSaveFile *>(saveFile.data())->setDirectWriteFallback(true); | | |||
787 | | ||||
788 | bool usingTemporaryBuffer = false; | | |||
789 | | ||||
790 | // open QSaveFile for write | | |||
791 | if (m_alwaysUseKAuthForSave || !saveFile->open(QIODevice::WriteOnly)) { | | |||
792 | | ||||
793 | // if that fails we need more privileges to save this file | 790 | // if that fails we need more privileges to save this file | ||
794 | // -> we write to a temporary file and then send its path to KAuth action for privileged save | 791 | // -> we write to a temporary file and then send its path to KAuth action for privileged save | ||
795 | | ||||
796 | usingTemporaryBuffer = true; | 792 | usingTemporaryBuffer = true; | ||
797 | 793 | | |||
798 | // we are now saving to a temporary buffer | 794 | // we are now saving to a temporary buffer | ||
799 | saveFile.reset(new QBuffer()); | 795 | saveFile.reset(new QBuffer()); | ||
800 | 796 | | |||
801 | // open buffer for write and read (read is used for checksum computing and writing to temporary file) | 797 | // open buffer for write and read (read is used for checksum computing and writing to temporary file) | ||
802 | if (!saveFile->open(QIODevice::ReadWrite)) { | 798 | if (!saveFile->open(QIODevice::ReadWrite)) { | ||
803 | return false; | 799 | return false; | ||
804 | } | 800 | } | ||
805 | } | 801 | } | ||
806 | 802 | | |||
807 | /** | 803 | /** | ||
808 | * construct correct filter device and try to open | 804 | * construct correct filter device and try to open | ||
809 | */ | 805 | */ | ||
810 | KCompressionDevice::CompressionType type = KFilterDev::compressionTypeForMimeType(m_mimeTypeForFilterDev); | 806 | KCompressionDevice::CompressionType type = KFilterDev::compressionTypeForMimeType(m_mimeTypeForFilterDev); | ||
811 | KCompressionDevice file(saveFile.data(), false, type); | 807 | KCompressionDevice file(saveFile.data(), false, type); | ||
812 | | ||||
813 | if (!file.open(QIODevice::WriteOnly)) { | 808 | if (!file.open(QIODevice::WriteOnly)) { | ||
814 | return false; | 809 | return false; | ||
815 | } | 810 | } | ||
816 | 811 | | |||
817 | /** | 812 | /** | ||
818 | * construct stream + disable Unicode headers | 813 | * construct stream + disable Unicode headers | ||
819 | */ | 814 | */ | ||
820 | QTextStream stream(&file); | 815 | QTextStream stream(&file); | ||
Show All 33 Lines | 845 | if (m_newLineAtEof) { | |||
854 | if (firstChar > -1 || lastLine->length() > 0) { | 849 | if (firstChar > -1 || lastLine->length() > 0) { | ||
855 | stream << eol; | 850 | stream << eol; | ||
856 | } | 851 | } | ||
857 | } | 852 | } | ||
858 | 853 | | |||
859 | // flush stream | 854 | // flush stream | ||
860 | stream.flush(); | 855 | stream.flush(); | ||
861 | 856 | | |||
862 | // close and delete file | 857 | // close file | ||
863 | file.close(); | 858 | file.close(); | ||
864 | 859 | | |||
865 | // flush file | 860 | // flush file, if not a QBuffer | ||
866 | if (!usingTemporaryBuffer) { | 861 | if (!usingTemporaryBuffer) { | ||
867 | static_cast<QSaveFile *>(saveFile.data())->flush(); | 862 | static_cast<QFile *>(saveFile.data())->flush(); | ||
868 | } | 863 | } | ||
869 | 864 | | |||
870 | // did save work? | 865 | // did save work? | ||
871 | // only finalize if stream status == OK | 866 | // only finalize if stream status == OK | ||
872 | bool ok = (stream.status() == QTextStream::Ok); | 867 | if (stream.status() != QTextStream::Ok) { | ||
873 | 868 | return false; | |||
874 | // commit changes | 869 | } | ||
875 | if (ok) { | | |||
876 | 870 | | |||
871 | /** | ||||
872 | * if we used a QBuffer for kauth, try to move that to the real location with the helper | ||||
873 | */ | ||||
877 | if (usingTemporaryBuffer) { | 874 | if (usingTemporaryBuffer) { | ||
878 | | ||||
879 | // temporary buffer was used to save the file | 875 | // temporary buffer was used to save the file | ||
880 | // -> computing checksum | 876 | // -> computing checksum | ||
881 | // -> saving to temporary file | 877 | // -> saving to temporary file | ||
882 | // -> copying the temporary file to the original file location with KAuth action | 878 | // -> copying the temporary file to the original file location with KAuth action | ||
883 | | ||||
884 | QTemporaryFile tempFile; | 879 | QTemporaryFile tempFile; | ||
885 | if (!tempFile.open()) { | 880 | if (!tempFile.open()) { | ||
886 | return false; | 881 | return false; | ||
887 | } | 882 | } | ||
888 | QCryptographicHash cryptographicHash(SecureTextBuffer::checksumAlgorithm); | | |||
889 | 883 | | |||
890 | // go to QBuffer start | 884 | // go to QBuffer start | ||
891 | saveFile->seek(0); | 885 | saveFile->seek(0); | ||
892 | 886 | | |||
893 | // read contents of QBuffer and add them to checksum utility as well as to QTemporaryFile | 887 | // read contents of QBuffer and add them to checksum utility as well as to QTemporaryFile | ||
894 | char buffer[bufferLength]; | 888 | char buffer[bufferLength]; | ||
895 | qint64 read = -1; | 889 | qint64 read = -1; | ||
890 | QCryptographicHash cryptographicHash(SecureTextBuffer::checksumAlgorithm); | ||||
896 | while ((read = saveFile->read(buffer, bufferLength)) > 0) { | 891 | while ((read = saveFile->read(buffer, bufferLength)) > 0) { | ||
897 | cryptographicHash.addData(buffer, read); | 892 | cryptographicHash.addData(buffer, read); | ||
898 | if (tempFile.write(buffer, read) == -1) { | 893 | if (tempFile.write(buffer, read) == -1) { | ||
899 | return false; | 894 | return false; | ||
900 | } | 895 | } | ||
901 | } | 896 | } | ||
902 | if (!tempFile.flush()) { | 897 | if (!tempFile.flush()) { | ||
903 | return false; | 898 | return false; | ||
904 | } | 899 | } | ||
905 | 900 | | |||
906 | // compute checksum | | |||
907 | QByteArray checksum = cryptographicHash.result(); | | |||
908 | | ||||
909 | // prepare data for KAuth action | 901 | // prepare data for KAuth action | ||
910 | QVariantMap kAuthActionArgs; | 902 | QVariantMap kAuthActionArgs; | ||
911 | kAuthActionArgs.insert(QLatin1String("sourceFile"), tempFile.fileName()); | 903 | kAuthActionArgs.insert(QLatin1String("sourceFile"), tempFile.fileName()); | ||
912 | kAuthActionArgs.insert(QLatin1String("targetFile"), filename); | 904 | kAuthActionArgs.insert(QLatin1String("targetFile"), filename); | ||
913 | kAuthActionArgs.insert(QLatin1String("checksum"), checksum); | 905 | kAuthActionArgs.insert(QLatin1String("checksum"), cryptographicHash.result()); | ||
914 | kAuthActionArgs.insert(QLatin1String("ownerId"), ownerId); | 906 | kAuthActionArgs.insert(QLatin1String("ownerId"), ownerId); | ||
915 | kAuthActionArgs.insert(QLatin1String("groupId"), groupId); | 907 | kAuthActionArgs.insert(QLatin1String("groupId"), groupId); | ||
916 | 908 | | |||
917 | // call save with elevated privileges | 909 | // call save with elevated privileges | ||
918 | if (KTextEditor::EditorPrivate::unitTestMode()) { | 910 | if (KTextEditor::EditorPrivate::unitTestMode()) { | ||
919 | | ||||
920 | // unit testing purposes only | 911 | // unit testing purposes only | ||
921 | ok = SecureTextBuffer::savefile(kAuthActionArgs).succeeded(); | 912 | if (!SecureTextBuffer::savefile(kAuthActionArgs).succeeded()) { | ||
922 | 913 | return false; | |||
914 | } | ||||
923 | } else { | 915 | } else { | ||
924 | | ||||
925 | KAuth::Action kAuthSaveAction(QLatin1String("org.kde.ktexteditor.katetextbuffer.savefile")); | 916 | KAuth::Action kAuthSaveAction(QLatin1String("org.kde.ktexteditor.katetextbuffer.savefile")); | ||
926 | kAuthSaveAction.setHelperId(QLatin1String("org.kde.ktexteditor.katetextbuffer")); | 917 | kAuthSaveAction.setHelperId(QLatin1String("org.kde.ktexteditor.katetextbuffer")); | ||
927 | kAuthSaveAction.setArguments(kAuthActionArgs); | 918 | kAuthSaveAction.setArguments(kAuthActionArgs); | ||
928 | KAuth::ExecuteJob *job = kAuthSaveAction.execute(); | 919 | KAuth::ExecuteJob *job = kAuthSaveAction.execute(); | ||
929 | ok = job->exec(); | 920 | if (!job->exec()) { | ||
930 | 921 | return false; | |||
931 | } | | |||
932 | | ||||
933 | } else { | | |||
934 | | ||||
935 | // standard save without elevated privileges | | |||
936 | | ||||
937 | QSaveFile *saveFileLocal = static_cast<QSaveFile *>(saveFile.data()); | | |||
938 | | ||||
939 | if (!newFile) { | | |||
940 | // ensure correct owner | | |||
941 | SecureTextBuffer::setOwner(saveFileLocal->handle(), ownerId, groupId); | | |||
942 | } | 922 | } | ||
943 | | ||||
944 | ok = saveFileLocal->commit(); | | |||
945 | | ||||
946 | } | 923 | } | ||
947 | } | 924 | } | ||
948 | 925 | | |||
949 | // remember this revision as last saved if we had success! | 926 | // remember this revision as last saved | ||
950 | if (ok) { | | |||
951 | m_history.setLastSavedRevision(); | 927 | m_history.setLastSavedRevision(); | ||
952 | } | | |||
953 | 928 | | |||
954 | // report CODEC + ERRORS | 929 | // inform that we have saved the state | ||
955 | BUFFER_DEBUG << "Saved file " << filename << "with codec" << m_textCodec->name() << (ok ? "without" : "with") << "errors"; | | |||
956 | | ||||
957 | if (ok) { | | |||
958 | markModifiedLinesAsSaved(); | 930 | markModifiedLinesAsSaved(); | ||
959 | } | | |||
960 | 931 | | |||
961 | // emit signal on success | 932 | // emit that file was saved and be done | ||
962 | if (ok) { | | |||
963 | emit saved(filename); | 933 | emit saved(filename); | ||
964 | } | 934 | return true; | ||
965 | | ||||
966 | // return success or not | | |||
967 | return ok; | | |||
968 | } | 935 | } | ||
969 | 936 | | |||
970 | void TextBuffer::notifyAboutRangeChange(KTextEditor::View *view, int startLine, int endLine, bool rangeWithAttribute) | 937 | void TextBuffer::notifyAboutRangeChange(KTextEditor::View *view, int startLine, int endLine, bool rangeWithAttribute) | ||
971 | { | 938 | { | ||
972 | /** | 939 | /** | ||
973 | * ignore calls if no document is around | 940 | * ignore calls if no document is around | ||
974 | */ | 941 | */ | ||
975 | if (!m_document) { | 942 | if (!m_document) { | ||
▲ Show 20 Lines • Show All 70 Lines • Show Last 20 Lines |