• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KLDAP Library

ldapoperation.cpp

00001 /*
00002   This file is part of libkldap.
00003   Copyright (c) 2004-2006 Szombathelyi György <gyurco@freemail.hu>
00004 
00005   This library is free software; you can redistribute it and/or
00006   modify it under the terms of the GNU Library General  Public
00007   License as published by the Free Software Foundation; either
00008   version 2 of the License, or (at your option) any later version.
00009 
00010   This library is distributed in the hope that it will be useful,
00011   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013   Library General Public License for more details.
00014 
00015   You should have received a copy of the GNU Library General Public License
00016   along with this library; see the file COPYING.LIB.  If not, write to
00017   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018   Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "ldapoperation.h"
00022 #include "kldap_config.h"
00023 
00024 #include <kdebug.h>
00025 
00026 #include <QtCore/QTime>
00027 
00028 #include <stdlib.h>
00029 #ifdef HAVE_SYS_TIME_H
00030 #include <sys/time.h>
00031 #endif
00032 
00033 #ifdef SASL2_FOUND
00034 #include <sasl/sasl.h>
00035 #endif
00036 
00037 #ifdef LDAP_FOUND
00038 #include <lber.h>
00039 #include <ldap.h>
00040 #endif
00041 
00042 #include "ldapdefs.h"
00043 
00044 using namespace KLDAP;
00045 
00046 #ifdef LDAP_FOUND
00047 static void extractControls( LdapControls &ctrls, LDAPControl **pctrls );
00048 #endif // LDAP_FOUND
00049 
00050 /*
00051    Returns the difference between msecs and elapsed. If msecs is -1,
00052    however, -1 is returned.
00053 */
00054 static int kldap_timeout_value( int msecs, int elapsed )
00055 {
00056   if ( msecs == -1 ) {
00057     return -1;
00058   }
00059 
00060   int timeout = msecs - elapsed;
00061   return timeout < 0 ? 0 : timeout;
00062 }
00063 
00064 class LdapOperation::LdapOperationPrivate
00065 {
00066   public:
00067     LdapOperationPrivate();
00068     ~LdapOperationPrivate();
00069 #ifdef LDAP_FOUND
00070     int processResult( int rescode, LDAPMessage *msg );
00071     int bind( const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data, bool async );
00072 #endif
00073     LdapControls mClientCtrls, mServerCtrls, mControls;
00074     LdapObject mObject;
00075     QByteArray mExtOid, mExtData;
00076     QByteArray mServerCred;
00077     QString mMatchedDn;
00078     QList<QByteArray> mReferrals;
00079 
00080   LdapConnection *mConnection;
00081   };
00082 
00083 LdapOperation::LdapOperation()
00084   : d( new LdapOperationPrivate )
00085 {
00086   d->mConnection = 0;
00087 }
00088 
00089 LdapOperation::LdapOperation( LdapConnection &conn )
00090   : d( new LdapOperationPrivate )
00091 {
00092   setConnection( conn );
00093 }
00094 
00095 LdapOperation::~LdapOperation()
00096 {
00097   delete d;
00098 }
00099 
00100 void LdapOperation::setConnection( LdapConnection &conn )
00101 {
00102   d->mConnection = &conn;
00103 }
00104 
00105 LdapConnection &LdapOperation::connection()
00106 {
00107   return *d->mConnection;
00108 }
00109 
00110 void LdapOperation::setClientControls( const LdapControls &ctrls )
00111 {
00112   d->mClientCtrls = ctrls;
00113 }
00114 
00115 void LdapOperation::setServerControls( const LdapControls &ctrls )
00116 {
00117   d->mServerCtrls = ctrls;
00118 }
00119 
00120 LdapControls LdapOperation::clientControls() const
00121 {
00122   return d->mClientCtrls;
00123 }
00124 
00125 LdapControls LdapOperation::serverControls() const
00126 {
00127   return d->mServerCtrls;
00128 }
00129 
00130 LdapObject LdapOperation::object() const
00131 {
00132   return d->mObject;
00133 }
00134 
00135 LdapControls LdapOperation::controls() const
00136 {
00137   return d->mControls;
00138 }
00139 
00140 QByteArray LdapOperation::extendedOid() const
00141 {
00142   return d->mExtOid;
00143 }
00144 
00145 QByteArray LdapOperation::extendedData() const
00146 {
00147   return d->mExtData;
00148 }
00149 
00150 QString LdapOperation::matchedDn() const
00151 {
00152   return d->mMatchedDn;
00153 }
00154 
00155 QList<QByteArray> LdapOperation::referrals() const
00156 {
00157   return d->mReferrals;
00158 }
00159 
00160 QByteArray LdapOperation::serverCred() const
00161 {
00162   return d->mServerCred;
00163 }
00164 
00165 LdapOperation::LdapOperationPrivate::LdapOperationPrivate()
00166 {
00167 }
00168 
00169 LdapOperation::LdapOperationPrivate::~LdapOperationPrivate()
00170 {
00171 }
00172 
00173 #ifdef LDAP_FOUND
00174 
00175 #ifdef SASL2_FOUND
00176 static int kldap_sasl_interact( sasl_interact_t *interact, LdapOperation::SASL_Data *data )
00177 {
00178   if ( data->proc ) {
00179     for ( ; interact->id != SASL_CB_LIST_END; interact++ ) {
00180       switch ( interact->id ) {
00181       case SASL_CB_GETREALM:
00182         data->creds.fields |= LdapOperation::SASL_Realm;
00183         break;
00184       case SASL_CB_AUTHNAME:
00185         data->creds.fields |= LdapOperation::SASL_Authname;
00186         break;
00187       case SASL_CB_PASS:
00188         data->creds.fields |= LdapOperation::SASL_Password;
00189         break;
00190       case SASL_CB_USER:
00191         data->creds.fields |= LdapOperation::SASL_Authzid;
00192         break;
00193       }
00194     }
00195     int retval;
00196     if ( ( retval = data->proc( data->creds, data->data ) ) ) {
00197       return retval;
00198     }
00199   }
00200 
00201   QString value;
00202 
00203   while ( interact->id != SASL_CB_LIST_END ) {
00204     value.clear();
00205     switch( interact->id ) {
00206     case SASL_CB_GETREALM:
00207       value = data->creds.realm;
00208       kDebug() << "SASL_REALM=" << value;
00209       break;
00210     case SASL_CB_AUTHNAME:
00211       value = data->creds.authname;
00212       kDebug() << "SASL_AUTHNAME=" << value;
00213       break;
00214     case SASL_CB_PASS:
00215       value = data->creds.password;
00216       kDebug() << "SASL_PASSWD=[hidden]";
00217       break;
00218     case SASL_CB_USER:
00219       value = data->creds.authzid;
00220       kDebug() << "SASL_AUTHZID=" << value;
00221       break;
00222     }
00223     if ( value.isEmpty() ) {
00224       interact->result = NULL;
00225       interact->len = 0;
00226     } else {
00227       interact->result = strdup( value.toUtf8() );
00228       interact->len = strlen( (const char *)interact->result );
00229     }
00230     interact++;
00231   }
00232   return KLDAP_SUCCESS;
00233 }
00234 #endif
00235 
00236 int LdapOperation::LdapOperationPrivate::bind( const QByteArray &creds,
00237                                                SASL_Callback_Proc *saslproc,
00238                                                void *data, bool async )
00239 {
00240   Q_ASSERT( mConnection );
00241   LDAP *ld = (LDAP*) mConnection->handle();
00242   LdapServer server;
00243   server = mConnection->server();
00244 
00245   int ret;
00246 
00247   if ( server.auth() == LdapServer::SASL ) {
00248 #ifdef SASL2_FOUND
00249     sasl_conn_t *saslconn = (sasl_conn_t *)mConnection->saslHandle();
00250     sasl_interact_t *client_interact = NULL;
00251     const char *out = NULL;
00252     uint outlen;
00253     const char *mechusing = NULL;
00254     struct berval ccred, *scred;
00255     int saslresult;
00256     QByteArray sdata = creds;
00257 
00258     QString mech = server.mech();
00259     if ( mech.isEmpty() ) {
00260       mech = "DIGEST-MD5";
00261     }
00262 
00263     SASL_Data sasldata;
00264     sasldata.proc = saslproc;
00265     sasldata.data = data;
00266     sasldata.creds.fields = 0;
00267     sasldata.creds.realm = server.realm();
00268     sasldata.creds.authname = server.user();
00269     sasldata.creds.authzid = server.bindDn();
00270     sasldata.creds.password = server.password();
00271 
00272     do {
00273       if ( sdata.isEmpty() ) {
00274         do {
00275           saslresult = sasl_client_start( saslconn, mech.toLatin1(),
00276                                           &client_interact, &out, &outlen, &mechusing );
00277 
00278           if ( saslresult == SASL_INTERACT ) {
00279             if ( kldap_sasl_interact( client_interact, &sasldata ) != KLDAP_SUCCESS ) {
00280               return KLDAP_SASL_ERROR;
00281             }
00282           }
00283           kDebug() << "sasl_client_start mech: "
00284                    << mechusing << " outlen " << outlen
00285                    << " result: " << saslresult;
00286         } while ( saslresult == SASL_INTERACT );
00287         if ( saslresult != SASL_CONTINUE && saslresult != SASL_OK ) {
00288           return KLDAP_SASL_ERROR;
00289         }
00290 
00291       } else {
00292         kDebug() << "sasl_client_step";
00293         do {
00294           saslresult = sasl_client_step( saslconn, sdata.data(), sdata.size(),
00295                                          &client_interact, &out, &outlen );
00296           if ( saslresult == SASL_INTERACT ) {
00297             if ( kldap_sasl_interact( client_interact, &sasldata ) != KLDAP_SUCCESS ) {
00298               return KLDAP_SASL_ERROR;
00299             }
00300           }
00301         } while ( saslresult == SASL_INTERACT );
00302         kDebug() << "sasl_client_step result" << saslresult;
00303         if ( saslresult != SASL_CONTINUE && saslresult != SASL_OK ) {
00304           return KLDAP_SASL_ERROR;
00305         }
00306       }
00307 
00308       ccred.bv_val = (char*) out;
00309       ccred.bv_len = outlen;
00310 
00311       if ( async ) {
00312         kDebug() << "ldap_sasl_bind";
00313         int msgid;
00314         ret =
00315           ldap_sasl_bind( ld, server.bindDn().toUtf8().data(), mech.toLatin1(),
00316                           &ccred, 0, 0, &msgid );
00317         if ( ret == 0 ) {
00318           ret = msgid;
00319         }
00320         kDebug() << "ldap_sasl_bind msgid" << ret;
00321       } else {
00322         kDebug() << "ldap_sasl_bind_s";
00323         ret =
00324           ldap_sasl_bind_s( ld, server.bindDn().toUtf8().data(), mech.toLatin1(),
00325                             &ccred, 0, 0, &scred );
00326         kDebug() << "ldap_sasl_bind_s ret" << ret;
00327         if ( scred ) {
00328           sdata = QByteArray( scred->bv_val, scred->bv_len );
00329         } else {
00330           sdata = QByteArray();
00331         }
00332       }
00333     } while ( !async && ret == KLDAP_SASL_BIND_IN_PROGRESS );
00334 #else
00335     kError() << "SASL authentication is not available "
00336              << "(re-compile kldap with cyrus-sasl development).";
00337     return KLDAP_SASL_ERROR;
00338 #endif
00339   } else { //simple auth
00340     QByteArray bindname, pass;
00341     struct berval ccred;
00342     if ( server.auth() == LdapServer::Simple ) {
00343       bindname = server.bindDn().toUtf8();
00344       pass = server.password().toUtf8();
00345     }
00346     ccred.bv_val = pass.data();
00347     ccred.bv_len = pass.size();
00348     kDebug() << "binding to server, bindname: " << bindname << " password: *****";
00349 
00350     if ( async ) {
00351       kDebug() << "ldap_sasl_bind (simple)";
00352       int msgid;
00353       ret = ldap_sasl_bind( ld, bindname.data(), 0, &ccred, 0, 0, &msgid );
00354 //    ret = ldap_simple_bind( ld, bindname.data(),pass.data() );
00355       if ( ret == 0 ) {
00356         ret = msgid;
00357       }
00358     } else {
00359       kDebug() << "ldap_sasl_bind_s (simple)";
00360       ret = ldap_sasl_bind_s( ld, bindname.data(), 0, &ccred, 0, 0, 0 );
00361 //    ret = ldap_simple_bind_s( ld, bindname.data(), pass.data() );
00362     }
00363   }
00364   return ret;
00365 }
00366 
00367 int LdapOperation::LdapOperationPrivate::processResult( int rescode, LDAPMessage *msg )
00368 {
00369   //kDebug();
00370   int retval;
00371   LDAP *ld = (LDAP*) mConnection->handle();
00372 
00373   kDebug() << "rescode: " << rescode;
00374   switch ( rescode ) {
00375   case RES_SEARCH_ENTRY:
00376   {
00377     //kDebug() << "Found search entry";
00378     mObject.clear();
00379     LdapAttrMap attrs;
00380     char *name;
00381     struct berval **bvals;
00382     BerElement     *entry;
00383 
00384     char *dn = ldap_get_dn( ld, msg );
00385     mObject.setDn( QString::fromUtf8( dn ) );
00386     ldap_memfree( dn );
00387 
00388     // iterate over the attributes
00389     name = ldap_first_attribute( ld, msg, &entry );
00390     while ( name != 0 ) {
00391       // print the values
00392       bvals = ldap_get_values_len( ld, msg, name );
00393       LdapAttrValue values;
00394       if ( bvals ) {
00395         for ( int i = 0; bvals[i] != 0; i++ ) {
00396           char *val = bvals[i]->bv_val;
00397           unsigned long len = bvals[i]->bv_len;
00398           values.append( QByteArray( val, len ) );
00399         }
00400         ldap_value_free_len( bvals );
00401       }
00402       attrs[ QString::fromLatin1( name ) ] = values;
00403       ldap_memfree( name );
00404 
00405       // next attribute
00406       name = ldap_next_attribute( ld, msg, entry );
00407     }
00408     ber_free( entry, 0 );
00409     mObject.setAttributes( attrs );
00410     break;
00411   }
00412   case RES_EXTENDED:
00413   {
00414     char *retoid;
00415     struct berval *retdata;
00416     retval = ldap_parse_extended_result( ld, msg, &retoid, &retdata, 0 );
00417     if ( retval != KLDAP_SUCCESS ) {
00418       ldap_msgfree( msg );
00419       return -1;
00420     }
00421     mExtOid = retoid ? QByteArray( retoid ) : QByteArray();
00422     mExtData = retdata ? QByteArray( retdata->bv_val, retdata->bv_len ) : QByteArray();
00423     ldap_memfree( retoid );
00424     ber_bvfree( retdata );
00425     break;
00426   }
00427   case RES_BIND:
00428   {
00429     struct berval *servercred;
00430     retval = ldap_parse_sasl_bind_result( ld, msg, &servercred, 0 );
00431     if ( retval != KLDAP_SUCCESS && retval != KLDAP_SASL_BIND_IN_PROGRESS ) {
00432       kDebug() << "RES_BIND error: " << retval;
00433       ldap_msgfree( msg );
00434       return -1;
00435     }
00436     kDebug() << "RES_BIND rescode" << rescode << "retval:" << retval;
00437     mServerCred = servercred ? QByteArray( servercred->bv_val, servercred->bv_len ) : QByteArray();
00438     ber_bvfree( servercred );
00439     break;
00440   }
00441   default:
00442   {
00443     LDAPControl **serverctrls = 0;
00444     char *matcheddn = 0, *errmsg = 0;
00445     char **referralsp;
00446     int errcodep;
00447     retval =
00448       ldap_parse_result( ld, msg, &errcodep, &matcheddn, &errmsg, &referralsp,
00449                          &serverctrls, 0 );
00450     kDebug() << "rescode" << rescode << "retval:" << retval
00451                  << "matcheddn:" << matcheddn << "errcode:"
00452                  << errcodep << "errmsg:" << errmsg;
00453     if ( retval != KLDAP_SUCCESS ) {
00454       ldap_msgfree( msg );
00455       return -1;
00456     }
00457     mControls.clear();
00458     if ( serverctrls ) {
00459       extractControls( mControls, serverctrls );
00460       ldap_controls_free( serverctrls );
00461     }
00462     mReferrals.clear();
00463     if ( referralsp ) {
00464       char **tmp = referralsp;
00465       while ( *tmp ) {
00466         mReferrals.append( QByteArray( *tmp ) );
00467         ldap_memfree( *tmp );
00468         tmp++;
00469       }
00470       ldap_memfree( (char *) referralsp );
00471     }
00472     mMatchedDn.clear();
00473     if ( matcheddn ) {
00474       mMatchedDn = QString::fromUtf8( matcheddn );
00475       ldap_memfree( matcheddn );
00476     }
00477     if ( errmsg ) {
00478       ldap_memfree( errmsg );
00479     }
00480   }
00481   }
00482 
00483   ldap_msgfree( msg );
00484 
00485   return rescode;
00486 }
00487 
00488 static void addModOp( LDAPMod ***pmods, int mod_type, const QString &attr,
00489                       const QByteArray *value = 0 )
00490 {
00491   //  kDebug() << "type:" << mod_type << "attr:" << attr <<
00492   //    "value:" << QString::fromUtf8(value,value.size()) <<
00493   //    "size:" << value.size();
00494   LDAPMod **mods;
00495 
00496   mods = *pmods;
00497 
00498   uint i = 0;
00499 
00500   if ( mods == 0 ) {
00501     mods = (LDAPMod **)malloc( 2 * sizeof( LDAPMod * ) );
00502     mods[ 0 ] = (LDAPMod *)malloc( sizeof( LDAPMod ) );
00503     mods[ 1 ] = 0;
00504     memset( mods[ 0 ], 0, sizeof( LDAPMod ) );
00505   } else {
00506     while ( mods[ i ] != 0 &&
00507             ( strcmp( attr.toUtf8(), mods[i]->mod_type ) != 0 ||
00508               ( mods[ i ]->mod_op & ~LDAP_MOD_BVALUES ) != mod_type ) ) i++;
00509 
00510     if ( mods[ i ] == 0 ) {
00511       mods = (LDAPMod **)realloc( mods, ( i + 2 ) * sizeof( LDAPMod * ) );
00512       if ( mods == 0 ) {
00513         kError() << "addModOp: realloc";
00514         return;
00515       }
00516       mods[ i + 1 ] = 0;
00517       mods[ i ] = (LDAPMod *) malloc( sizeof( LDAPMod ) );
00518       memset( mods[ i ], 0, sizeof( LDAPMod ) );
00519     }
00520   }
00521 
00522   mods[ i ]->mod_op = mod_type | LDAP_MOD_BVALUES;
00523   if ( mods[ i ]->mod_type == 0 ) {
00524     mods[ i ]->mod_type = strdup( attr.toUtf8() );
00525   }
00526 
00527   *pmods = mods;
00528 
00529   if ( value == 0 ) {
00530     return;
00531   }
00532 
00533   int vallen = value->size();
00534   BerValue *berval;
00535   berval = (BerValue *) malloc( sizeof( BerValue ) );
00536   berval -> bv_len = vallen;
00537   if ( vallen > 0 ) {
00538     berval -> bv_val = (char *) malloc( vallen );
00539     memcpy( berval -> bv_val, value->data(), vallen );
00540   } else {
00541     berval -> bv_val = 0;
00542   }
00543 
00544   if ( mods[ i ] -> mod_vals.modv_bvals == 0 ) {
00545     mods[ i ]->mod_vals.modv_bvals =
00546       (BerValue **) malloc( sizeof( BerValue * ) * 2 );
00547     mods[ i ]->mod_vals.modv_bvals[ 0 ] = berval;
00548     mods[ i ]->mod_vals.modv_bvals[ 1 ] = 0;
00549 //    kDebug() << "new bervalue struct" << attr << value;
00550   } else {
00551     uint j = 0;
00552     while ( mods[ i ]->mod_vals.modv_bvals[ j ] != 0 ) {
00553       j++;
00554     }
00555     mods[ i ]->mod_vals.modv_bvals =
00556       (BerValue **)realloc( mods[ i ]->mod_vals.modv_bvals,
00557                             ( j + 2 ) * sizeof( BerValue * ) );
00558     if ( mods[ i ]->mod_vals.modv_bvals == 0 ) {
00559       kError() << "addModOp: realloc";
00560       free( berval );
00561       return;
00562     }
00563     mods[ i ]->mod_vals.modv_bvals[ j ] = berval;
00564     mods[ i ]->mod_vals.modv_bvals[ j+1 ] = 0;
00565     kDebug() << j << ". new bervalue";
00566   }
00567 }
00568 
00569 static void addControlOp( LDAPControl ***pctrls, const QString &oid,
00570                           const QByteArray &value, bool critical )
00571 {
00572   LDAPControl **ctrls;
00573   LDAPControl *ctrl = (LDAPControl *) malloc( sizeof( LDAPControl ) );
00574 
00575   ctrls = *pctrls;
00576 
00577   kDebug() << "oid:'" << oid << "' val: '" << value << "'";
00578   int vallen = value.size();
00579   ctrl->ldctl_value.bv_len = vallen;
00580   if ( vallen ) {
00581     ctrl->ldctl_value.bv_val = (char *) malloc( vallen );
00582     memcpy( ctrl->ldctl_value.bv_val, value.data(), vallen );
00583   } else {
00584     ctrl->ldctl_value.bv_val = 0;
00585   }
00586   ctrl->ldctl_iscritical = critical;
00587   ctrl->ldctl_oid = strdup( oid.toUtf8() );
00588 
00589   uint i = 0;
00590 
00591   if ( ctrls == 0 ) {
00592     ctrls = (LDAPControl **)malloc ( 2 * sizeof( LDAPControl * ) );
00593     ctrls[ 0 ] = 0;
00594     ctrls[ 1 ] = 0;
00595   } else {
00596     while ( ctrls[ i ] != 0 ) {
00597       i++;
00598     }
00599     ctrls[ i + 1 ] = 0;
00600     ctrls =
00601       (LDAPControl **)realloc( ctrls, ( i + 2 ) * sizeof( LDAPControl * ) );
00602   }
00603   ctrls[ i ] = ctrl;
00604   *pctrls = ctrls;
00605 }
00606 
00607 static void createControls( LDAPControl ***pctrls, const LdapControls &ctrls )
00608 {
00609   for ( int i = 0; i< ctrls.count(); ++i ) {
00610     addControlOp( pctrls, ctrls[i].oid(), ctrls[i].value(), ctrls[i].critical() );
00611   }
00612 }
00613 
00614 static void extractControls( LdapControls &ctrls, LDAPControl **pctrls )
00615 {
00616   LDAPControl *ctrl;
00617   LdapControl control;
00618   int i = 0;
00619 
00620   while ( pctrls[i] ) {
00621     ctrl = pctrls[ i ];
00622     control.setOid( QString::fromUtf8( ctrl->ldctl_oid ) );
00623     control.setValue( QByteArray( ctrl->ldctl_value.bv_val,
00624                                   ctrl->ldctl_value.bv_len ) );
00625     control.setCritical( ctrl->ldctl_iscritical );
00626     ctrls.append( control );
00627     i++;
00628   }
00629 }
00630 
00631 int LdapOperation::bind( const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data )
00632 {
00633   return d->bind( creds, saslproc, data, true );
00634 }
00635 
00636 int LdapOperation::bind_s( SASL_Callback_Proc *saslproc, void *data )
00637 {
00638   return d->bind( QByteArray(), saslproc, data, false );
00639 }
00640 
00641 int LdapOperation::search( const LdapDN &base, LdapUrl::Scope scope,
00642                            const QString &filter, const QStringList &attributes )
00643 {
00644   Q_ASSERT( d->mConnection );
00645   LDAP *ld = (LDAP*) d->mConnection->handle();
00646 
00647   char **attrs = 0;
00648   int msgid;
00649 
00650   LDAPControl **serverctrls = 0, **clientctrls = 0;
00651   createControls( &serverctrls, d->mServerCtrls );
00652   createControls( &serverctrls, d->mClientCtrls );
00653 
00654   int count = attributes.count();
00655   if ( count > 0 ) {
00656     attrs = static_cast<char**>( malloc( ( count + 1 ) * sizeof( char * ) ) );
00657     for ( int i=0; i<count; i++ ) {
00658       attrs[i] = strdup( attributes.at(i).toUtf8() );
00659     }
00660     attrs[count] = 0;
00661   }
00662 
00663   int lscope = LDAP_SCOPE_BASE;
00664   switch ( scope ) {
00665   case LdapUrl::Base:
00666     lscope = LDAP_SCOPE_BASE;
00667     break;
00668   case LdapUrl::One:
00669     lscope = LDAP_SCOPE_ONELEVEL;
00670     break;
00671   case LdapUrl::Sub:
00672     lscope = LDAP_SCOPE_SUBTREE;
00673     break;
00674   }
00675 
00676   kDebug() << "asyncSearch() base=\"" << base.toString()
00677            << "\" scope=" << scope
00678            << "filter=\"" << filter
00679            << "\" attrs=" << attributes;
00680   int retval =
00681     ldap_search_ext( ld, base.toString().toUtf8().data(), lscope,
00682                      filter.isEmpty() ? QByteArray( "objectClass=*" ).data() :
00683                                         filter.toUtf8().data(),
00684                      attrs, 0, serverctrls, clientctrls, 0,
00685                      d->mConnection->sizeLimit(), &msgid );
00686 
00687   ldap_controls_free( serverctrls );
00688   ldap_controls_free( clientctrls );
00689 
00690   // free the attributes list again
00691   if ( count > 0 ) {
00692     for ( int i=0; i<count; i++ ) {
00693       free( attrs[i] );
00694     }
00695     free( attrs );
00696   }
00697 
00698   if ( retval == 0 ) {
00699     retval = msgid;
00700   }
00701   return retval;
00702 }
00703 
00704 int LdapOperation::add( const LdapObject &object )
00705 {
00706   Q_ASSERT( d->mConnection );
00707   LDAP *ld = (LDAP*) d->mConnection->handle();
00708 
00709   int msgid;
00710   LDAPMod **lmod = 0;
00711 
00712   LDAPControl **serverctrls = 0, **clientctrls = 0;
00713   createControls( &serverctrls, d->mServerCtrls );
00714   createControls( &serverctrls, d->mClientCtrls );
00715 
00716   for ( LdapAttrMap::ConstIterator it = object.attributes().begin();
00717         it != object.attributes().end(); ++it ) {
00718     QString attr = it.key();
00719     for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) {
00720       addModOp( &lmod, 0, attr, &(*it2) );
00721     }
00722   }
00723 
00724   int retval =
00725     ldap_add_ext( ld, object.dn().toString().toUtf8().data(), lmod, serverctrls,
00726                   clientctrls, &msgid );
00727 
00728   ldap_controls_free( serverctrls );
00729   ldap_controls_free( clientctrls );
00730   ldap_mods_free( lmod, 1 );
00731   if ( retval == 0 ) {
00732     retval = msgid;
00733   }
00734   return retval;
00735 }
00736 
00737 int LdapOperation::add_s( const LdapObject &object )
00738 {
00739   Q_ASSERT( d->mConnection );
00740   LDAP *ld = (LDAP*) d->mConnection->handle();
00741 
00742   LDAPMod **lmod = 0;
00743 
00744   LDAPControl **serverctrls = 0, **clientctrls = 0;
00745   createControls( &serverctrls, d->mServerCtrls );
00746   createControls( &serverctrls, d->mClientCtrls );
00747 
00748   for ( LdapAttrMap::ConstIterator it = object.attributes().begin();
00749         it != object.attributes().end(); ++it ) {
00750     QString attr = it.key();
00751     for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) {
00752       addModOp( &lmod, 0, attr, &(*it2) );
00753     }
00754   }
00755 
00756   int retval =
00757     ldap_add_ext_s( ld, object.dn().toString().toUtf8().data(), lmod, serverctrls,
00758                     clientctrls );
00759 
00760   ldap_controls_free( serverctrls );
00761   ldap_controls_free( clientctrls );
00762   ldap_mods_free( lmod, 1 );
00763   return retval;
00764 }
00765 
00766 int LdapOperation::add( const LdapDN &dn, const ModOps &ops )
00767 {
00768   Q_ASSERT( d->mConnection );
00769   LDAP *ld = (LDAP*) d->mConnection->handle();
00770 
00771   int msgid;
00772   LDAPMod **lmod = 0;
00773 
00774   LDAPControl **serverctrls = 0, **clientctrls = 0;
00775   createControls( &serverctrls, d->mServerCtrls );
00776   createControls( &serverctrls, d->mClientCtrls );
00777 
00778   for ( int i = 0; i < ops.count(); ++i ) {
00779     for ( int j = 0; j < ops[i].values.count(); ++j ) {
00780       addModOp( &lmod, 0, ops[i].attr, &ops[i].values[j] );
00781     }
00782   }
00783 
00784   int retval =
00785     ldap_add_ext( ld, dn.toString().toUtf8().data(), lmod, serverctrls,
00786                   clientctrls, &msgid );
00787 
00788   ldap_controls_free( serverctrls );
00789   ldap_controls_free( clientctrls );
00790   ldap_mods_free( lmod, 1 );
00791   if ( retval == 0 ) {
00792     retval = msgid;
00793   }
00794   return retval;
00795 }
00796 
00797 int LdapOperation::add_s( const LdapDN &dn, const ModOps &ops )
00798 {
00799   Q_ASSERT( d->mConnection );
00800   LDAP *ld = (LDAP*) d->mConnection->handle();
00801 
00802   LDAPMod **lmod = 0;
00803 
00804   LDAPControl **serverctrls = 0, **clientctrls = 0;
00805   createControls( &serverctrls, d->mServerCtrls );
00806   createControls( &serverctrls, d->mClientCtrls );
00807 
00808   for ( int i = 0; i < ops.count(); ++i ) {
00809     for ( int j = 0; j < ops[i].values.count(); ++j ) {
00810       addModOp( &lmod, 0, ops[i].attr, &ops[i].values[j] );
00811     }
00812   }
00813   kDebug() << dn.toString();
00814   int retval =
00815     ldap_add_ext_s( ld, dn.toString().toUtf8().data(), lmod, serverctrls,
00816                     clientctrls );
00817 
00818   ldap_controls_free( serverctrls );
00819   ldap_controls_free( clientctrls );
00820   ldap_mods_free( lmod, 1 );
00821   return retval;
00822 }
00823 
00824 int LdapOperation::rename( const LdapDN &dn, const QString &newRdn,
00825                            const QString &newSuperior, bool deleteold )
00826 {
00827   Q_ASSERT( d->mConnection );
00828   LDAP *ld = (LDAP*) d->mConnection->handle();
00829 
00830   int msgid;
00831 
00832   LDAPControl **serverctrls = 0, **clientctrls = 0;
00833   createControls( &serverctrls, d->mServerCtrls );
00834   createControls( &serverctrls, d->mClientCtrls );
00835 
00836   int retval = ldap_rename( ld, dn.toString().toUtf8().data(), newRdn.toUtf8().data(),
00837                             newSuperior.isEmpty() ? (char *) 0 : newSuperior.toUtf8().data(),
00838                             deleteold, serverctrls, clientctrls, &msgid );
00839 
00840   ldap_controls_free( serverctrls );
00841   ldap_controls_free( clientctrls );
00842 
00843   if ( retval == 0 ) {
00844     retval = msgid;
00845   }
00846   return retval;
00847 }
00848 
00849 int LdapOperation::rename_s( const LdapDN &dn, const QString &newRdn,
00850                              const QString &newSuperior, bool deleteold )
00851 {
00852   Q_ASSERT( d->mConnection );
00853   LDAP *ld = (LDAP*) d->mConnection->handle();
00854 
00855   LDAPControl **serverctrls = 0, **clientctrls = 0;
00856   createControls( &serverctrls, d->mServerCtrls );
00857   createControls( &serverctrls, d->mClientCtrls );
00858 
00859   int retval = ldap_rename_s( ld, dn.toString().toUtf8().data(), newRdn.toUtf8().data(),
00860                               newSuperior.isEmpty() ? (char *) 0 : newSuperior.toUtf8().data(),
00861                               deleteold, serverctrls, clientctrls );
00862 
00863   ldap_controls_free( serverctrls );
00864   ldap_controls_free( clientctrls );
00865 
00866   return retval;
00867 }
00868 
00869 int LdapOperation::del( const LdapDN &dn )
00870 {
00871   Q_ASSERT( d->mConnection );
00872   LDAP *ld = (LDAP*) d->mConnection->handle();
00873 
00874   int msgid;
00875 
00876   LDAPControl **serverctrls = 0, **clientctrls = 0;
00877   createControls( &serverctrls, d->mServerCtrls );
00878   createControls( &serverctrls, d->mClientCtrls );
00879 
00880   int retval =
00881     ldap_delete_ext( ld, dn.toString().toUtf8().data(), serverctrls, clientctrls, &msgid );
00882 
00883   ldap_controls_free( serverctrls );
00884   ldap_controls_free( clientctrls );
00885 
00886   if ( retval == 0 ) {
00887     retval = msgid;
00888   }
00889   return retval;
00890 }
00891 
00892 int LdapOperation::del_s( const LdapDN &dn )
00893 {
00894   Q_ASSERT( d->mConnection );
00895   LDAP *ld = (LDAP*) d->mConnection->handle();
00896 
00897   LDAPControl **serverctrls = 0, **clientctrls = 0;
00898   createControls( &serverctrls, d->mServerCtrls );
00899   createControls( &serverctrls, d->mClientCtrls );
00900 
00901   int retval = ldap_delete_ext_s( ld, dn.toString().toUtf8().data(), serverctrls, clientctrls );
00902 
00903   ldap_controls_free( serverctrls );
00904   ldap_controls_free( clientctrls );
00905 
00906   return retval;
00907 }
00908 
00909 int LdapOperation::modify( const LdapDN &dn, const ModOps &ops )
00910 {
00911   Q_ASSERT( d->mConnection );
00912   LDAP *ld = (LDAP *)d->mConnection->handle();
00913 
00914   int msgid;
00915   LDAPMod **lmod = 0;
00916 
00917   LDAPControl **serverctrls = 0, **clientctrls = 0;
00918   createControls( &serverctrls, d->mServerCtrls );
00919   createControls( &serverctrls, d->mClientCtrls );
00920 
00921   for ( int i = 0; i < ops.count(); ++i ) {
00922     int mtype = 0;
00923     switch ( ops[i].type ) {
00924     case Mod_None:
00925       mtype = 0;
00926       break;
00927     case Mod_Add:
00928       mtype = LDAP_MOD_ADD;
00929       break;
00930     case Mod_Replace:
00931       mtype = LDAP_MOD_REPLACE;
00932       break;
00933     case Mod_Del:
00934       mtype = LDAP_MOD_DELETE;
00935       break;
00936     }
00937     addModOp( &lmod, mtype, ops[i].attr, 0 );
00938     for ( int j = 0; j < ops[i].values.count(); ++j ) {
00939       addModOp( &lmod, mtype, ops[i].attr, &ops[i].values[j] );
00940     }
00941   }
00942 
00943   int retval =
00944     ldap_modify_ext( ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls, &msgid );
00945 
00946   ldap_controls_free( serverctrls );
00947   ldap_controls_free( clientctrls );
00948   ldap_mods_free( lmod, 1 );
00949   if ( retval == 0 ) {
00950     retval = msgid;
00951   }
00952   return retval;
00953 }
00954 
00955 int LdapOperation::modify_s( const LdapDN &dn, const ModOps &ops )
00956 {
00957   Q_ASSERT( d->mConnection );
00958   LDAP *ld = (LDAP*) d->mConnection->handle();
00959 
00960   LDAPMod **lmod = 0;
00961 
00962   LDAPControl **serverctrls = 0, **clientctrls = 0;
00963   createControls( &serverctrls, d->mServerCtrls );
00964   createControls( &serverctrls, d->mClientCtrls );
00965 
00966   for ( int i = 0; i < ops.count(); ++i ) {
00967     int mtype = 0;
00968     switch ( ops[i].type ) {
00969     case Mod_None:
00970       mtype = 0;
00971       break;
00972     case Mod_Add:
00973       mtype = LDAP_MOD_ADD;
00974       break;
00975     case Mod_Replace:
00976       mtype = LDAP_MOD_REPLACE;
00977       break;
00978     case Mod_Del:
00979       mtype = LDAP_MOD_DELETE;
00980       break;
00981     }
00982     addModOp( &lmod, mtype, ops[i].attr, 0 );
00983     for ( int j = 0; j < ops[i].values.count(); ++j ) {
00984       addModOp( &lmod, mtype, ops[i].attr, &ops[i].values[j] );
00985     }
00986   }
00987 
00988   int retval =
00989     ldap_modify_ext_s( ld, dn.toString().toUtf8().data(), lmod, serverctrls, clientctrls );
00990 
00991   ldap_controls_free( serverctrls );
00992   ldap_controls_free( clientctrls );
00993   ldap_mods_free( lmod, 1 );
00994   return retval;
00995 }
00996 
00997 int LdapOperation::compare( const LdapDN &dn, const QString &attr, const QByteArray &value )
00998 {
00999   Q_ASSERT( d->mConnection );
01000   LDAP *ld = (LDAP*) d->mConnection->handle();
01001   int msgid;
01002 
01003   LDAPControl **serverctrls = 0, **clientctrls = 0;
01004   createControls( &serverctrls, d->mServerCtrls );
01005   createControls( &serverctrls, d->mClientCtrls );
01006 
01007   int vallen = value.size();
01008   BerValue *berval;
01009   berval = (BerValue *) malloc( sizeof( BerValue ) );
01010   berval -> bv_val = (char *) malloc( vallen );
01011   berval -> bv_len = vallen;
01012   memcpy( berval -> bv_val, value.data(), vallen );
01013 
01014   int retval = ldap_compare_ext( ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval,
01015                                  serverctrls, clientctrls, &msgid );
01016 
01017   ber_bvfree( berval );
01018   ldap_controls_free( serverctrls );
01019   ldap_controls_free( clientctrls );
01020 
01021   if ( retval == 0 ) {
01022     retval = msgid;
01023   }
01024   return retval;
01025 }
01026 
01027 int LdapOperation::compare_s( const LdapDN &dn, const QString &attr, const QByteArray &value )
01028 {
01029   Q_ASSERT( d->mConnection );
01030   LDAP *ld = (LDAP*) d->mConnection->handle();
01031 
01032   LDAPControl **serverctrls = 0, **clientctrls = 0;
01033   createControls( &serverctrls, d->mServerCtrls );
01034   createControls( &serverctrls, d->mClientCtrls );
01035 
01036   int vallen = value.size();
01037   BerValue *berval;
01038   berval = (BerValue *) malloc( sizeof( BerValue ) );
01039   berval -> bv_val = (char *) malloc( vallen );
01040   berval -> bv_len = vallen;
01041   memcpy( berval -> bv_val, value.data(), vallen );
01042 
01043   int retval = ldap_compare_ext_s( ld, dn.toString().toUtf8().data(), attr.toUtf8().data(), berval,
01044                                    serverctrls, clientctrls );
01045 
01046   ber_bvfree( berval );
01047   ldap_controls_free( serverctrls );
01048   ldap_controls_free( clientctrls );
01049 
01050   return retval;
01051 }
01052 
01053 int LdapOperation::exop( const QString &oid, const QByteArray &data )
01054 {
01055   Q_ASSERT( d->mConnection );
01056 #if defined(HAVE_LDAP_EXTENDED_OPERATION) && defined(HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE)
01057   LDAP *ld = (LDAP*) d->mConnection->handle();
01058   int msgid;
01059 
01060   LDAPControl **serverctrls = 0, **clientctrls = 0;
01061   createControls( &serverctrls, d->mServerCtrls );
01062   createControls( &serverctrls, d->mClientCtrls );
01063 
01064   int vallen = data.size();
01065   BerValue *berval;
01066   berval = (BerValue *) malloc( sizeof( BerValue ) );
01067   berval -> bv_val = (char *) malloc( vallen );
01068   berval -> bv_len = vallen;
01069   memcpy( berval -> bv_val, data.data(), vallen );
01070 
01071   int retval = ldap_extended_operation( ld, oid.toUtf8().data(), berval,
01072                                         serverctrls, clientctrls, &msgid );
01073 
01074   ber_bvfree( berval );
01075   ldap_controls_free( serverctrls );
01076   ldap_controls_free( clientctrls );
01077 
01078   if ( retval == 0 ) {
01079     retval = msgid;
01080   }
01081   return retval;
01082 #else
01083   kError() << "Your LDAP client libraries don't support extended operations.";
01084   return -1;
01085 #endif
01086 }
01087 
01088 int LdapOperation::exop_s( const QString &oid, const QByteArray &data )
01089 {
01090 #if defined(HAVE_LDAP_EXTENDED_OPERATION) && defined(HAVE_LDAP_EXTENDED_OPERATION_PROTOTYPE)
01091   Q_ASSERT( d->mConnection );
01092   LDAP *ld = (LDAP*) d->mConnection->handle();
01093   BerValue *retdata;
01094   char *retoid;
01095 
01096   LDAPControl **serverctrls = 0, **clientctrls = 0;
01097   createControls( &serverctrls, d->mServerCtrls );
01098   createControls( &serverctrls, d->mClientCtrls );
01099 
01100   int vallen = data.size();
01101   BerValue *berval;
01102   berval = (BerValue *) malloc( sizeof( BerValue ) );
01103   berval -> bv_val = (char *) malloc( vallen );
01104   berval -> bv_len = vallen;
01105   memcpy( berval -> bv_val, data.data(), vallen );
01106 
01107   int retval = ldap_extended_operation_s( ld, oid.toUtf8().data(), berval,
01108                                           serverctrls, clientctrls, &retoid, &retdata );
01109 
01110   ber_bvfree( berval );
01111   ber_bvfree( retdata );
01112   free( retoid );
01113   ldap_controls_free( serverctrls );
01114   ldap_controls_free( clientctrls );
01115 
01116   return retval;
01117 #else
01118   kError() << "Your LDAP client libraries don't support extended operations.";
01119   return -1;
01120 #endif
01121 }
01122 
01123 int LdapOperation::abandon( int id )
01124 {
01125   Q_ASSERT( d->mConnection );
01126   LDAP *ld = (LDAP*) d->mConnection->handle();
01127 
01128   LDAPControl **serverctrls = 0, **clientctrls = 0;
01129   createControls( &serverctrls, d->mServerCtrls );
01130   createControls( &serverctrls, d->mClientCtrls );
01131 
01132   int retval = ldap_abandon_ext( ld, id, serverctrls, clientctrls );
01133 
01134   ldap_controls_free( serverctrls );
01135   ldap_controls_free( clientctrls );
01136 
01137   return retval;
01138 }
01139 
01140 int LdapOperation::waitForResult( int id, int msecs )
01141 {
01142   Q_ASSERT( d->mConnection );
01143   LDAP *ld = (LDAP*) d->mConnection->handle();
01144 
01145   LDAPMessage *msg;
01146   int rescode;
01147 
01148   QTime stopWatch;
01149   stopWatch.start();
01150   int attempt( 1 );
01151   int timeout( 0 );
01152 
01153   do {
01154     // Calculate the timeout value to use and assign it to a timeval structure
01155     // see man select (2) for details
01156     timeout = kldap_timeout_value( msecs, stopWatch.elapsed() );
01157     kDebug() << "(" << id << "," << msecs
01158              << "): Waiting" << timeout
01159              << "msecs for result. Attempt #" << attempt++;
01160     struct timeval tv;
01161     tv.tv_sec = timeout / 1000;
01162     tv.tv_usec = ( timeout % 1000 ) * 1000;
01163 
01164     // Wait for a result
01165     rescode = ldap_result( ld, id, 0, timeout < 0 ? 0 : &tv, &msg );
01166     if ( rescode == -1 ) {
01167       return -1;
01168     }
01169     // Act on the return code
01170     if ( rescode != 0 ) {
01171       // Some kind of result is available for processing
01172       return d->processResult( rescode, msg );
01173     }
01174   } while ( msecs == -1 || stopWatch.elapsed() < msecs );
01175 
01176   return 0; //timeout
01177 }
01178 
01179 #else
01180 
01181 int LdapOperation::bind( const QByteArray &creds, SASL_Callback_Proc *saslproc, void *data )
01182 {
01183   kError() << "LDAP support not compiled";
01184   return -1;
01185 }
01186 
01187 int LdapOperation::bind_s( SASL_Callback_Proc *saslproc, void *data )
01188 {
01189   kError() << "LDAP support not compiled";
01190   return -1;
01191 }
01192 
01193 int LdapOperation::search( const LdapDN &base, LdapUrl::Scope scope,
01194                            const QString &filter, const QStringList &attributes )
01195 {
01196   kError() << "LDAP support not compiled";
01197   return -1;
01198 }
01199 
01200 int LdapOperation::add( const LdapObject &object )
01201 {
01202   kError() << "LDAP support not compiled";
01203   return -1;
01204 }
01205 
01206 int LdapOperation::add_s( const LdapObject &object )
01207 {
01208   kError() << "LDAP support not compiled";
01209   return -1;
01210 }
01211 
01212 int LdapOperation::add( const LdapDN &dn, const ModOps &ops )
01213 {
01214   kError() << "LDAP support not compiled";
01215   return -1;
01216 }
01217 
01218 int LdapOperation::add_s( const LdapDN &dn, const ModOps &ops )
01219 {
01220   kError() << "LDAP support not compiled";
01221   return -1;
01222 }
01223 
01224 int LdapOperation::rename( const LdapDN &dn, const QString &newRdn,
01225                            const QString &newSuperior, bool deleteold )
01226 {
01227   kError() << "LDAP support not compiled";
01228   return -1;
01229 }
01230 
01231 int LdapOperation::rename_s( const LdapDN &dn, const QString &newRdn,
01232                              const QString &newSuperior, bool deleteold )
01233 {
01234   kError() << "LDAP support not compiled";
01235   return -1;
01236 }
01237 
01238 int LdapOperation::del( const LdapDN &dn )
01239 {
01240   kError() << "LDAP support not compiled";
01241   return -1;
01242 }
01243 
01244 int LdapOperation::del_s( const LdapDN &dn )
01245 {
01246   kError() << "LDAP support not compiled";
01247   return -1;
01248 }
01249 
01250 int LdapOperation::modify( const LdapDN &dn, const ModOps &ops )
01251 {
01252   kError() << "LDAP support not compiled";
01253   return -1;
01254 }
01255 
01256 int LdapOperation::modify_s( const LdapDN &dn, const ModOps &ops )
01257 {
01258   kError() << "LDAP support not compiled";
01259   return -1;
01260 }
01261 
01262 int LdapOperation::compare( const LdapDN &dn, const QString &attr, const QByteArray &value )
01263 {
01264   kError() << "LDAP support not compiled";
01265   return -1;
01266 }
01267 
01268 int LdapOperation::exop( const QString &oid, const QByteArray &data )
01269 {
01270   kError() << "LDAP support not compiled";
01271   return -1;
01272 }
01273 
01274 int LdapOperation::compare_s( const LdapDN &dn, const QString &attr, const QByteArray &value )
01275 {
01276   kError() << "LDAP support not compiled";
01277   return -1;
01278 }
01279 
01280 int LdapOperation::exop_s( const QString &oid, const QByteArray &data )
01281 {
01282   kError() << "LDAP support not compiled";
01283   return -1;
01284 }
01285 
01286 int LdapOperation::waitForResult( int id, int msecs )
01287 {
01288   kError() << "LDAP support not compiled";
01289   return -1;
01290 }
01291 
01292 int LdapOperation::abandon( int id )
01293 {
01294   kError() << "LDAP support not compiled";
01295   return -1;
01296 }
01297 
01298 #endif

KLDAP Library

Skip menu "KLDAP Library"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.8
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal