kacl.cpp
00001 /* This file is part of the KDE project 00002 Copyright (C) 2005 Till Adam <adam@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 // $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $ 00020 00021 #ifdef HAVE_CONFIG_H 00022 #include <config.h> 00023 #endif 00024 00025 #include <sys/types.h> 00026 #include <pwd.h> 00027 #include <grp.h> 00028 #include <sys/stat.h> 00029 #ifdef USE_POSIX_ACL 00030 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS 00031 #include <acl/libacl.h> 00032 #else 00033 #include <posixacladdons.h> 00034 #endif 00035 #endif 00036 #include <tqintdict.h> 00037 00038 #include <kdebug.h> 00039 00040 #include "kacl.h" 00041 00042 00043 #ifdef USE_POSIX_ACL 00044 static void printACL( acl_t acl, const TQString &comment ); 00045 static TQString aclAsString(const acl_t acl); 00046 #endif 00047 00048 class KACL::KACLPrivate { 00049 public: 00050 KACLPrivate() : m_acl( 0 ) { init(); } 00051 #ifdef USE_POSIX_ACL 00052 KACLPrivate( acl_t acl ) 00053 : m_acl( acl ) { init(); } 00054 ~KACLPrivate() { if ( m_acl ) acl_free( m_acl ); } 00055 #endif 00056 void init() { 00057 m_usercache.setAutoDelete( true ); 00058 m_groupcache.setAutoDelete( true ); 00059 } 00060 // helpers 00061 #ifdef USE_POSIX_ACL 00062 bool setMaskPermissions( unsigned short v ); 00063 TQString getUserName( uid_t uid ) const; 00064 TQString getGroupName( gid_t gid ) const; 00065 bool setAllUsersOrGroups( const TQValueList< QPair<TQString, unsigned short> > &list, acl_tag_t type ); 00066 bool setNamedUserOrGroupPermissions( const TQString& name, unsigned short permissions, acl_tag_t type ); 00067 00068 acl_t m_acl; 00069 #else 00070 int m_acl; 00071 #endif 00072 mutable TQIntDict<TQString> m_usercache; 00073 mutable TQIntDict<TQString> m_groupcache; 00074 }; 00075 00076 KACL::KACL( const TQString &aclString ) 00077 : d( new KACLPrivate ) 00078 { 00079 setACL( aclString ); 00080 } 00081 00082 KACL::KACL( mode_t basePermissions ) 00083 #ifdef USE_POSIX_ACL 00084 : d( new KACLPrivate( acl_from_mode( basePermissions ) ) ) 00085 #else 00086 : d( new KACLPrivate ) 00087 #endif 00088 { 00089 #ifndef USE_POSIX_ACL 00090 Q_UNUSED( basePermissions ); 00091 #endif 00092 } 00093 00094 KACL::KACL() 00095 : d( new KACLPrivate ) 00096 { 00097 } 00098 00099 KACL::KACL( const KACL& rhs ) 00100 : d( new KACLPrivate ) 00101 { 00102 setACL( rhs.asString() ); 00103 } 00104 00105 KACL::~KACL() 00106 { 00107 delete d; 00108 } 00109 00110 bool KACL::operator==( const KACL& rhs ) const { 00111 #ifdef USE_POSIX_ACL 00112 return ( acl_cmp( d->m_acl, rhs.d->m_acl ) == 0 ); 00113 #else 00114 Q_UNUSED( rhs ); 00115 return true; 00116 #endif 00117 } 00118 00119 bool KACL::isValid() const 00120 { 00121 bool valid = false; 00122 #ifdef USE_POSIX_ACL 00123 if ( d->m_acl ) { 00124 valid = ( acl_valid( d->m_acl ) == 0 ); 00125 } 00126 #endif 00127 return valid; 00128 } 00129 00130 bool KACL::isExtended() const 00131 { 00132 #ifdef USE_POSIX_ACL 00133 return ( acl_equiv_mode( d->m_acl, NULL ) != 0 ); 00134 #else 00135 return false; 00136 #endif 00137 } 00138 00139 #ifdef USE_POSIX_ACL 00140 static acl_entry_t entryForTag( acl_t acl, acl_tag_t tag ) 00141 { 00142 acl_entry_t entry; 00143 int ret = acl_get_entry( acl, ACL_FIRST_ENTRY, &entry ); 00144 while ( ret == 1 ) { 00145 acl_tag_t currentTag; 00146 acl_get_tag_type( entry, ¤tTag ); 00147 if ( currentTag == tag ) 00148 return entry; 00149 ret = acl_get_entry( acl, ACL_NEXT_ENTRY, &entry ); 00150 } 00151 return 0; 00152 } 00153 00154 static unsigned short entryToPermissions( acl_entry_t entry ) 00155 { 00156 if ( entry == 0 ) return 0; 00157 acl_permset_t permset; 00158 if ( acl_get_permset( entry, &permset ) != 0 ) return 0; 00159 return( acl_get_perm( permset, ACL_READ ) << 2 | 00160 acl_get_perm( permset, ACL_WRITE ) << 1 | 00161 acl_get_perm( permset, ACL_EXECUTE ) ); 00162 } 00163 00164 static void permissionsToEntry( acl_entry_t entry, unsigned short v ) 00165 { 00166 if ( entry == 0 ) return; 00167 acl_permset_t permset; 00168 if ( acl_get_permset( entry, &permset ) != 0 ) return; 00169 acl_clear_perms( permset ); 00170 if ( v & 4 ) acl_add_perm( permset, ACL_READ ); 00171 if ( v & 2 ) acl_add_perm( permset, ACL_WRITE ); 00172 if ( v & 1 ) acl_add_perm( permset, ACL_EXECUTE ); 00173 } 00174 00175 static void printACL( acl_t acl, const TQString &comment ) 00176 { 00177 kdDebug() << comment << aclAsString( acl ) << endl; 00178 } 00179 00180 static int getUidForName( const TQString& name ) 00181 { 00182 struct passwd *user = getpwnam( name.latin1() ); 00183 if ( user ) 00184 return user->pw_uid; 00185 else 00186 return -1; 00187 } 00188 00189 static int getGidForName( const TQString& name ) 00190 { 00191 struct group *group = getgrnam( name.latin1() ); 00192 if ( group ) 00193 return group->gr_gid; 00194 else 00195 return -1; 00196 } 00197 #endif 00198 // ------------------ begin API implementation ------------ 00199 00200 unsigned short KACL::ownerPermissions() const 00201 { 00202 #ifdef USE_POSIX_ACL 00203 return entryToPermissions( entryForTag( d->m_acl, ACL_USER_OBJ ) ); 00204 #else 00205 return 0; 00206 #endif 00207 } 00208 00209 bool KACL::setOwnerPermissions( unsigned short v ) 00210 { 00211 #ifdef USE_POSIX_ACL 00212 permissionsToEntry( entryForTag( d->m_acl, ACL_USER_OBJ ), v ); 00213 #else 00214 Q_UNUSED( v ); 00215 #endif 00216 return true; 00217 } 00218 00219 unsigned short KACL::owningGroupPermissions() const 00220 { 00221 #ifdef USE_POSIX_ACL 00222 return entryToPermissions( entryForTag( d->m_acl, ACL_GROUP_OBJ ) ); 00223 #else 00224 return 0; 00225 #endif 00226 } 00227 00228 bool KACL::setOwningGroupPermissions( unsigned short v ) 00229 { 00230 #ifdef USE_POSIX_ACL 00231 permissionsToEntry( entryForTag( d->m_acl, ACL_GROUP_OBJ ), v ); 00232 #else 00233 Q_UNUSED( v ); 00234 #endif 00235 return true; 00236 } 00237 00238 unsigned short KACL::othersPermissions() const 00239 { 00240 #ifdef USE_POSIX_ACL 00241 return entryToPermissions( entryForTag( d->m_acl, ACL_OTHER ) ); 00242 #else 00243 return 0; 00244 #endif 00245 } 00246 00247 bool KACL::setOthersPermissions( unsigned short v ) 00248 { 00249 #ifdef USE_POSIX_ACL 00250 permissionsToEntry( entryForTag( d->m_acl, ACL_OTHER ), v ); 00251 #else 00252 Q_UNUSED( v ); 00253 #endif 00254 return true; 00255 } 00256 00257 mode_t KACL::basePermissions() const 00258 { 00259 mode_t perms( 0 ); 00260 #ifdef USE_POSIX_ACL 00261 if ( ownerPermissions() & ACL_READ ) perms |= S_IRUSR; 00262 if ( ownerPermissions() & ACL_WRITE ) perms |= S_IWUSR; 00263 if ( ownerPermissions() & ACL_EXECUTE ) perms |= S_IXUSR; 00264 if ( owningGroupPermissions() & ACL_READ ) perms |= S_IRGRP; 00265 if ( owningGroupPermissions() & ACL_WRITE ) perms |= S_IWGRP; 00266 if ( owningGroupPermissions() & ACL_EXECUTE ) perms |= S_IXGRP; 00267 if ( othersPermissions() & ACL_READ ) perms |= S_IROTH; 00268 if ( othersPermissions() & ACL_WRITE ) perms |= S_IWOTH; 00269 if ( othersPermissions() & ACL_EXECUTE ) perms |= S_IXOTH; 00270 #endif 00271 return perms; 00272 } 00273 00274 unsigned short KACL::maskPermissions( bool &exists ) const 00275 { 00276 exists = true; 00277 #ifdef USE_POSIX_ACL 00278 acl_entry_t entry = entryForTag( d->m_acl, ACL_MASK ); 00279 if ( entry == 0 ) { 00280 exists = false; 00281 return 0; 00282 } 00283 return entryToPermissions( entry ); 00284 #else 00285 return 0; 00286 #endif 00287 } 00288 00289 #ifdef USE_POSIX_ACL 00290 bool KACL::KACLPrivate::setMaskPermissions( unsigned short v ) 00291 { 00292 acl_entry_t entry = entryForTag( m_acl, ACL_MASK ); 00293 if ( entry == 0 ) { 00294 acl_create_entry( &m_acl, &entry ); 00295 acl_set_tag_type( entry, ACL_MASK ); 00296 } 00297 permissionsToEntry( entry, v ); 00298 return true; 00299 } 00300 #endif 00301 00302 bool KACL::setMaskPermissions( unsigned short v ) 00303 { 00304 #ifdef USE_POSIX_ACL 00305 return d->setMaskPermissions( v ); 00306 #else 00307 Q_UNUSED( v ); 00308 return true; 00309 #endif 00310 } 00311 00312 /************************** 00313 * Deal with named users * 00314 **************************/ 00315 unsigned short KACL::namedUserPermissions( const TQString& name, bool *exists ) const 00316 { 00317 #ifdef USE_POSIX_ACL 00318 acl_entry_t entry; 00319 uid_t id; 00320 *exists = false; 00321 int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); 00322 while ( ret == 1 ) { 00323 acl_tag_t currentTag; 00324 acl_get_tag_type( entry, ¤tTag ); 00325 if ( currentTag == ACL_USER ) { 00326 id = *( (uid_t*) acl_get_qualifier( entry ) ); 00327 if ( d->getUserName( id ) == name ) { 00328 *exists = true; 00329 return entryToPermissions( entry ); 00330 } 00331 } 00332 ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); 00333 } 00334 #else 00335 Q_UNUSED( name ); 00336 Q_UNUSED( exists ); 00337 #endif 00338 return 0; 00339 } 00340 00341 #ifdef USE_POSIX_ACL 00342 bool KACL::KACLPrivate::setNamedUserOrGroupPermissions( const TQString& name, unsigned short permissions, acl_tag_t type ) 00343 { 00344 bool allIsWell = true; 00345 acl_t newACL = acl_dup( m_acl ); 00346 acl_entry_t entry; 00347 bool createdNewEntry = false; 00348 bool found = false; 00349 int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry ); 00350 while ( ret == 1 ) { 00351 acl_tag_t currentTag; 00352 acl_get_tag_type( entry, ¤tTag ); 00353 if ( currentTag == type ) { 00354 int id = * (int*)acl_get_qualifier( entry ); 00355 const TQString entryName = type == ACL_USER? getUserName( id ): getGroupName( id ); 00356 if ( entryName == name ) { 00357 // found him, update 00358 permissionsToEntry( entry, permissions ); 00359 found = true; 00360 break; 00361 } 00362 } 00363 ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry ); 00364 } 00365 if ( !found ) { 00366 acl_create_entry( &newACL, &entry ); 00367 acl_set_tag_type( entry, type ); 00368 int id = type == ACL_USER? getUidForName( name ): getGidForName( name ); 00369 if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) { 00370 acl_delete_entry( newACL, entry ); 00371 allIsWell = false; 00372 } else { 00373 permissionsToEntry( entry, permissions ); 00374 createdNewEntry = true; 00375 } 00376 } 00377 if ( allIsWell && createdNewEntry ) { 00378 // 23.1.1 of 1003.1e states that as soon as there is a named user or 00379 // named group entry, there needs to be a mask entry as well, so add 00380 // one, if the user hasn't explicitely set one. 00381 if ( entryForTag( newACL, ACL_MASK ) == 0 ) { 00382 acl_calc_mask( &newACL ); 00383 } 00384 } 00385 00386 if ( !allIsWell || acl_valid( newACL ) != 0 ) { 00387 acl_free( newACL ); 00388 allIsWell = false; 00389 } else { 00390 acl_free( m_acl ); 00391 m_acl = newACL; 00392 } 00393 return allIsWell; 00394 } 00395 #endif 00396 00397 bool KACL::setNamedUserPermissions( const TQString& name, unsigned short permissions ) 00398 { 00399 #ifdef USE_POSIX_ACL 00400 return d->setNamedUserOrGroupPermissions( name, permissions, ACL_USER ); 00401 #else 00402 Q_UNUSED( name ); 00403 Q_UNUSED( permissions ); 00404 return true; 00405 #endif 00406 } 00407 00408 ACLUserPermissionsList KACL::allUserPermissions() const 00409 { 00410 ACLUserPermissionsList list; 00411 #ifdef USE_POSIX_ACL 00412 acl_entry_t entry; 00413 uid_t id; 00414 int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); 00415 while ( ret == 1 ) { 00416 acl_tag_t currentTag; 00417 acl_get_tag_type( entry, ¤tTag ); 00418 if ( currentTag == ACL_USER ) { 00419 id = *( (uid_t*) acl_get_qualifier( entry ) ); 00420 TQString name = d->getUserName( id ); 00421 unsigned short permissions = entryToPermissions( entry ); 00422 ACLUserPermissions pair = qMakePair( name, permissions ); 00423 list.append( pair ); 00424 } 00425 ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); 00426 } 00427 #endif 00428 return list; 00429 } 00430 00431 #ifdef USE_POSIX_ACL 00432 bool KACL::KACLPrivate::setAllUsersOrGroups( const TQValueList< QPair<TQString, unsigned short> > &list, acl_tag_t type ) 00433 { 00434 bool allIsWell = true; 00435 bool atLeastOneUserOrGroup = false; 00436 00437 // make working copy, in case something goes wrong 00438 acl_t newACL = acl_dup( m_acl ); 00439 acl_entry_t entry; 00440 00441 //printACL( newACL, "Before cleaning: " ); 00442 // clear user entries 00443 int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry ); 00444 while ( ret == 1 ) { 00445 acl_tag_t currentTag; 00446 acl_get_tag_type( entry, ¤tTag ); 00447 if ( currentTag == type ) { 00448 acl_delete_entry( newACL, entry ); 00449 // we have to start from the beginning, the iterator is 00450 // invalidated, on deletion 00451 ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry ); 00452 } else { 00453 ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry ); 00454 } 00455 } 00456 //printACL( newACL, "After cleaning out entries: " ); 00457 00458 // now add the entries from the list 00459 TQValueList< QPair<TQString, unsigned short> >::const_iterator it = list.constBegin(); 00460 while ( it != list.constEnd() ) { 00461 acl_create_entry( &newACL, &entry ); 00462 acl_set_tag_type( entry, type ); 00463 int id = type == ACL_USER? getUidForName( (*it).first):getGidForName( (*it).first ); 00464 if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) { 00465 // user or group doesn't exist => error 00466 acl_delete_entry( newACL, entry ); 00467 allIsWell = false; 00468 break; 00469 } else { 00470 permissionsToEntry( entry, (*it).second ); 00471 atLeastOneUserOrGroup = true; 00472 } 00473 ++it; 00474 } 00475 //printACL( newACL, "After adding entries: " ); 00476 if ( allIsWell && atLeastOneUserOrGroup ) { 00477 // 23.1.1 of 1003.1e states that as soon as there is a named user or 00478 // named group entry, there needs to be a mask entry as well, so add 00479 // one, if the user hasn't explicitely set one. 00480 if ( entryForTag( newACL, ACL_MASK ) == 0 ) { 00481 acl_calc_mask( &newACL ); 00482 } 00483 } 00484 if ( allIsWell && ( acl_valid( newACL ) == 0 ) ) { 00485 acl_free( m_acl ); 00486 m_acl = newACL; 00487 } else { 00488 acl_free( newACL ); 00489 } 00490 return allIsWell; 00491 } 00492 #endif 00493 00494 bool KACL::setAllUserPermissions( const ACLUserPermissionsList &users ) 00495 { 00496 #ifdef USE_POSIX_ACL 00497 return d->setAllUsersOrGroups( users, ACL_USER ); 00498 #else 00499 Q_UNUSED( users ); 00500 return true; 00501 #endif 00502 } 00503 00504 00505 /************************** 00506 * Deal with named groups * 00507 **************************/ 00508 00509 unsigned short KACL::namedGroupPermissions( const TQString& name, bool *exists ) const 00510 { 00511 *exists = false; 00512 #ifdef USE_POSIX_ACL 00513 acl_entry_t entry; 00514 gid_t id; 00515 int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); 00516 while ( ret == 1 ) { 00517 acl_tag_t currentTag; 00518 acl_get_tag_type( entry, ¤tTag ); 00519 if ( currentTag == ACL_GROUP ) { 00520 id = *( (gid_t*) acl_get_qualifier( entry ) ); 00521 if ( d->getGroupName( id ) == name ) { 00522 *exists = true; 00523 return entryToPermissions( entry ); 00524 } 00525 } 00526 ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); 00527 } 00528 #else 00529 Q_UNUSED( name ); 00530 #endif 00531 return 0; 00532 } 00533 00534 bool KACL::setNamedGroupPermissions( const TQString& name, unsigned short permissions ) 00535 { 00536 #ifdef USE_POSIX_ACL 00537 return d->setNamedUserOrGroupPermissions( name, permissions, ACL_GROUP ); 00538 #else 00539 Q_UNUSED( name ); 00540 Q_UNUSED( permissions ); 00541 return true; 00542 #endif 00543 } 00544 00545 00546 ACLGroupPermissionsList KACL::allGroupPermissions() const 00547 { 00548 ACLGroupPermissionsList list; 00549 #ifdef USE_POSIX_ACL 00550 acl_entry_t entry; 00551 gid_t id; 00552 int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); 00553 while ( ret == 1 ) { 00554 acl_tag_t currentTag; 00555 acl_get_tag_type( entry, ¤tTag ); 00556 if ( currentTag == ACL_GROUP ) { 00557 id = *( (gid_t*) acl_get_qualifier( entry ) ); 00558 TQString name = d->getGroupName( id ); 00559 unsigned short permissions = entryToPermissions( entry ); 00560 ACLGroupPermissions pair = qMakePair( name, permissions ); 00561 list.append( pair ); 00562 } 00563 ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); 00564 } 00565 #endif 00566 return list; 00567 } 00568 00569 bool KACL::setAllGroupPermissions( const ACLGroupPermissionsList &groups ) 00570 { 00571 #ifdef USE_POSIX_ACL 00572 return d->setAllUsersOrGroups( groups, ACL_GROUP ); 00573 #else 00574 Q_UNUSED( groups ); 00575 return true; 00576 #endif 00577 } 00578 00579 /************************** 00580 * from and to string * 00581 **************************/ 00582 00583 bool KACL::setACL( const TQString &aclStr ) 00584 { 00585 bool ret = false; 00586 #ifdef USE_POSIX_ACL 00587 if ( aclStr.isEmpty() ) 00588 return false; 00589 00590 acl_t temp = acl_from_text( aclStr.latin1() ); 00591 if ( acl_valid( temp ) != 0 ) { 00592 // TODO errno is set, what to do with it here? 00593 acl_free( temp ); 00594 } else { 00595 if ( d->m_acl ) 00596 acl_free( d->m_acl ); 00597 d->m_acl = temp; 00598 ret = true; 00599 } 00600 #else 00601 Q_UNUSED( aclStr ); 00602 #endif 00603 return ret; 00604 } 00605 00606 TQString KACL::asString() const 00607 { 00608 #ifdef USE_POSIX_ACL 00609 return aclAsString( d->m_acl ); 00610 #else 00611 return TQString::null; 00612 #endif 00613 } 00614 00615 00616 // helpers 00617 00618 #ifdef USE_POSIX_ACL 00619 TQString KACL::KACLPrivate::getUserName( uid_t uid ) const 00620 { 00621 TQString *temp; 00622 temp = m_usercache.find( uid ); 00623 if ( !temp ) { 00624 struct passwd *user = getpwuid( uid ); 00625 if ( user ) { 00626 m_usercache.insert( uid, new TQString(TQString::fromLatin1(user->pw_name)) ); 00627 return TQString::fromLatin1( user->pw_name ); 00628 } 00629 else 00630 return TQString::number( uid ); 00631 } 00632 else 00633 return *temp; 00634 } 00635 00636 00637 TQString KACL::KACLPrivate::getGroupName( gid_t gid ) const 00638 { 00639 TQString *temp; 00640 temp = m_groupcache.find( gid ); 00641 if ( !temp ) { 00642 struct group *grp = getgrgid( gid ); 00643 if ( grp ) { 00644 m_groupcache.insert( gid, new TQString(TQString::fromLatin1(grp->gr_name)) ); 00645 return TQString::fromLatin1( grp->gr_name ); 00646 } 00647 else 00648 return TQString::number( gid ); 00649 } 00650 else 00651 return *temp; 00652 } 00653 00654 static TQString aclAsString(const acl_t acl) 00655 { 00656 char *aclString = acl_to_text( acl, 0 ); 00657 TQString ret = TQString::fromLatin1( aclString ); 00658 acl_free( (void*)aclString ); 00659 return ret; 00660 } 00661 00662 00663 #endif 00664 00665 void KACL::virtual_hook( int, void* ) 00666 { /*BASE::virtual_hook( id, data );*/ } 00667 00668 TQDataStream & operator<< ( TQDataStream & s, const KACL & a ) 00669 { 00670 s << a.asString(); 00671 return s; 00672 } 00673 00674 TQDataStream & operator>> ( TQDataStream & s, KACL & a ) 00675 { 00676 TQString str; 00677 s >> str; 00678 a.setACL( str ); 00679 return s; 00680 } 00681 00682 // vim:set ts=8 sw=4: