1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2005-2007 Till Adam <adam@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | // $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $ |
8 | |
9 | #include <config-kiocore.h> |
10 | |
11 | #include "kacl.h" |
12 | |
13 | #if HAVE_POSIX_ACL |
14 | #include "../aclhelpers_p.h" |
15 | #endif |
16 | |
17 | #include <QDataStream> |
18 | #include <QHash> |
19 | #include <QString> |
20 | |
21 | #if HAVE_POSIX_ACL |
22 | using namespace KIO; |
23 | #endif |
24 | |
25 | class Q_DECL_HIDDEN KACL::KACLPrivate |
26 | { |
27 | public: |
28 | KACLPrivate() |
29 | #if HAVE_POSIX_ACL |
30 | : m_acl(nullptr) |
31 | #endif |
32 | { |
33 | } |
34 | #if HAVE_POSIX_ACL |
35 | explicit KACLPrivate(acl_t acl) |
36 | : m_acl(acl) |
37 | { |
38 | } |
39 | #endif |
40 | #if HAVE_POSIX_ACL |
41 | ~KACLPrivate() |
42 | { |
43 | if (m_acl) { |
44 | acl_free(m_acl); |
45 | } |
46 | } |
47 | #endif |
48 | // helpers |
49 | #if HAVE_POSIX_ACL |
50 | bool setMaskPermissions(unsigned short v); |
51 | QString getUserName(uid_t uid) const; |
52 | QString getGroupName(gid_t gid) const; |
53 | bool setAllUsersOrGroups(const QList<QPair<QString, unsigned short>> &list, acl_tag_t type); |
54 | bool setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type); |
55 | |
56 | acl_t m_acl; |
57 | mutable QHash<uid_t, QString> m_usercache; |
58 | mutable QHash<gid_t, QString> m_groupcache; |
59 | #endif |
60 | }; |
61 | |
62 | KACL::KACL(const QString &aclString) |
63 | : d(new KACLPrivate) |
64 | { |
65 | setACL(aclString); |
66 | } |
67 | |
68 | KACL::KACL(mode_t basePermissions) |
69 | #if HAVE_POSIX_ACL |
70 | : d(new KACLPrivate(ACLPortability::acl_from_mode(basePermissions))) |
71 | #else |
72 | : d(new KACLPrivate) |
73 | #endif |
74 | { |
75 | #if !HAVE_POSIX_ACL |
76 | Q_UNUSED(basePermissions); |
77 | #endif |
78 | } |
79 | |
80 | KACL::KACL() |
81 | : d(new KACLPrivate) |
82 | { |
83 | } |
84 | |
85 | KACL::KACL(const KACL &rhs) |
86 | : d(new KACLPrivate) |
87 | { |
88 | setACL(rhs.asString()); |
89 | } |
90 | |
91 | KACL::~KACL() = default; |
92 | |
93 | KACL &KACL::operator=(const KACL &rhs) |
94 | { |
95 | if (this != &rhs) { |
96 | setACL(rhs.asString()); |
97 | } |
98 | return *this; |
99 | } |
100 | |
101 | bool KACL::operator==(const KACL &rhs) const |
102 | { |
103 | #if HAVE_POSIX_ACL |
104 | return (ACLPortability::acl_cmp(d->m_acl, rhs.d->m_acl) == 0); |
105 | #else |
106 | Q_UNUSED(rhs); |
107 | return true; |
108 | #endif |
109 | } |
110 | |
111 | bool KACL::operator!=(const KACL &rhs) const |
112 | { |
113 | return !operator==(rhs); |
114 | } |
115 | |
116 | bool KACL::isValid() const |
117 | { |
118 | bool valid = false; |
119 | #if HAVE_POSIX_ACL |
120 | if (d->m_acl) { |
121 | valid = (acl_valid(d->m_acl) == 0); |
122 | } |
123 | #endif |
124 | return valid; |
125 | } |
126 | |
127 | bool KACL::isExtended() const |
128 | { |
129 | #if HAVE_POSIX_ACL |
130 | return (ACLPortability::acl_equiv_mode(d->m_acl, nullptr) != 0); |
131 | #else |
132 | return false; |
133 | #endif |
134 | } |
135 | |
136 | #if HAVE_POSIX_ACL |
137 | static acl_entry_t entryForTag(acl_t acl, acl_tag_t tag) |
138 | { |
139 | acl_entry_t entry; |
140 | int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); |
141 | while (ret == 1) { |
142 | acl_tag_t currentTag; |
143 | acl_get_tag_type(entry, ¤tTag); |
144 | if (currentTag == tag) { |
145 | return entry; |
146 | } |
147 | ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); |
148 | } |
149 | return nullptr; |
150 | } |
151 | |
152 | static unsigned short entryToPermissions(acl_entry_t entry) |
153 | { |
154 | if (entry == nullptr) { |
155 | return 0; |
156 | } |
157 | acl_permset_t permset; |
158 | if (acl_get_permset(entry, &permset) != 0) { |
159 | return 0; |
160 | } |
161 | return (ACLPortability::acl_get_perm(permset, ACL_READ) << 2 | ACLPortability::acl_get_perm(permset, ACL_WRITE) << 1 |
162 | | ACLPortability::acl_get_perm(permset, ACL_EXECUTE)); |
163 | } |
164 | |
165 | static void permissionsToEntry(acl_entry_t entry, unsigned short v) |
166 | { |
167 | if (entry == nullptr) { |
168 | return; |
169 | } |
170 | acl_permset_t permset; |
171 | if (acl_get_permset(entry, &permset) != 0) { |
172 | return; |
173 | } |
174 | acl_clear_perms(permset); |
175 | if (v & 4) { |
176 | acl_add_perm(permset, ACL_READ); |
177 | } |
178 | if (v & 2) { |
179 | acl_add_perm(permset, ACL_WRITE); |
180 | } |
181 | if (v & 1) { |
182 | acl_add_perm(permset, ACL_EXECUTE); |
183 | } |
184 | } |
185 | |
186 | static int getUidForName(const QString &name) |
187 | { |
188 | struct passwd *user = getpwnam(name.toLocal8Bit().constData()); |
189 | if (user) { |
190 | return user->pw_uid; |
191 | } else { |
192 | return -1; |
193 | } |
194 | } |
195 | |
196 | static int getGidForName(const QString &name) |
197 | { |
198 | struct group *group = getgrnam(name.toLocal8Bit().constData()); |
199 | if (group) { |
200 | return group->gr_gid; |
201 | } else { |
202 | return -1; |
203 | } |
204 | } |
205 | #endif |
206 | // ------------------ begin API implementation ------------ |
207 | |
208 | unsigned short KACL::ownerPermissions() const |
209 | { |
210 | #if HAVE_POSIX_ACL |
211 | return entryToPermissions(entryForTag(d->m_acl, ACL_USER_OBJ)); |
212 | #else |
213 | return 0; |
214 | #endif |
215 | } |
216 | |
217 | bool KACL::setOwnerPermissions(unsigned short v) |
218 | { |
219 | #if HAVE_POSIX_ACL |
220 | permissionsToEntry(entryForTag(d->m_acl, ACL_USER_OBJ), v); |
221 | #else |
222 | Q_UNUSED(v); |
223 | #endif |
224 | return true; |
225 | } |
226 | |
227 | unsigned short KACL::owningGroupPermissions() const |
228 | { |
229 | #if HAVE_POSIX_ACL |
230 | return entryToPermissions(entryForTag(d->m_acl, ACL_GROUP_OBJ)); |
231 | #else |
232 | return 0; |
233 | #endif |
234 | } |
235 | |
236 | bool KACL::setOwningGroupPermissions(unsigned short v) |
237 | { |
238 | #if HAVE_POSIX_ACL |
239 | permissionsToEntry(entryForTag(d->m_acl, ACL_GROUP_OBJ), v); |
240 | #else |
241 | Q_UNUSED(v); |
242 | #endif |
243 | return true; |
244 | } |
245 | |
246 | unsigned short KACL::othersPermissions() const |
247 | { |
248 | #if HAVE_POSIX_ACL |
249 | return entryToPermissions(entryForTag(d->m_acl, ACL_OTHER)); |
250 | #else |
251 | return 0; |
252 | #endif |
253 | } |
254 | |
255 | bool KACL::setOthersPermissions(unsigned short v) |
256 | { |
257 | #if HAVE_POSIX_ACL |
258 | permissionsToEntry(entryForTag(d->m_acl, ACL_OTHER), v); |
259 | #else |
260 | Q_UNUSED(v); |
261 | #endif |
262 | return true; |
263 | } |
264 | |
265 | mode_t KACL::basePermissions() const |
266 | { |
267 | mode_t perms(0); |
268 | #if HAVE_POSIX_ACL |
269 | if (ownerPermissions() & ACL_READ) { |
270 | perms |= S_IRUSR; |
271 | } |
272 | if (ownerPermissions() & ACL_WRITE) { |
273 | perms |= S_IWUSR; |
274 | } |
275 | if (ownerPermissions() & ACL_EXECUTE) { |
276 | perms |= S_IXUSR; |
277 | } |
278 | if (owningGroupPermissions() & ACL_READ) { |
279 | perms |= S_IRGRP; |
280 | } |
281 | if (owningGroupPermissions() & ACL_WRITE) { |
282 | perms |= S_IWGRP; |
283 | } |
284 | if (owningGroupPermissions() & ACL_EXECUTE) { |
285 | perms |= S_IXGRP; |
286 | } |
287 | if (othersPermissions() & ACL_READ) { |
288 | perms |= S_IROTH; |
289 | } |
290 | if (othersPermissions() & ACL_WRITE) { |
291 | perms |= S_IWOTH; |
292 | } |
293 | if (othersPermissions() & ACL_EXECUTE) { |
294 | perms |= S_IXOTH; |
295 | } |
296 | #endif |
297 | return perms; |
298 | } |
299 | |
300 | unsigned short KACL::maskPermissions(bool &exists) const |
301 | { |
302 | exists = true; |
303 | #if HAVE_POSIX_ACL |
304 | acl_entry_t entry = entryForTag(d->m_acl, ACL_MASK); |
305 | if (entry == nullptr) { |
306 | exists = false; |
307 | return 0; |
308 | } |
309 | return entryToPermissions(entry); |
310 | #else |
311 | return 0; |
312 | #endif |
313 | } |
314 | |
315 | #if HAVE_POSIX_ACL |
316 | bool KACL::KACLPrivate::setMaskPermissions(unsigned short v) |
317 | { |
318 | acl_entry_t entry = entryForTag(m_acl, ACL_MASK); |
319 | if (entry == nullptr) { |
320 | acl_create_entry(&m_acl, &entry); |
321 | acl_set_tag_type(entry, ACL_MASK); |
322 | } |
323 | permissionsToEntry(entry, v); |
324 | return true; |
325 | } |
326 | #endif |
327 | |
328 | bool KACL::setMaskPermissions(unsigned short v) |
329 | { |
330 | #if HAVE_POSIX_ACL |
331 | return d->setMaskPermissions(v); |
332 | #else |
333 | Q_UNUSED(v); |
334 | return true; |
335 | #endif |
336 | } |
337 | |
338 | #if HAVE_POSIX_ACL |
339 | using unique_ptr_acl_free = std::unique_ptr<void, int (*)(void *)>; |
340 | #endif |
341 | |
342 | /************************** |
343 | * Deal with named users * |
344 | **************************/ |
345 | unsigned short KACL::namedUserPermissions(const QString &name, bool *exists) const |
346 | { |
347 | #if HAVE_POSIX_ACL |
348 | acl_entry_t entry; |
349 | *exists = false; |
350 | int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); |
351 | while (ret == 1) { |
352 | acl_tag_t currentTag; |
353 | acl_get_tag_type(entry, ¤tTag); |
354 | if (currentTag == ACL_USER) { |
355 | const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); |
356 | const uid_t id = *(static_cast<uid_t *>(idptr.get())); |
357 | if (d->getUserName(id) == name) { |
358 | *exists = true; |
359 | return entryToPermissions(entry); |
360 | } |
361 | } |
362 | ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); |
363 | } |
364 | #else |
365 | Q_UNUSED(name); |
366 | Q_UNUSED(exists); |
367 | #endif |
368 | return 0; |
369 | } |
370 | |
371 | #if HAVE_POSIX_ACL |
372 | bool KACL::KACLPrivate::setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type) |
373 | { |
374 | bool allIsWell = true; |
375 | acl_t newACL = acl_dup(m_acl); |
376 | acl_entry_t entry; |
377 | bool createdNewEntry = false; |
378 | bool found = false; |
379 | int ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry); |
380 | while (ret == 1) { |
381 | acl_tag_t currentTag; |
382 | acl_get_tag_type(entry, ¤tTag); |
383 | if (currentTag == type) { |
384 | const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); |
385 | const int id = *(static_cast<int *>(idptr.get())); // We assume that sizeof(uid_t) == sizeof(gid_t) |
386 | const QString entryName = type == ACL_USER ? getUserName(id) : getGroupName(id); |
387 | if (entryName == name) { |
388 | // found him, update |
389 | permissionsToEntry(entry, permissions); |
390 | found = true; |
391 | break; |
392 | } |
393 | } |
394 | ret = acl_get_entry(newACL, ACL_NEXT_ENTRY, &entry); |
395 | } |
396 | if (!found) { |
397 | acl_create_entry(&newACL, &entry); |
398 | acl_set_tag_type(entry, type); |
399 | int id = type == ACL_USER ? getUidForName(name) : getGidForName(name); |
400 | if (id == -1 || acl_set_qualifier(entry, &id) != 0) { |
401 | acl_delete_entry(newACL, entry); |
402 | allIsWell = false; |
403 | } else { |
404 | permissionsToEntry(entry, permissions); |
405 | createdNewEntry = true; |
406 | } |
407 | } |
408 | if (allIsWell && createdNewEntry) { |
409 | // 23.1.1 of 1003.1e states that as soon as there is a named user or |
410 | // named group entry, there needs to be a mask entry as well, so add |
411 | // one, if the user hasn't explicitly set one. |
412 | if (entryForTag(newACL, ACL_MASK) == nullptr) { |
413 | acl_calc_mask(&newACL); |
414 | } |
415 | } |
416 | |
417 | if (!allIsWell || acl_valid(newACL) != 0) { |
418 | acl_free(newACL); |
419 | allIsWell = false; |
420 | } else { |
421 | acl_free(m_acl); |
422 | m_acl = newACL; |
423 | } |
424 | return allIsWell; |
425 | } |
426 | #endif |
427 | |
428 | bool KACL::setNamedUserPermissions(const QString &name, unsigned short permissions) |
429 | { |
430 | #if HAVE_POSIX_ACL |
431 | return d->setNamedUserOrGroupPermissions(name, permissions, ACL_USER); |
432 | #else |
433 | Q_UNUSED(name); |
434 | Q_UNUSED(permissions); |
435 | return true; |
436 | #endif |
437 | } |
438 | |
439 | ACLUserPermissionsList KACL::allUserPermissions() const |
440 | { |
441 | ACLUserPermissionsList list; |
442 | #if HAVE_POSIX_ACL |
443 | acl_entry_t entry; |
444 | int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); |
445 | while (ret == 1) { |
446 | acl_tag_t currentTag; |
447 | acl_get_tag_type(entry, ¤tTag); |
448 | if (currentTag == ACL_USER) { |
449 | const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); |
450 | const uid_t id = *(static_cast<uid_t *>(idptr.get())); |
451 | QString name = d->getUserName(id); |
452 | unsigned short permissions = entryToPermissions(entry); |
453 | ACLUserPermissions pair = qMakePair(name, permissions); |
454 | list.append(pair); |
455 | } |
456 | ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); |
457 | } |
458 | #endif |
459 | return list; |
460 | } |
461 | |
462 | #if HAVE_POSIX_ACL |
463 | bool KACL::KACLPrivate::setAllUsersOrGroups(const QList<QPair<QString, unsigned short>> &list, acl_tag_t type) |
464 | { |
465 | bool allIsWell = true; |
466 | bool atLeastOneUserOrGroup = false; |
467 | |
468 | // make working copy, in case something goes wrong |
469 | acl_t newACL = acl_dup(m_acl); |
470 | acl_entry_t entry; |
471 | |
472 | // clear user entries |
473 | int ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry); |
474 | while (ret == 1) { |
475 | acl_tag_t currentTag; |
476 | acl_get_tag_type(entry, ¤tTag); |
477 | if (currentTag == type) { |
478 | acl_delete_entry(newACL, entry); |
479 | // we have to start from the beginning, the iterator is |
480 | // invalidated, on deletion |
481 | ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry); |
482 | } else { |
483 | ret = acl_get_entry(newACL, ACL_NEXT_ENTRY, &entry); |
484 | } |
485 | } |
486 | |
487 | // now add the entries from the list |
488 | for (const auto &[name, userId] : list) { |
489 | acl_create_entry(&newACL, &entry); |
490 | acl_set_tag_type(entry, type); |
491 | int id = type == ACL_USER ? getUidForName(name) : getGidForName(name); |
492 | if (id == -1 || acl_set_qualifier(entry, &id) != 0) { |
493 | // user or group doesn't exist => error |
494 | acl_delete_entry(newACL, entry); |
495 | allIsWell = false; |
496 | break; |
497 | } else { |
498 | permissionsToEntry(entry, userId); |
499 | atLeastOneUserOrGroup = true; |
500 | } |
501 | } |
502 | |
503 | if (allIsWell && atLeastOneUserOrGroup) { |
504 | // 23.1.1 of 1003.1e states that as soon as there is a named user or |
505 | // named group entry, there needs to be a mask entry as well, so add |
506 | // one, if the user hasn't explicitly set one. |
507 | if (entryForTag(newACL, ACL_MASK) == nullptr) { |
508 | acl_calc_mask(&newACL); |
509 | } |
510 | } |
511 | if (allIsWell && (acl_valid(newACL) == 0)) { |
512 | acl_free(m_acl); |
513 | m_acl = newACL; |
514 | } else { |
515 | acl_free(newACL); |
516 | } |
517 | return allIsWell; |
518 | } |
519 | #endif |
520 | |
521 | bool KACL::setAllUserPermissions(const ACLUserPermissionsList &users) |
522 | { |
523 | #if HAVE_POSIX_ACL |
524 | return d->setAllUsersOrGroups(users, ACL_USER); |
525 | #else |
526 | Q_UNUSED(users); |
527 | return true; |
528 | #endif |
529 | } |
530 | |
531 | /************************** |
532 | * Deal with named groups * |
533 | **************************/ |
534 | |
535 | unsigned short KACL::namedGroupPermissions(const QString &name, bool *exists) const |
536 | { |
537 | *exists = false; |
538 | #if HAVE_POSIX_ACL |
539 | acl_entry_t entry; |
540 | int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); |
541 | while (ret == 1) { |
542 | acl_tag_t currentTag; |
543 | acl_get_tag_type(entry, ¤tTag); |
544 | if (currentTag == ACL_GROUP) { |
545 | const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); |
546 | const gid_t id = *(static_cast<gid_t *>(idptr.get())); |
547 | if (d->getGroupName(id) == name) { |
548 | *exists = true; |
549 | return entryToPermissions(entry); |
550 | } |
551 | } |
552 | ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); |
553 | } |
554 | #else |
555 | Q_UNUSED(name); |
556 | #endif |
557 | return 0; |
558 | } |
559 | |
560 | bool KACL::setNamedGroupPermissions(const QString &name, unsigned short permissions) |
561 | { |
562 | #if HAVE_POSIX_ACL |
563 | return d->setNamedUserOrGroupPermissions(name, permissions, ACL_GROUP); |
564 | #else |
565 | Q_UNUSED(name); |
566 | Q_UNUSED(permissions); |
567 | return true; |
568 | #endif |
569 | } |
570 | |
571 | ACLGroupPermissionsList KACL::allGroupPermissions() const |
572 | { |
573 | ACLGroupPermissionsList list; |
574 | #if HAVE_POSIX_ACL |
575 | acl_entry_t entry; |
576 | int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); |
577 | while (ret == 1) { |
578 | acl_tag_t currentTag; |
579 | acl_get_tag_type(entry, ¤tTag); |
580 | if (currentTag == ACL_GROUP) { |
581 | const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); |
582 | const gid_t id = *(static_cast<gid_t *>(idptr.get())); |
583 | QString name = d->getGroupName(id); |
584 | unsigned short permissions = entryToPermissions(entry); |
585 | ACLGroupPermissions pair = qMakePair(name, permissions); |
586 | list.append(pair); |
587 | } |
588 | ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); |
589 | } |
590 | #endif |
591 | return list; |
592 | } |
593 | |
594 | bool KACL::setAllGroupPermissions(const ACLGroupPermissionsList &groups) |
595 | { |
596 | #if HAVE_POSIX_ACL |
597 | return d->setAllUsersOrGroups(groups, ACL_GROUP); |
598 | #else |
599 | Q_UNUSED(groups); |
600 | return true; |
601 | #endif |
602 | } |
603 | |
604 | /************************** |
605 | * from and to string * |
606 | **************************/ |
607 | |
608 | bool KACL::setACL(const QString &aclStr) |
609 | { |
610 | bool ret = false; |
611 | #if HAVE_POSIX_ACL |
612 | acl_t temp = acl_from_text(aclStr.toLatin1().constData()); |
613 | if (acl_valid(temp) != 0) { |
614 | // TODO errno is set, what to do with it here? |
615 | acl_free(temp); |
616 | } else { |
617 | if (d->m_acl) { |
618 | acl_free(d->m_acl); |
619 | } |
620 | d->m_acl = temp; |
621 | ret = true; |
622 | } |
623 | #else |
624 | Q_UNUSED(aclStr); |
625 | #endif |
626 | return ret; |
627 | } |
628 | |
629 | QString KACL::asString() const |
630 | { |
631 | #if HAVE_POSIX_ACL |
632 | ssize_t size = 0; |
633 | char *txt = acl_to_text(d->m_acl, &size); |
634 | const QString ret = QString::fromLatin1(txt, size); |
635 | acl_free(txt); |
636 | return ret; |
637 | #else |
638 | return QString(); |
639 | #endif |
640 | } |
641 | |
642 | // helpers |
643 | |
644 | #if HAVE_POSIX_ACL |
645 | QString KACL::KACLPrivate::getUserName(uid_t uid) const |
646 | { |
647 | auto it = m_usercache.find(uid); |
648 | if (it == m_usercache.end()) { |
649 | struct passwd *user = getpwuid(uid); |
650 | if (user) { |
651 | it = m_usercache.insert(uid, QString::fromLatin1(user->pw_name)); |
652 | } else { |
653 | return QString::number(uid); |
654 | } |
655 | } |
656 | return *it; |
657 | } |
658 | |
659 | QString KACL::KACLPrivate::getGroupName(gid_t gid) const |
660 | { |
661 | auto it = m_groupcache.find(gid); |
662 | if (it == m_groupcache.end()) { |
663 | struct group *grp = getgrgid(gid); |
664 | if (grp) { |
665 | it = m_groupcache.insert(gid, QString::fromLatin1(grp->gr_name)); |
666 | } else { |
667 | return QString::number(gid); |
668 | } |
669 | } |
670 | return *it; |
671 | } |
672 | #endif |
673 | |
674 | void KACL::virtual_hook(int, void *) |
675 | { |
676 | /*BASE::virtual_hook( id, data );*/ |
677 | } |
678 | |
679 | QDataStream &operator<<(QDataStream &s, const KACL &a) |
680 | { |
681 | s << a.asString(); |
682 | return s; |
683 | } |
684 | |
685 | QDataStream &operator>>(QDataStream &s, KACL &a) |
686 | { |
687 | QString str; |
688 | s >> str; |
689 | a.setACL(str); |
690 | return s; |
691 | } |
692 | |