Krita Source Code Documentation
Loading...
Searching...
No Matches
KisMacosSecurityBookmarkManager.mm
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2023 Ivan Santa MarĂ­a <ghevan@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8
9#import <Foundation/Foundation.h>
10#import <Security/SecCode.h>
11
12#ifdef KIS_STANDALONE
13#import <AppKit/AppKit.h>
14#endif
15
16#include <QStandardPaths>
17#include <QSettings>
18#include <QHash>
19#include <QVariant>
20#include <QString>
21#include <QUrl>
22#include <QMetaEnum>
23
24#include <QMessageBox>
25#include <QFileDialog>
26
27#include <QDebug>
28
29#include <klocalizedstring.h>
30
32
33
34#if ! __has_feature(objc_arc)
35#error "Enable ARC to compile this file"
36#endif
37
39
41{
42public:
44 : securedFiles(QHash<QString,QString>())
45 , entitlements(KisMacosEntitlements())
46 {
47 }
48
50 {
51 }
52
54 QHash<QString, QString> securedFiles;
55};
56
61
68
73
75{
76 bool contained = false;
77 Q_FOREACH(QString key, m_d->securedFiles.keys()) {
78 if(path.contains(key)) {
79 contained = true;
80 break;
81 }
82 }
83 return contained;
84}
85
86void KisMacosSecurityBookmarkManager::createBookmarkFromPath(const QString &path, const QString &refpath, SecurityBookmarkType type)
87{
88 if(path.isEmpty()) {
89 return;
90 }
91 NSURLBookmarkCreationOptions
92 options = m_d->entitlements.hasEntitlement(KisMacosEntitlements::Entitlements::BookmarkScopeApp) ? NSURLBookmarkCreationWithSecurityScope : 0;
93 NSError *err = nil;
94
95 NSString *pathEscaped = [path.toNSString() stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
96 NSURL *url = [NSURL URLWithString:pathEscaped];
97
98 NSURL *refurl = nil;
99 if (!refpath.isEmpty()) {
100 NSString *refpathEscaped = [refpath.toNSString() stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
101 refurl = [NSURL URLWithString:refpathEscaped];
102 }
103
104 // working with paths as strings
105 // this assumes user used NSOpenPanel on the path itself
106
107 // Create bookmark
108 NSData *bookmark = [url bookmarkDataWithOptions: options includingResourceValuesForKeys: nil relativeToURL: refurl error:&err];
109 NSString *bookmarkString = [bookmark base64EncodedStringWithOptions: NSDataBase64Encoding64CharacterLineLength];
110 NSLog(@" path: %@\n err: %@", pathEscaped, err);
111
112 if (!err) {
113 [url startAccessingSecurityScopedResource];
114
115 QString base64Data = QString::fromNSString(bookmarkString);
116 // write to file
117 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
118 QSettings kritarc(configPath + QStringLiteral("/securitybookmarkrc"), QSettings::NativeFormat);
119
120 // get array current size
121 int size = kritarc.beginReadArray(securityBookmarkTypeToString(type));
122 kritarc.endArray();
123
124 kritarc.beginWriteArray(securityBookmarkTypeToString(type));
125 kritarc.setArrayIndex(size);
126 kritarc.setValue("path",path);
127 kritarc.setValue("base64", base64Data);
128 kritarc.endArray();
129
130 // Finally add to hashmap
131 m_d->securedFiles[path] = base64Data;
132 }
133
134 return;
135}
136
138{
139 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
140 QSettings kritarc(configPath + QStringLiteral("/securitybookmarkrc"), QSettings::NativeFormat);
141
142 int size = kritarc.beginReadArray(securityBookmarkTypeToString(arrayKey));
143 for (int i = 0; i < size; i++) {
144 kritarc.setArrayIndex(i);
145 QString key = kritarc.value("path").toString();
146 m_d->securedFiles[key] = kritarc.value("base64").toString();
147 }
148 kritarc.endArray();
149}
150
156
158 NSString *bookmarkString = encodedPath.toNSString();
159 NSData *decodedBookmark = [[NSData alloc] initWithBase64EncodedString: bookmarkString options:NSDataBase64DecodingIgnoreUnknownCharacters];
160
161 NSError *err = nil;
162
163 // Resolve the decoded bookmark data into a security-scoped URL.
164 NSURL *url =[NSURL URLByResolvingBookmarkData: decodedBookmark options: NSURLBookmarkResolutionWithSecurityScope relativeToURL: nil bookmarkDataIsStale: nil error:&err];
165 return QUrl::fromNSURL(url);
166}
167
169{
170 Q_FOREACH(QString encodedPath, m_d->securedFiles.values()) {
171 NSURL *url = decodeBookmarkToURL(encodedPath).toNSURL();
172 [url startAccessingSecurityScopedResource];
173 }
174}
175
177{
178 Q_FOREACH(QString encodedPath, m_d->securedFiles.values()) {
179 NSURL *url = decodeBookmarkToURL(encodedPath).toNSURL();
180 [url stopAccessingSecurityScopedResource];
181 }
182}
183
185{
186 bool fileSelected = false;
187 QUrl fileURL = QUrl(path);
188
189 NSString *nsStdPath = path.toNSString();
190#ifdef KIS_STANDALONE
191 NSAlert *alert = [[NSAlert alloc] init];
192 [alert setAlertStyle:NSAlertStyleInformational];
193 [alert setMessageText:[NSString stringWithFormat:@"The file %@ is located in a directory where the application has no permission, please give the permission to the container folder or a higher one to allow krita to save temporary backups next to your file", [nsStdPath lastPathComponent]]];
194 [alert runModal];
195
196
197 NSOpenPanel *panel = [NSOpenPanel openPanel];
198 panel.canChooseFiles = false;
199 panel.canChooseDirectories = true;
200 NSURL *startLoc = [[NSURL alloc] initFileURLWithPath:nsStdPath ];
201 panel.directoryURL = startLoc;
202
203 if ([panel runModal] == NSModalResponseOK) {
204 static NSURL *fileUrl = [panel URL];
205 NSString *pathString = [fileUrl absoluteString];
206
207 QString filepath = QString::fromNSString(pathString);
209 fileSelected = true;
210 }
211
212#else
213 QMessageBox msgBox;
214 msgBox.setText(i18n("The file %1 is located in a directory where the application has no permissions, please give krita permission to this directory or a higher one to allow krita to save temporary backups next to your file", fileURL.fileName()));
215 msgBox.setInformativeText(i18n("The directory you select will grant krita permissions to all files and directories contained in it"));
216 msgBox.setStandardButtons(QMessageBox::Ok);
217 msgBox.setDefaultButton(QMessageBox::Ok);
218 int ret = msgBox.exec();
219
220 QUrl dirUrl = QFileDialog::getExistingDirectoryUrl(0, QString(), QUrl(path));
221
222 if (!dirUrl.isEmpty()) {
223 QString filepath = dirUrl.toDisplayString(QUrl::None);
225 fileSelected = true;
226 }
227#endif
228 return fileSelected;
229}
230
232{
233 return QMetaEnum::fromType<SecurityBookmarkType>().valueToKey(type);
234}
235
237{
238 const QString path = url.toDisplayString(QUrl::None);
239 qDebug() << "1 inserting to sandbox" << url << path;
240 if (!parentDirHasPermissions(path)) {
241 // we can't force the user to select a directory in particular
242 // we add the bookmark even if the file root directory was selected
243 createBookmarkFromPath(path, QString());
244
245 requestAccessToDir(path);
246 }
247}
248
250{
251 // necessary convert to get proper location
252 QUrl url = QUrl::fromLocalFile(path);
253 QString pathString = url.toDisplayString();
254 if (!m_d->securedFiles.contains(pathString)) {
255 createBookmarkFromPath(pathString, QString());
256 }
257}
258
260{
261 return m_d->entitlements.sandbox();
262}
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
void createBookmarkFromPath(const QString &path, const QString &refpath, SecurityBookmarkType type=SecurityBookmarkType::File)
static KisMacosSecurityBookmarkManager * instance()
QString securityBookmarkTypeToString(const SecurityBookmarkType)